Planet Python
EuroPython: EuroPython September 2023 Newsletter
Hello there and welcome to the post conference newsletter! We really hope you enjoyed EuroPython 2023 cause we sure did and are still recovering from all the fun and excitement. 😊
We have some updates to share with you and also wanted to use this newsletter to nostalgically look back at all the good times 🙌 we had in Prague just a month ago. Surrounded by old friends and new in the beautiful city of Prague, EuroPython 2023 was special for a lot of us 🤗 and the community, so we want to highlight some of those experiences!! So without further ado let’s get into the updates 🐍
EuroPython SocietyThe EPS board is working with our accountant and auditor to get our financial reports in order in the next couple of weeks. As soon as that is finalised, we will be excited to call for the next Annual General Assembly (GA); the actual GA will be held at least 14 days after our formal notice.
General Assembly is a great opportunity to hear about EuroPython Society&aposs developments and updates in the last year & a new board will also be elected at the end of the GA.
All EPS members are invited to attend the GA and have voting rights. Find out how to sign up to become an EPS member for free here: https://www.europython-society.org/application/
More about the EPS BoardThe EPS board is made up of up to 9 directors (including 1 chair and 1 vice chair); the board runs the day-to-day business of the EuroPython Society, including running the EuroPython conference series, and supports the community through various initiatives such as our grants programme. The board collectively takes up the fiscal and legal responsibility of the Society.
At the moment, running the annual EuroPython conference is a major task for the EPS. As such, the board members are expected to invest significant time and effort towards overseeing the smooth execution of the conference, ranging from venue selection, contract negotiations, and budgeting, to volunteer management. Every board member has the duty to support one or more EuroPython teams to facilitate decision-making and knowledge transfer.
In addition, the Society prioritises building a close relationship with local communities. Board members should not only be passionate about the Python community but have a high-level vision and plan for how the EPS could best serve the community.
How can you become an EPS 2024 board member?Any EPS member can nominate themselves for the EPS 2024 board. Nominations will be published prior to the GA.
Though the formal deadline for self-nomination is at the GA, it is recommended that you send in yours as early as possible (yes, now is a good time!) to board@europython.eu.We look forward to your email :)
& for more information check out our Call for Board Candidates!
EPS 2023 General Assembly - Call for Board CandidatesIt feels like yesterday that many of us were together in Prague or online for EuroPython 2023. Each year, the current board of the EuroPython Society (EPS) holds a General Assembly (GA). It is a precious opportunity for all our members to get together annually, and reflect on the learningsEuroPython SocietyRaquel DouConference NumbersWith 142 Talks, 22 Tutorials, 10 Special events, 5 Keynotes, 3 panel discussions happening throughout the week, our “learn more about this” bookmarks list/backlog reached new heights this year! If you know what I mean 😉
Let&aposs take a closer look at our stats, if you too are into that kinda thing.
Thank you Volunteers & Sponsors <3Year after year EuroPython shines because of the hard work of our amazing team of volunteers
But beyond the logistics and the schedules, it&aposs your smiles, your enthusiasm, and your genuine willingness to go the extra mile that truly made EuroPython 2023 truly special. Your efforts have not only fostered a sense of belonging among first time attendees but also exemplified the power of community and collaboration that lies at the heart of this conference.
Once again, thank you for being the backbone of EuroPython, for your dedication, and for showing the world yet again why people who come for the Python language end up staying for the amazing community :)
https://ep2023.europython.eu/thankyou
And a special thank you to all of the Sponsors for all of their support!
Thank you Sponsors 🥳Conference Photos & VideosThe official conference photos are up on Flickr! Do not forget to tag us when you share your favourite clicks on your socials 😉.
https://ep2023.europython.eu/photos
We know how much you would love to see and share videos of some amazing talks and keynotes we had during the conference. Rest assured we are working with our AV team to have videos edited and ready in a month or so. Stay tuned for that.
In the meantime if you want to revisit a talk you missed or just want to check out a talk again, all the live streams from across the conference days is still available on our page
https://ep2023.europython.eu/live/forum
We also have some really sweet highlight videos featuring the amazing humans of EuroPython! Check it out on Youtube.
Community write-upsIt warms our hearts to see posts from the community about their experience and stories this year! Here are some of them, please feel free to share yours by tagging us on socials @europython or mailing us at news@europython.eu
Aleksandra Golofaeva on LinkedIn: #prague #python #europython #europython2023About my experience with EuroPython 2023 in Prague! EuroPython conference is the oldest and most significant event of its kind across Europe. As a newcomer to…LinkedInAleksandra GolofaevaSena S. on LinkedIn: I do love pythons, how did you guess that 🤔🤭TL;DR pythonista shares her…I do love pythons, how did you guess that 🤔🤭TL;DR pythonista shares her own EuroPython 2023 experience from her perspective EuroPython 2023 happened at…LinkedInSena S.https://sof.dog/europython2023
Weekly Report, EuroPython 2023 - Łukasz LangaOur new Security Developer in Residence is out and about, and publishes weekly updates on what he’s up to. That inspires me to resume doing the equivalent of those updates. And what better opportunity to do that than on the heels of EuroPython 2023!lukasz.langa.pllukasz.langaMariia Lukash wrote to us saying
I wanted to express my sincere gratitude for providing me with the opportunity to attend EuroPython 2023 remotely and free of charge. The conference was truly exceptional! The speakers were incredible, and their presentations were both informative and inspiring. I learned so much from each session. This being my first-ever conference experience, I never imagined it would be so captivating and enlightening. Moreover, I was particularly impressed by the sense of community that was evident throughout the event. Once again, thank you for this incredible opportunity. I am truly grateful for the experience, and if the chance arises, I would be delighted to attend future events organized by EuroPython.Messages like these warm our hearts and pushes us to do better for the 🐍 community every single year ❤️
Code of ConductCode of Conduct Transparency Report is now published on our website
https://www.europython-society.org/europython-2023-transparency-report/
EuroPython might over but fret not there are a bunch of more amazing Python conferences happening!!!
- PyCon CZ https://cz.pycon.org/2023/ 🇨🇿
- PyCon EE https://pycon.ee/ 🇪🇪
- PyCon PT https://2023.pycon.pt/ 🇵🇹
- PyCon UK https://2023.pyconuk.org/ 🇬🇧
- PyCon ES https://2023.es.pycon.org/ 🇪🇪
- PyCon SE https://www.pycon.se/ 🇸🇪
- PyCon IE https://python.ie/pycon-2023 🇮🇪
- PyLadiesCon https://conference.pyladies.com/ 🌎
- Swiss Python Summit https://www.python-summit.ch/ 🇨🇭
Add your own jokes to PyJokes (a project invented at a EuroPython sprint) via this issue: https://github.com/pyjokes/pyjokes/issues/10
Stack Abuse: Check if an Object has an Attribute in Python
In Python, everything is an object, and each object has attributes. These attributes can be methods, variables, data types, etc. But how do we know what attribute an object has?
In this Byte, we'll discuss why it's important to check for attributes in Python objects, and how to do so. We'll also touch on the AttributeError and how to handle it.
Why Check for Attributes?Attributes are integral to Python objects as they define the characteristics and actions that an object can perform. However, not all objects have the same set of attributes. Attempting to access an attribute that an object does not have will raise an AttributeError. This is where checking for an attribute before accessing it becomes crucial. It helps to ensure that your code is robust and less prone to runtime errors.
The AttributeError in PythonAttributeError is a built-in exception in Python that is raised when you try to access or call an attribute that an object does not have. Here's a simple example:
class TestClass: def __init__(self): self.x = 10 test_obj = TestClass() print(test_obj.y)The above code will raise an AttributeError because the object test_obj does not have an attribute y. The output will be:
AttributeError: 'TestClass' object has no attribute 'y'This error can be avoided by checking if an object has a certain attribute before trying to access it.
How to Check if an Object has an AttributePython provides a couple of ways to check if an object has a specific attribute. One way is to use the built-in hasattr() function, and the other is to use a try/except block.
Using hasattr() FunctionThe simplest way to check if an object has a specific attribute in Python is by using the built-in hasattr() function. This function takes two parameters: the object and the name of the attribute you want to check (in string format), and returns True if the attribute exists, False otherwise.
Here's how you can use hasattr():
class MyClass: def __init__(self): self.my_attribute = 42 my_instance = MyClass() print(hasattr(my_instance, 'my_attribute')) # Output: True print(hasattr(my_instance, 'non_existent_attribute')) # Output: FalseIn the above example, hasattr(my_instance, 'my_attribute') returns True because my_attribute is indeed an attribute of my_instance. On the other hand, hasattr(my_instance, 'non_existent_attribute') returns False because non_existent_attribute is not an attribute of my_instance.
Using try/except BlockAnother way to check for an attribute is by using a try/except block. You can attempt to access the attribute within the try block. If the attribute does not exist, Python will raise an AttributeError which you can catch in the except block.
Here's an example:
class MyClass: def __init__(self): self.my_attribute = 42 my_instance = MyClass() try: my_instance.my_attribute print("Attribute exists!") except AttributeError: print("Attribute does not exist!")In this example, if my_attribute exists, the code within the try block will execute without any issues and "Attribute exists!" will be printed. If my_attribute does not exist, an AttributeError will be raised and "Attribute does not exist!" will be printed.
Note: While this method works, it is generally not recommended to use exceptions for flow control in Python. Exceptions should be used for exceptional cases, not for regular conditional checks.
Checking for Multiple AttributesIf you need to check for multiple attributes, you can simply use hasattr() multiple times. However, if you want to check if an object has all or any of a list of attributes, you can use the built-in all() or any() function in combination with hasattr().
Here's an example:
class MyClass: def __init__(self): self.attr1 = 42 self.attr2 = 'Hello' self.attr3 = None my_instance = MyClass() attributes = ['attr1', 'attr2', 'attr3', 'non_existent_attribute'] print(all(hasattr(my_instance, attr) for attr in attributes)) # Output: False print(any(hasattr(my_instance, attr) for attr in attributes)) # Output: TrueIn this code, all(hasattr(my_instance, attr) for attr in attributes) returns False because not all attributes in the list exist in my_instance. However, any(hasattr(my_instance, attr) for attr in attributes) returns True because at least one attribute in the list exists in my_instance.
ConclusionIn this Byte, we've explored different ways to check if an object has a specific attribute in Python. We've learned how to use the hasattr() function, how to use a try/except block to catch AttributeError, and how to check for multiple attributes using all() or any().
Python People: Naomi Ceder
Naomi is an elected fellow of the PSF, and has served as chair of its board of directors.
Topics:
- Building replacement leadership for every endeavor you start
- What the PSF board does
- Keeping Python's growth in increasing diversity
- Learning foreign languages
- PyCon Charlas
- London
- Guitar and music
- The Quick Python Book
- Community building
- Retiring
Glyph Lefkowitz: Get Your Mac Python From Python.org
One of the most unfortunate things about learning Python is that there are so many different ways to get it installed, and you need to choose one before you even begin. The differences can also be subtle and require technical depth to truly understand, which you don’t have yet.1 Even experts can be missing information about which one to use and why.
There are perhaps more of these on macOS than on any other platform, and that’s the platform I primarily use these days. If you’re using macOS, I’d like to make it simple for you.
The One You Probably Want: Python.orgMy recommendation is to use an official build from python.org.
I recommed the official installer for most uses, and if you were just looking for a choice about which one to use, you can stop reading now. Thanks for your time, and have fun with Python.
If you want to get into the nerdy nuances, read on.
For starters, the official builds are compiled in such a way that they will run on a wide range of macs, both new and old. They are universal2 binaries, unlike some other builds, which means you can distribute them as part of a mac application.
The main advantage that the Python.org build has, though, is very subtle, and not any concrete technical detail. It’s a social, structural issue: the Python.org builds are produced by the people who make CPython, who are more likely to know about the nuances of what options it can be built with, and who are more likely to adopt their own improvements as they are released. Third party builders who are focused on a more niche use-case may not realize that there are build options or environment requirements that could make their Pythons better.
I’m being a bit vague deliberately here, because at any particular moment in time, this may not be an advantage at all. Third party integrators generally catch up to changes, and eventually achieve parity. But for a specific upcoming example, PEP 703 will have extensive build-time implications, and I would trust the python.org team to be keeping pace with all those subtle details immediately as releases happen.
(And Auto-Update It)The one downside of the official build is that you have to return to the website to check for security updates. Unlike other options described below, there’s no built-in auto-updater for security patches. If you follow the normal process, you still have to click around in a GUI installer to update it once you’ve clicked around on the website to get the file.
I have written a micro-tool to address this and you can pip install mopup and then periodically run mopup and it will install any security updates for your current version of Python, with no interaction besides entering your admin password.
(And Always Use Virtual Environments)Once you have installed Python from python.org, never pip install anything globally into that Python, even using the --user flag. Always, always use a virtual environment of some kind. In fact, I recommend configuring it so that it is not even possible to do so, by putting this in your ~/.pip/pip.conf:
1 2[global] require-virtualenv = trueThis will avoid damaging your Python installation by polluting it with libraries that you install and then forget about. Any time you need to do something new, you should make a fresh virtual environment, and then you don’t have to worry about library conflicts between different projects that you may work on.
If you need to install tools written in Python, don’t manage those environments directly, install the tools with pipx. By using pipx, you allow each tool to maintain its own set dependencies, which means you don’t need to worry about whether two tools you use have conflicting version requirements, or whether the tools conflict with your own code.2
The OthersThere are, of course, several other ways to install Python, which you probably don’t want to use.
The One For Running Other People’s Code, Not Yours: HomebrewIn general, Homebrew Python is not for you.
The purpose of Homebrew’s python is to support applications packaged within Homebrew, which have all been tested against the versions of python libraries also packaged within Homebrew. It may upgrade without warning on just about any brew operation, and you can’t downgrade it without breaking other parts of your install.
Specifically for creating redistributable binaries, Homebrew python is typically compiled only for your specific architecture, and thus will not create binaries that can be used on Intel macs if you have an Apple Silicon machine, or will run slower on Apple Silicon machines if you have an Intel mac. Also, if there are prebuilt wheels which don’t yet exist for Apple Silicon, you cannot easily arch -x86_64 python ... and just install them; you have to install a whole second copy of Homebrew in a different location, which is a headache.
In other words, homebrew is an alternative to pipx, not to Python. For that purpose, it’s fine.
The One For When You Need 20 Different Pythons For Debugging: pyenvLike Homebrew, pyenv will default to building a single-architecture binary. Even worse, it will not build a Framework build of Python, which means several things related to being a mac app just won’t work properly. Remember those build-time esoterica that the core team is on top of but third parties may not be? “Should I use a Framework build” is an enduring piece of said esoterica.
The purpose of pyenv is to provide a large matrix of different, precise legacy versions of python for library authors to test compatibility against those older Pythons. If you need to do that, particularly if you work on different projects where you may need to install some random super-old version of Python that you would not normally use to test something on, then pyenv is great. But if you only need one version of Python, it’s not a great way to get it.
The Other One That’s Exactly Like pyenv: asdf-pythonThe issues are exactly the same as with pyenv, as the tool is a straightforward alternative for the exact same purpose. It’s a bit less focused on Python than pyenv, which has pros and cons; it has broader community support, but it’s less specifically tuned for Python. But a comparative exploration of their differences is beyond the scope of this post.
The Built-In One That Isn’t Really Built-In: /usr/bin/python3There is a binary in /usr/bin/python3 which might seem like an appealing option — it comes from Apple, after all! — but it is provided as a developer tool, for running things like build scripts. It isn’t for building applications with.
That binary is not a “system python”; the thing in the operating system itself is only a shim, which will determine if you have development tools, and shell out to a tool that will download the development tools for you if you don’t. There is unfortunately a lot of folk wisdom among older Python programmers who remember a time when apple did actually package an antedeluvian version of the interpreter that seemed to be supported forever, and might suggest it for things intended to be self-contained or have minimal bundled dependencies, but this is exactly the reason that Apple stopped shipping that.
If you use this option, it means that your Python might come from the Xcode Command Line Tools, or the Xcode application, depending on the state of xcode-select in your current environment and the order in which you installed them.
Upgrading Xcode via the app store or a developer.apple.com manual download — or its command-line tools, which are installed separately, and updated via the “settings” application in a completely different workflow — therefore also upgrades your version of Python without an easy way to downgrade, unless you manage multiple Xcode installs. Which, at 12G per install, is probably not an appealing option.3
The One With The Data And The Science: CondaAs someone with a limited understanding of data science and scientific computing, I’m not really qualified to go into the detailed pros and cons here, but luckily, Itamar Turner-Trauring is, and he did.
My one coda to his detailed exploration here is that while there are good reasons to want to use Anaconda — particularly if you are managing a data-science workload across multiple platforms and you want a consistent, holistic development experience across a large team supporting heterogenous platforms — some people will tell you that you need Conda to get you your libraries if you want to do data science or numerical work with Python at all, because Conda is how you install those libraries, and otherwise things just won’t work.
This is a historical artifact that is no longer true. Over the last decade, Python Wheels have been comprehensively adopted across the Python community, and almost every popular library with an extension module ships pre-built binaries to multiple platforms. There may be some libraries that only have prebuilt binaries for conda, but they are sufficiently specialized that I don’t know what they are.
The One for Being Consistent With Your Cloud HostingAnother way to run Python on macOS is to not run it on macOS, but to get another computer inside your computer that isn’t running macOS, and instead run Python inside that, usually using Docker.4
There are good reasons to want to use a containerized configuration for development, but they start to drift away from the point of this post and into more complicated stuff about how to get your Python into the cloud.
So rather than saying “use Python.org native Python instead of Docker”, I am specifically not covering Docker as a replacement for a native mac Python here because in a lot of cases, it can’t be one. Many tools require native mac facilities like displaying GUIs or scripting applications, or want to be able to take a path name to a file without elaborate pre-work to allow the program to access it.
SummaryIf you didn’t want to read all of that, here’s the summary.
If you use a mac:
- Get your Python interpreter from python.org.
- Update it with mopup so you don’t fall behind on security updates.
- Always use venvs for specific projects, never pip install anything directly.
- Use pipx to manage your Python applications so you don’t have to worry about dependency conflicts.
- Don’t worry if Homebrew also installs a python executable, but don’t use it for your own stuff.
- You might need a different Python interpreter if you have any specialized requirements, but you’ll probably know if you do.
Thank you to my patrons who are supporting my writing on this blog. If you like what you’ve read here and you’d like to read more of it, or you’d like to support my various open-source endeavors, you can support me on Patreon as well! I am also available for consulting work if you think your organization could benefit from expertise on topics like “which Python is the really good one”.
-
If somebody sent you this article because you’re trying to get into Python and you got stuck on this point, let me first reassure you that all the information about this really is highly complex and confusing; if you’re feeling overwhelmed, that’s normal. But the good news is that you can really ignore most of it. Just read the next little bit. ↩
-
Some tools need to be installed in the same environment as the code they’re operating on, so you may want to have multiple installs of, for example, Mypy, PuDB, or sphinx. But for things that just do something useful but don’t need to load your code — such as this small selection of examples from my own collection: certbot, pgcli, asciinema, gister, speedtest-cli) — pipx means you won’t have to debug wonky dependency interactions. ↩
-
The command-line tools are a lot smaller, but cannot have multiple versions installed at once, and are updated through a different mechanism. There are odd little details like the fact that the default bundle identifier for the framework differs, being either org.python.python or com.apple.python3. They’re generally different in a bunch of small subtle ways that don’t really matter in 95% of cases until they suddenly matter a lot in that last 5%. ↩
-
Or minikube, or podman, or colima or whatever I guess, there’s way too many of these containerization Pokémon running around for me to keep track of them all these days. ↩
PyCoder’s Weekly: Issue #592 (Aug. 29, 2023)
#592 – AUGUST 29, 2023
View in Browser »
A good introduction to I/O bound concurrency in Python and the libraries used to achieve it. Has a nice compare and contrast between the approaches and finishes with some good advice: you probably don’t need any of them.
BITE CODE
In this tutorial, you’ll learn how to use the Python asterisk and slash special parameters in function definitions. With these symbols, you can define whether your functions will accept positional or keyword arguments.
REAL PYTHON
Scan your code and dependencies for free with Semgrep - the trusted OSS tool used by top companies like Gitlab, Snowflake, and Slack. No security expertise needed, simply add your project and let Semgrep do the work in just minutes →
SEMGREP sponsor
Become a better web developer by taking a deep dive into Flask’s internals to learn about its core features and functionality.
TESTDRIVEN.IO • Shared by Patrick Kennedy
How can you improve a classification model while avoiding overfitting? Once you have a model, what tools can you use to explain it to others? This week on the show, we talk with author and Python trainer Matt Harrison about his new book Effective XGBoost: Tuning, Understanding, and Deploying Classification Models.
REAL PYTHON podcast
In this step-by-step tutorial, you’ll build a code image generator that creates nice-looking images of your code snippets to share on social media. Your code image generator will be powered by the Flask web framework and include exciting packages like Pygments and Playwright.
REAL PYTHON
Get an introduction to Temporal’s Python SDK by walking through our easy, free tutorials. Learn how to build Temporal applications using Python, including building a data pipeline Workflow and a subscription Workflow. Get started her →
TEMPORAL sponsor
This article teaches you about all of the use cases that the underscore (_) has in Python, from the use cases that have syntactic impact, to well-accepted conventions that make your code semantically clearer, to usages that improve the readability of your code.
RODRIGO GIRÃO SERRÃO • Shared by Rodrigo Girão Serrão @ mathspp
“Optimizing Django query performance is critical for building performant web applications.” This blog post explores a collection of additional and essential tips that help pinpoint and resolve your inefficient Django queries.
JOHNNY METZ
This blog post walks you through programmatically identifying tables on PDF pages and extracting their content using PyMuPDF. Table identification and extraction was recently added in PyMuPDF version 1.23.0.
HARALD LIEDER • Shared by Harald Lieder
Python is an object-oriented programming language and one of its features is that it supports operator overloading, learn how to overload common operators such as addition, subtraction, comparison, and more.
ALEJANDRO SÁNCHEZ YALÍ • Shared by Alejandro
In this tutorial, you’ll learn how to use the Click library to build robust, extensible, and user-friendly command-line interfaces (CLI) for your Python automation and tooling scripts.
REAL PYTHON
🤔 Can cell therapy and AI be used together? Learn how to efficiently build and deploy scientific AI models using open-source technologies with Beckman Coulter Life Sciences at our upcoming DevCon OpenVINO webinar. #DevCon2023
INTEL CORPORATION sponsor
Automatic differentiation is at the heart of neural network training. This article introduces you to the concept by showing you some Python that implements the algorithm.
VICTOR MARTIN
Simplicity is hard. This article talks briefly about how you approach coding while keeping things simple.
BOB BELDERBOS
August 30, 2023
REALPYTHON.COM
August 31, 2023
MEETUP.COM
September 2 to September 4, 2023
PYCON.ORG
September 4, 2023
J.MP
September 6 to September 7, 2023
CLOUD-BUILDERS.TECH
September 7 to September 9, 2023
PYCON.EE
September 7 to September 10, 2023
PYCON.PT
Happy Pythoning!
This was PyCoder’s Weekly Issue #592.
View in Browser »
[ Subscribe to 🐍 PyCoder’s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]
Stack Abuse: Hidden Features of Python
Python is a powerful programming language that's easy to learn and fun to play with. But beyond the basics, there are plenty of hidden features and tricks that can help you write more efficient and more effective Python code.
In this article, we'll uncover some of these hidden features and show you how to use them to improve your Python programming skills.
Exploring Python's Hidden FeaturesPython is full of hidden features, some of which are more hidden than others. These features can be incredibly useful and can help you write more efficient and readable code. However, they can also be a bit tricky to discover if you don't know where to look.
In the next few sections we'll take a look at a couple features that are both helpful to know and not known as well throughout Python programmers.
The _ (Underscore) in PythonOne of Python's hidden gems is the underscore (_). It's a versatile character that can be used in various ways in Python code.
First, it can be used as a variable in Python. It's often used as a throwaway variable, i.e., a variable that is being declared but the value is not actually used.
for _ in range(5): print("Hello, World!")In the above code, we're not using the variable _ anywhere, it's just there because a variable is required by the syntax of the for loop.
Second, _ is used for ignoring the specific values. If you don’t need the specific values or the values are not used, just assign the values to underscore.
# Ignore a value when unpacking x, _, y = (1, 2, 3) # x = 1, y = 3Here we need both the x and y variables, but Python's syntax won't let us declare them without something in betwee, so we use the underscore.
Last, in the Python console, _ represents the last executed expression value.
>>> 10 + 20 30 >>> _ 30Note: The use of _ for storing the last value is specific to Python’s interactive interpreter and won’t work in scripts!
Regex Debugging via Parse TreeRegular expressions can be complex and hard to understand. Thankfully, Python provides a hidden feature to debug them via a parse tree. The re module in Python provides the re.DEBUG flag which can be used to debug regular expressions.
Consider the following code:
import re re.compile("(\d+)\.(\d+)", re.DEBUG)This will output:
SUBPATTERN 1 0 0 MAX_REPEAT 1 MAXREPEAT IN CATEGORY CATEGORY_DIGIT LITERAL 46 SUBPATTERN 2 0 0 MAX_REPEAT 1 MAXREPEAT IN CATEGORY CATEGORY_DIGIT 0. INFO 4 0b0 3 MAXREPEAT (to 5) 5: MARK 0 7. REPEAT_ONE 9 1 MAXREPEAT (to 17) 11. IN 4 (to 16) 13. CATEGORY UNI_DIGIT 15. FAILURE 16: SUCCESS 17: MARK 1 19. LITERAL 0x2e ('.') 21. MARK 2 23. REPEAT_ONE 9 1 MAXREPEAT (to 33) 27. IN 4 (to 32) 29. CATEGORY UNI_DIGIT 31. FAILURE 32: SUCCESS 33: MARK 3 35. SUCCESS re.compile('(\\d+)\\.(\\d+)', re.DEBUG)This is the parse tree of the regular expression. It shows that the regular expression has two subpatterns ((\d+) and (\d+)), separated by a literal dot (.).
This can be incredibly useful when debugging complex regular expressions. It gives you a clear, visual representation of your regular expression, showing exactly what each part of the expression does.
Note: The re.DEBUG flag does not work with the re.match() or re.search() functions. It only works with re.compile().
EllipsisPython's ellipsis is a unique feature that's not commonly seen in other programming languages. It's represented by three consecutive dots (...) and it's actually a built-in constant in Python. You might be wondering, what could this possibly be used for? Let's explore some of its applications.
Python's ellipsis can be used as a placeholder for code. This can be very useful when you're sketching out a program structure but haven't implemented all parts yet. For instance:
def my_func(): ... # TODO: implement this functionHere, the ellipsis indicates that my_func is incomplete and needs to be implemented.
Python's ellipsis also plays a role in slicing multi-dimensional arrays, especially in data science libraries like NumPy. Here's how you can use it:
import numpy as np # Create a 3D array arr = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) # Use ellipsis to access elements print(arr[..., 2])This will output:
[[ 3 6] [ 9 12]]In this case, the ellipsis is used to access the third element of each sub-array in our 3D array.
The dir() FunctionThe dir() function is another hidden gem in Python. It's a powerful built-in function that returns a list of names in the current local scope or a list of attributes of an object.
When used without an argument, dir() returns a list of names in the current local scope. Here's an example:
x = 1 y = 2 print(dir())This will print something like:
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'x', 'y']Here, x and y are the variables we defined, and the rest are built-in names in Python.
When used with an object as an argument, dir() returns a list of the object's attributes. For instance, if we use it with a string object:
print(dir('Hello, StackAbuse!'))This will output:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']These are all the methods that you can use with a string object. dir() is a handy function when you want to explore Python objects and understand their capabilities.
Lambda FunctionsLambda functions, also known as anonymous functions, are a feature of Python that allow you to create small, one-time, unnamed functions that you can use quickly and then discard. They're perfect for when you need a function for a short period of time and don't want to bother with the full function definition syntax.
Here's how you would create a lambda function:
multiply = lambda x, y: x * y print(multiply(5, 4))Output:
20In the above example, we've created a lambda function that multiplies two numbers together. We then call the function with the numbers 5 and 4, and it returns 20.
Note: While lambda functions are powerful and convenient, they should be used sparingly. Overuse of lambda functions can lead to code that is difficult to read and debug.
Chaining Comparison OperatorsPython allows you to chain comparison operators in a way that's intuitive and easy to read. This can be a real time-saver when you're writing complex comparisons.
For example, let's say you want to check if a number is between 1 and 10. Instead of writing two separate comparisons and combining them with an and operator, you can do this:
x = 5 print(1 < x < 10)Output:
TrueIn this example, 1 < x < 10 is equivalent to 1 < x and x < 10. Python checks both comparisons and returns True if both are true, just as if you'd used the and operator.
Note: You can chain as many comparisons as you want in this way. For example, 1 < x < 10 < x * 10 < 100 is perfectly valid Python code.
The zip() FunctionPython's zip() function is a hidden gem that doesn't get the attention it deserves. This function, which has been part of Python since version 1.5, can make your code cleaner and more efficient by allowing you to iterate over multiple sequences in parallel.
Here's a simple example of how it works:
names = ["Alice", "Bob", "Charlie"] ages = [25, 30, 35] for name, age in zip(names, ages): print(f"{name} is {age} years old.")And the output:
Alice is 25 years old. Bob is 30 years old. Charlie is 35 years old.Note: The zip() function stops at the end of the shortest input sequence. So if your sequences aren't the same length, no exception is raised - but you may lose some data from the longer sequences.
DecoratorsDecorators are another powerful feature of Python that can greatly simplify your code. Essentially, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
Let's look at an example. Suppose you have a function that performs an operation, but you want to log when the operation starts and ends. You could add logging statements directly to the function, but that would clutter your code. Instead, you can create a decorator to handle the logging:
def log_decorator(func): def wrapper(): print("Starting operation...") func() print("Operation finished.") return wrapper @log_decorator def perform_operation(): print("Performing operation...") perform_operation()When you run this code, you'll see the following output:
Starting operation... Performing operation... Operation finished.The @log_decorator line is a decorator. It tells Python to pass the function perform_operation() to the log_decorator() function. The log_decorator() function then wraps the perform_operation() function with additional code to log the start and end of the operation.
Note: Decorators can also take arguments, which allows them to be even more flexible. Just remember that if your decorator takes arguments, you need to write it as a function that returns a decorator, rather than a simple decorator function.
Context Managers and the "with" StatementIn Python, context managers are a hidden gem that can be super useful in managing resources. They allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement.
Let's take a look at an example:
with open('hello.txt', 'w') as f: f.write('Hello, World!')In this example, the with statement is used to open a file and assign it to the variable f. The file is kept open for the duration of the with block, and automatically closed at the end, even if exceptions occur within the block. This ensures that the clean-up is done for us.
Note: Using the with statement is like saying, "with this thing, do this stuff, and no matter how it ends, close it out properly."
Generators and the Yield StatementGenerators are a type of iterable, like lists or tuples. They do not allow indexing but they can still be iterated through with for loops. They are created using functions and the yield statement.
The yield statement is used to define a generator, replacing the return of a function to provide a result to its caller without destroying local variables.
Here's a simple generator that generates even numbers:
def even_numbers(n): for i in range(n): if i % 2 == 0: yield i for number in even_numbers(10): print(number)Output:
0 2 4 6 8Unlike normal functions, the local variables are not destroyed when the function yields. Furthermore, the generator object can be iterated only once.
Note: Generators are a great way to produce data which is huge or infinite. It represents a stream of data; this feature is used in Python 3's range() function.
These hidden features of Python, context managers and generators, can make your code more efficient and readable. They are worth understanding and using in your day-to-day Python coding.
MetaclassesIn Python, everything is an object - including classes themselves. This fact leads us to the concept of metaclasses. A metaclass is the class of a class; a class is an instance of a metaclass. It's a higher-level abstraction that controls the creation and management of classes in Python.
To define a metaclass, we typically inherit from the built-in type class. Let's take a look at a simple example:
class Meta(type): def __new__(cls, name, bases, attrs): print(f"Creating a new class named: {name}") return super().__new__(cls, name, bases, attrs) class MyClass(metaclass=Meta): passRunning this code, you'll see the following output:
$ python3 metaclass_example.py Creating a new class named: MyClassIn the above example, Meta is our metaclass that inherits from type. It overrides the __new__ method, which is responsible for creating and returning a new object. When we define MyClass with Meta as its metaclass, the __new__ method of Meta is called, and we see our custom message printed.
Note: Metaclasses can be powerful, but they can also make code more complex and harder to understand. Use them sparingly and only when necessary.
ConclusionPython is a versatile language with a plethora of hidden features that can make your coding experience more efficient and enjoyable. From the often overlooked underscore, to the powerful and complex concept of metaclasses, there's always something new to discover in Python. The key to mastering these features is understanding when and how to use them appropriately in your code.
Python Software Foundation: The Python Software Foundation has been authorized by the CVE Program as a CVE Numbering Authority (CNA)
When a vulnerability is disclosed in software you're depending on, the last thing you want is for the remediation process to be confusing or ad-hoc. Towards the goal of a more secure and safe Python ecosystem, the Python Software Foundation has been authorized by the CVE Program as a CVE Numbering Authority (CNA).
Being authorized as a CNA is one milestone in the Python Software Foundation's strategy to improve the vulnerability response processes of critical projects in the Python ecosystem. The Python Software Foundation CNA scope covers Python and pip, two projects which are fundamental to the rest of Python ecosystem.
By becoming a CNA, the PSF will be providing the following benefits to in-scope projects:
- Paid staffing for CNA operations rather than requiring volunteer time.
- Quicker allocations of CVE IDs after a vulnerability is reported.
- Involvement of each projects' security response teams during the reporting of vulnerabilities.
- Richer published advisories and CVE Records including descriptions, metadata, and remediation information.
- Consistent disclosures and publishing locations.
CNA operations will be staffed primarily by the recently hired Security Developer-in-Residence Seth Michael Larson, Ee Durbin, and Chloe Gerhardson.
The PSF wants to help other Open Source organizations and will be sharing lessons learned and developing guidance on becoming a CNA and day-to-day operations.
To be alerted of newly published vulnerabilities in Python or pip, subscribe to the security-announce@python.org mailing list for security advisories. There is also a new advisory database published to GitHub using the machine-readable Open Source Vulnerability (OSV) format.
If you'd like to report a security vulnerability to Python or pip, the vulnerability disclosure policy is available on python.org.
The mission of the Common Vulnerabilities and Exposures (CVE®) Program is to
identify, define, and catalog publicly disclosed cybersecurity vulnerabilities. There
is one CVE Record for each vulnerability in the catalog. The vulnerabilities are
discovered then assigned and published by organizations from around the world
that have partnered with the CVE Program. Partners publish CVE Records to
communicate consistent descriptions of vulnerabilities. Information technology
and cybersecurity professionals use CVE Records to ensure they are discussing
the same issue, and to coordinate their efforts to prioritize and address the
vulnerabilities.
The Python Software Foundation (PSF) is the non-profit organization behind Python and PyPI. Our mission is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers. The PSF supports the Python community using corporate sponsorships, grants, and donations. Are you interested in sponsoring or donating to the PSF so it can continue supporting Python and its community? Check out our sponsorship program, donate directly here, or contact our team!
Real Python: Create a Python Wordle Clone With Rich
In this video course, you’ll build your own Wordle clone for the terminal. Since Josh Wardle launched Wordle in October 2021, millions of people have played it. While you can play the original game on the Web, you’ll create your version as a command-line application and then use the Rich library to make it look good.
As you follow along in this step-by-step project, you’ll practice how to set up a simple prototype game before iteratively developing it into a solid application.
In this video course, you’ll learn how to:
- Build out a command-line application from a prototype to a polished game
- Read and validate user input
- Use Rich’s console to create an attractive user interface in the terminal
- Organize your code into functions
- Provide your users with actionable feedback
You’ll create Wyrdl, your own Wordle clone in Python. This project is for anyone getting comfortable with Python who wants to build a terminal application from the ground up. Throughout the course, you’ll build your code step-by-step while focusing on having a game that you can play from the start.
[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
Stack Abuse: Appending Strings in Python
In Python, strings are a sequence of characters and are one of the most used data types. There are lots of scenarios (taking user input, reading data from files, etc) where you'll need to append one string to another.
This Byte will guide you through the basic ways of appending strings in Python.
Basic String Appending in PythonAs we all know, Python is a very flexible language that makes it easy to do many common programming tasks. This is done by providing operators, methods, or other means to perform a task. As you'll see in the next few sections, there are quite a few ways to append strings.
Using the '+' OperatorThe + operator is the most straightforward way to append strings in Python. However, it's worth noting that the + operator can only concatenate strings. If you try to use it with a non-string type, Python will raise a TypeError.
Here's an example:
str1 = "The answer is " num = 42 str2 = str1 + num print(str2)This will raise the following error:
TypeError: can only concatenate str (not "int") to strTo fix this, you need to convert the non-string type to a string using the str() function:
str1 = "The answer is " num = 42 str2 = str1 + str(num) print(str2)And the output will be:
The answer is 42 Using the '%' OperatorAnother way to append strings in Python is by using the % operator. This operator is used for string formatting and can be a powerful tool for appending strings and non-string types.
Let's see an example:
num = 42 str1 = "The answer is %s" % num print(str1)The output will be:
The answer is 42In this example, %s is a placeholder for a string. When the % operator is used, it replaces the placeholder with the value of num. Note that the % operator automatically converts non-string types to strings, so you don't need to use the str() function.
Note: The % operator also supports other types of placeholders, such as %d for integers and %f for floating-point numbers.
Using the 'format()' FunctionPython's built-in format() function is another versatile tool for string manipulation. It allows us to insert and format data in a string in a variety of ways. To append one string to another using the format() function, we can use placeholders, represented by curly braces {}.
Here's a basic example:
str1 = "Hello" str2 = "World" result = "{} {}".format(str1, str2) print(result)Output:
Hello WorldIn this example, the format() function replaces the {} placeholders with the arguments provided, in the order they are given.
Using the 'join()' FunctionThe join() function is a string method that concatenates a sequence of strings with a specified delimiter. If we want to append strings without any delimiter, we can use an empty string '' as the delimiter.
Here's how you can do it:
str1 = "Hello" str2 = "World" result = ''.join([str1, str2]) print(result)Output:
HelloWorldNote: The join() function expects an iterable (like a list or a tuple) as the argument, so we need to put our strings into a list or tuple.
Using 'f-string' FormattingIntroduced in Python 3.6, f-string formatting (also known as f-strings) is a new way to format strings in Python. It's faster, more readable, and less error-prone than other string formatting methods.
To append one string to another using f-strings, we can simply include the variables we want to append inside {} in the string. Here's an example:
str1 = "Hello" str2 = "World" result = f"{str1}{str2}" print(result)Output:
HelloWorld ConclusionIn this Byte, we've covered different ways to append strings in Python using the format(), join(), and f-string methods. Each method has its own use-cases and advantages. The format() function is versatile and allows for complex string manipulations, the join() function is useful when dealing with lists of strings, and f-strings offer a more readable and efficient way to format strings.
Stack Abuse: Reading from stdin in Python
In most programming languages, data can be read from various sources, like files, databases, or user input. However, one of the most common forms of input is the standard input, or stdin.
This article will provide a few different ways for reading from stdin in Python, which is an important skill for any developer working on command-line interfaces.
What is stdin?Standard input, commonly referred to as stdin, is a stream from which a program reads its input data. It's one of the three standard streams in computer systems, alongside stdout (standard output) and stderr (standard error). By default, stdin refers to the data that a user inputs from the keyboard.
Note: The concept of stdin, stdout, and stderr comes from Unix-like operating systems, but it's also available in other systems like Windows. They are used for inter-process communication and can be redirected, which is a significant feature in shell scripting and system programming.
In Python, you can access stdin through the sys.stdin object, which behaves like a file object. This means you can use methods like read(), readline(), and readlines() to read from stdin, just as you would read from a file.
import sys for line in sys.stdin: print(line)In this simple example, the program will read from stdin line by line and print each line until it encounters an "EOF" (End of File) signal. You can send an EOF signal in the terminal by pressing Ctrl+D in Unix-like systems or Ctrl+Z in Windows.
Why read from stdin?Standard input, or stdin, is a fundamental concept in computer programming that refers to the default data stream from which a program reads its input data. But why would you want to read from stdin instead of another source?
Well, reading from stdin is a common way to allow for dynamic user input during the program execution. It's also a way to receive data piped from another program. For example, let's say you have a program that processes text in some way, and you want to be able to use it in a Unix pipeline to process the output of another program. Reading from stdin is the way to go.
How to Read from stdin in PythonPython, like most programming languages, provides a few different ways to read from stdin. We'll explore a couple of the most common methods in the following sections.
Using sys.stdinThe sys.stdin object in Python is a file-like object that acts as the stdin stream for the Python program. You can read from this stream just like you would read from a file.
Here's an example:
import sys line = sys.stdin.readline() # Removing the trailing newline character line = line.strip() print(f"Received: {line}")In this code, we simply use stdin's readline() method to get input from the user. The line is then printed out with the prefix "Received: ".
If you run this code and type in some input, you'll see that it echoes back each line you enter:
$ python read_stdin.py Hello, world! Received: Hello, world! Using the input() FunctionThe input() function is a simpler way to read a line of input from stdin. This function reads a line of input, converts it to a string (stripping the trailing newline), and returns that string.
Here's an example:
line = input() print("Received: " + line)Again, if you run this code and type in some input, you'll see that it echoes back the line you enter:
$ python read_input.py Hello, world! Received: Hello, world!The input() function always returns a string. If you want to read a number or some other type of data, you'll need to convert the string to that type using a function like int() or float().
Link: For more information on how to read and convert data to the correct formats, see the following articles:
- Python: Check if Variable is a Number
- Check if String Contains a Number in Python
- How to Check if a String is Empty or None in Python
In Python, reading multiple lines from stdin can be accomplished in a couple of ways. One common approach is to use a for loop with sys.stdin. This allows you to iterate over each line that is entered until an EOF (End of File) character is received.
Here's an example:
import sys for line in sys.stdin: print(line)In this code, each line that is entered into stdin will be printed out. The program will continue to read lines until an EOF character is received. You can simulate this by pressing Ctrl+D in the terminal.
Another way to read multiple lines of input from stdin is by using the input() function inside a loop. This is a more user-friendly approach since it waits for the user to press Enter before reading the next line.
Here's how you can do it:
while True: try: line = input() print(line) except EOFError: breakIn this case, the program will also continue to read lines until an EOF character is received.
Note: The input() function automatically strips the newline character at the end of the line, while sys.stdin does not.
Reading Specific Number of Characters from stdinSometimes, you might want to read a specific number of characters from stdin. This can be done using the read() method provided by sys.stdin. The read() method takes an integer as an argument, which specifies the number of characters to read.
Here's an example where we read 10 characters from stdin:
import sys chars = sys.stdin.read(10) print(chars)In this code, the program will read the first 10 characters of input and print them out. If less than 10 characters are entered, the program will wait for more input until it has read 10 characters. stdin has a read() function since it's a file-like object, thus, you can read only a given number of characters, just like you can with files.
Note: The read() method does not automatically strip newline characters, so if the input includes newline characters within the first 10 characters, they will be included in the output.
Remember, the input() function can also be used to read a specific number of characters, but it works slightly differently. The input() function reads a line of input (up to the newline character), and then you can use slicing to get the desired number of characters.
Here's an example:
line = input() chars = line[:10] print(chars)In this case, the program will read a line of input, and then print the first 10 characters of that line. If the line is less than 10 characters long (not including the newline), the entire line will be printed.
Handling EOFError when Reading from stdinWhile reading from stdin in Python, you might encounter an EOFError. This error is raised when one of the built-in functions like input() hits an end-of-file condition (EOF) without reading any data. This usually happens when you run a program that's expecting input but doesn't receive any. It's important to handle this error in your code to prevent your program from crashing unexpectedly.
Here's how you can gracefully handle an EOFError:
try: data = input() except EOFError: print("EOFError encountered. No input received.")In this code, we use a try-except block to catch and handle the EOFError. If input() hits an EOF condition, the program prints a message instead of crashing.
ConclusionReading from stdin is a fundamental task in Python programming, especially when dealing with command-line applications or scripts. In this article, we've explored what stdin is, why you might want to read from it, and how to do so using various methods. We've also covered how to handle an EOFError, which can occur when reading from stdin. Understanding these concepts will help you create more robust, reliable Python applications.
Stack Abuse: Flush the Output of the print() Function in Python
In Python, the print function is a fundamental tool for outputting data to the console (and for many of us, our primary debugging tool). But, as you may have run into, sometimes the output of this function doesn't appear immediately. This is because of a feature called the "output buffer".
In this Byte, we'll explore the output buffer, why it's necessary to flush it, and how the print function's flush parameter can help us control this behavior.
The Output BufferThe output buffer is a temporary storage area in your computer's memory where data waiting to be outputted to the console is held. The buffer improves performance by reducing the number of I/O operations. Instead of writing each piece of data to the console separately, Python collects several chunks of data and writes them all at once.
Here's a simple analogy: Imagine you're mailing letters. Instead of going to the post office each time you finish a letter, you'd save a lot of time by writing several letters, then taking them all to the post office at once. That's essentially what the output buffer does.
Why We Need to Flush the Output BufferWhile buffering is great for performance, it can cause confusion when you're debugging your code. If your program crashes, you might not see all the output that was generated before the crash because some of it may still be in the buffer.
That's where flushing comes in. When you flush the output buffer, you're telling Python to immediately write out any data that's currently stored in the buffer, even if the buffer isn't full.
This can be especially useful when you're print-debugging, because it ensures that you see all output up to the point of the crash.
The Print Function and the Flush ParameterPython's print function has a flush parameter that you can use to control whether the output buffer should be flushed. By default, flush is set to False, which means the buffer is not flushed after each call to print.
If you set flush to True, Python will flush the output buffer after each print call. Here's an example:
print("Hello, World!", flush=True)In this code, the string "Hello, World!" is printed to the console, and then the output buffer is immediately flushed. This ensures that "Hello, World!" appears on the console right away, even if there's more data waiting to be printed.
Note: Flushing the output buffer can slow down your program, especially if you're printing a lot of data. Therefore, it's generally a good idea to only use flush=True when you're debugging and need to see your output immediately.
In the next sections of this Byte, we'll go over how to use the flush parameter and provide some examples.
How to Use the Flush ParameterIn Python's print function, there's a parameter called flush. This parameter is set to False by default, which means that the output buffer isn't immediately cleared after the print function is executed. But if you set flush=True, Python will instantly flush the output buffer.
Here's the syntax for using the flush parameter:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)In this syntax, *objects are the values you want to print, sep is the separator which is a space by default, end is the character that is printed at the end which is a newline by default, file is the object where the values are printed, and flush is the parameter we're interested in.
To use the flush parameter, simply include flush=True in your print function:
print("Hello, StackAbuse!", flush=True)This will print the string "Hello, StackAbuse!" and immediately clear the output buffer.
Examples of Using the Flush ParameterLet's look at a few examples of how the flush parameter can be used in real-world scenarios.
Printing in a LoopWhen you're printing inside a loop, especially a long-running one, you might want to see the output immediately. Here's how you can do it:
import time for i in range(10): print(i, flush=True) time.sleep(1)In this code, the numbers from 0 to 9 will be printed one at a time, with a delay of one second between each print. Because we've set flush=True, each number is printed immediately to the console.
Printing in a FileIf you're writing to a file using the print function, you can use flush=True to ensure that the output is written to the file immediately. Here's how this works:
with open('output.txt', 'w') as f: print("Hello, StackAbuse!", file=f, flush=True)Now, the string "Hello, StackAbuse!" is written to the file output.txt immediately.
ConclusionIn this Byte, we've explored how to use the flush parameter in Python's print function to immediately clear the output buffer. This can be particularly useful when you're printing inside a loop or writing to a file and you want to see the output right away.
Talk Python to Me: #428: Django Trends in 2023
Python Bytes: #350 You've Got The Stamina For This Episode
Brian Okken: pytest Course - Ch3 pytest Fixtures is up
death and gravity: reader 3.9 released
Hi there!
I'm happy to announce version 3.9 of reader, a Python feed reader library.
What's new? #Here are the highlights since reader 3.7.
Better handling of unexpected update errors #Unexpected exceptions raised by update hooks, retrievers, and parsers are now wrapped in UpdateError, so errors for one feed don't prevent others from being updated. Also, hooks that run after a feed is updated are all run, regardless of individual failures. Plugins should benefit most from the improved fault isolation.
Exception hierarchy diagram #The API docs got a cool new exception hierarchy diagram (yes, it's autogenerated):
ReaderError ├── ReaderWarning [UserWarning] ├── ResourceNotFoundError ├── FeedError │ ├── FeedExistsError │ ├── FeedNotFoundError [ResourceNotFoundError] │ └── InvalidFeedURLError [ValueError] ├── EntryError │ ├── EntryExistsError │ └── EntryNotFoundError [ResourceNotFoundError] ├── UpdateError │ ├── ParseError [FeedError, ReaderWarning] │ └── UpdateHookError │ ├── SingleUpdateHookError │ └── UpdateHookErrorGroup [ExceptionGroup] ├── StorageError ├── SearchError │ ├── SearchNotEnabledError │ └── InvalidSearchQueryError [ValueError] ├── PluginError │ ├── InvalidPluginError [ValueError] │ └── PluginInitError └── TagError └── TagNotFoundError Parser cleanup #I moved all modules related to feed retrieval and parsing to reader._parser, another step towards internal API stabilization. This has also given me an opportunity to make lazy imports a bit less intrusive.
Timer experimental plugin #There's a new timer experimental plugin to collect per-call method timings.
The web app shows them in the footer like so:
Python versions #Python 3.9 support is no more, as foretold in the ancient murals.
For more details, see the full changelog.
That's it for now.
Want to contribute? Check out the docs and the roadmap.
Learned something new today? Share this with others, it really helps! Twitter HN Reddit
What is reader? #reader takes care of the core functionality required by a feed reader, so you can focus on what makes yours different.
reader allows you to:
- retrieve, store, and manage Atom, RSS, and JSON feeds
- mark articles as read or important
- add arbitrary tags/metadata to feeds and articles
- filter feeds and articles
- full-text search articles
- get statistics on feed and user activity
- write plugins to extend its functionality
...all these with:
- a stable, clearly documented API
- excellent test coverage
- fully typed Python
To find out more, check out the GitHub repo and the docs, or give the tutorial a try.
Why use a feed reader library? #Have you been unhappy with existing feed readers and wanted to make your own, but:
- never knew where to start?
- it seemed like too much work?
- you don't like writing backend code?
Are you already working with feedparser, but:
- want an easier way to store, filter, sort and search feeds and entries?
- want to get back type-annotated objects instead of dicts?
- want to restrict or deny file-system access?
- want to change the way feeds are retrieved by using Requests?
- want to also support JSON Feed?
- want to support custom information sources?
... while still supporting all the feed types feedparser does?
If you answered yes to any of the above, reader can help.
The reader philosophy #- reader is a library
- reader is for the long term
- reader is extensible
- reader is stable (within reason)
- reader is simple to use; API matters
- reader features work well together
- reader is tested
- reader is documented
- reader has minimal dependencies
So you can:
- have full control over your data
- control what features it has or doesn't have
- decide how much you pay for it
- make sure it doesn't get closed while you're still using it
- really, it's easier than you think
Obviously, this may not be your cup of tea, but if it is, reader can help.
Real Python: How to Iterate Through a Dictionary in Python
Dictionaries are one of the most important and useful built-in data structures in Python. They’re everywhere and are a fundamental part of the language itself. In your code, you’ll use dictionaries to solve many programming problems that may require iterating through the dictionary at hand. In this tutorial, you’ll dive deep into how to iterate through a dictionary in Python.
Solid knowledge of dictionary iteration will help you write better, more robust code. In your journey through dictionary iteration, you’ll write several examples that will help you grasp the different ways to traverse a dictionary by iterating over its keys, values, and items.
In this tutorial, you’ll:
- Get to know some of the main features of dictionaries
- Iterate through a dictionary in Python by using different techniques and tools
- Transform your dictionaries while iterating through them in Python
- Explore other tools and techniques that facilitate dictionary iteration
To get the most out of this tutorial, you should have a basic understanding of Python dictionaries, know how to use Python for loops, and be familiar with comprehensions. Knowing other tools like the built-in map() and filter() functions and the itertools and collections modules is also a plus.
Get Your Code: Click here to download the sample code that shows you how to iterate through a dictionary with Python.
Take the Quiz: Test your knowledge with our interactive “Python Dictionary Iteration” quiz. Upon completion you will receive a score so you can track your learning progress over time:
Getting Started With Python DictionariesDictionaries are a cornerstone of Python. Many aspects of the language are built around dictionaries. Modules, classes, objects, globals(), and locals() are all examples of how dictionaries are deeply wired into Python’s implementation.
Here’s how the Python official documentation defines a dictionary:
An associative array, where arbitrary keys are mapped to values. The keys can be any object with __hash__() and __eq__() methods. (Source)
There are a couple of points to notice in this definition:
- Dictionaries map keys to values and store them in an array or collection. The key-value pairs are commonly known as items.
- Dictionary keys must be of a hashable type, which means that they must have a hash value that never changes during the key’s lifetime.
Unlike sequences, which are iterables that support element access using integer indices, dictionaries are indexed by keys. This means that you can access the values stored in a dictionary using the associated key rather than an integer index.
The keys in a dictionary are much like a set, which is a collection of hashable and unique objects. Because the keys need to be hashable, you can’t use mutable objects as dictionary keys.
On the other hand, dictionary values can be of any Python type, whether they’re hashable or not. There are literally no restrictions for values. You can use anything as a value in a Python dictionary.
Note: The concepts and topics that you’ll learn about in this section and throughout this tutorial refer to the CPython implementation of Python. Other implementations, such as PyPy, IronPython, and Jython, could exhibit different dictionary behaviors and features that are beyond the scope of this tutorial.
Before Python 3.6, dictionaries were unordered data structures. This means that the order of items typically wouldn’t match the insertion order:
>>>>>> # Python 3.5 >>> likes = {"color": "blue", "fruit": "apple", "pet": "dog"} >>> likes {'color': 'blue', 'pet': 'dog', 'fruit': 'apple'}Note how the order of items in the resulting dictionary doesn’t match the order in which you originally inserted the items.
In Python 3.6 and greater, the keys and values of a dictionary retain the same order in which you insert them into the underlying dictionary. From 3.6 onward, dictionaries are compact ordered data structures:
>>>>>> # Python 3.6 >>> likes = {"color": "blue", "fruit": "apple", "pet": "dog"} >>> likes {'color': 'blue', 'fruit': 'apple', 'pet': 'dog'}Keeping the items in order is a pretty useful feature. However, if you work with code that supports older Python versions, then you must not rely on this feature, because it can generate buggy behaviors. With newer versions, it’s completely safe to rely on the feature.
Another important feature of dictionaries is that they’re mutable data types. This means that you can add, delete, and update their items in place as needed. It’s worth noting that this mutability also means that you can’t use a dictionary as a key in another dictionary.
Understanding How to Iterate Through a Dictionary in Python Read the full article at https://realpython.com/iterate-through-dictionary-python/ »[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]
Brett Cannon: Why I put in some effort to lower my carbon footprint
I was talking with someone about how Andrea and I have been consciously taking less flights since the pandemic started in order to lower our carbon footprint (Take the Jump suggests a flight under 1500km every 3 years, longer than that every 8 years; heard about this from David Suzuki), and how that probably means always driving to PyCascades (thanks to our EV), flying to PyCon US (or EuroPython depending on things) and the core dev sprints, and that potentially being it for conference travel unless I combine it with a holiday. The person I was chatting with then asked me why I seemed to be willing to sacrifice some happiness from conferences for the planet when my individual carbon footprint is miniscule compared to entire countries who are not seemingly putting in as much effort as I am? I honestly wasn&apost prepared for that question, so I didn&apost have a good way to articulate why. But now that I have reflected on it, this blog post records my reasons for putting in at least some effort to lower my carbon footprint at the cost of some happiness for myself.
First, I think every little bit helps. I think of it in terms of a fighting game like Street Fighter 2 or Mortal Kombat: you might survive by a sliver of life, but a win is a win. Since I don&apost know what the magic tipping point is for the climate crisis to spiral out of control and destroy this planet for human beings, I would rather help keep even a sliver of health on that life bar for the planet instead of looking back on my life on my deathbed and wondering if I should have done more (at my age, I very much expect to make it to 2050 and see how good/bad things look for the rest of the century)?
Second, I want to influence however I can everyone around me who votes to help push politicians to do their work to fight the climate crisis as that&aposs where real gains can be made. This is essentially trickle-up ethics where I am trying to influence those around me, to then influence those around them, and so on and so forth, until politicians realize people care about the environment and they need to make changes to keep their jobs (or lives depending on the political system). This is a bit of a slog as you end up needing to have conversations over years on the climate with the same people, but I have seen changes in folks like my in-laws who are (unfortunately) the primary generation of folks who bother voting, so getting them to change their minds is important.
Anyway, so that&aposs why I bother doing what I consider my part in lowering my carbon footprint. As I said, I fully realize I could do more, but I am still willing to make some sacrifices to help out as I don&apost know if my small effort won&apost have some trickle-on effect that leads to marked improvements. And if we all did a small bit of sacrificing, it can add up in various ways whether its directly in the atmosphere or via ethical views of society.
Stack Abuse: Writing a Pandas DataFrame to a CSV File in Python
Working with data is a big part of any data analysis project. In Python, the Pandas library is a powerful tool that provides flexible and efficient data structures to make the process of data manipulation and analysis easier. One of the most common data structures provided by Pandas is the DataFrame, which can be thought of as a table of data with rows and columns. However, often you'll want to save your DataFrame to a file for later use, or to share with others. One of the most common file formats for data storage is CSV.
In this article, we'll explore how to write a pandas DataFrame to a CSV file.
Why Write a DataFrame to a CSV File?CSV files are a popular choice for data storage for a number of reasons. First and foremost, they are text-based and therefore human-readable. This means you can open a CSV file in a plain text editor to quickly view and understand the data it contains.
CSV files are also widely used and understood by many different software applications. This makes it easy to share data between different systems and programming languages. If you're working with a team that uses a variety of tools, saving your DataFrame to a CSV file ensures that everyone can work with the data.
Finally, writing a DataFrame to a CSV file is a way to persist your data. When you're working in a Python session, your DataFrame exists only in memory. If you close your Python session, your DataFrame is lost. By writing it to a CSV file, you can save your data to disk, allowing you to access it again later, even after you've closed and reopened your Python session.
import pandas as pd # Create a simple DataFrame df = pd.DataFrame({ 'A': [1, 2, 3], 'B': ['a', 'b', 'c'] }) # Write DataFrame to CSV file df.to_csv('my_data.csv')In this code, a DataFrame is created and then written to a CSV file named my_data.csv. After running this code, you'll find a new file in your current directory with this name, containing the data from your DataFrame.
How to Write a DataFrame to a CSV FilePandas, a popular Python data manipulation library, provides a simple yet powerful method to write a DataFrame to a CSV file. The function to_csv() is what we need.
Let's start with a basic DataFrame:
import pandas as pd data = {'Name': ['John', 'Anna', 'Peter'], 'Age': [28, 24, 33], 'Country': ['USA', 'Sweden', 'Germany']} df = pd.DataFrame(data)Our DataFrame looks like this:
Name Age Country 0 John 28 USA 1 Anna 24 Sweden 2 Peter 33 GermanyTo write this DataFrame to a CSV file, we use the to_csv() function like so:
df.to_csv('data.csv')This will create a CSV file named data.csv in your current directory.
If you want to specify a different location, provide the full path. For example, df.to_csv('/path/to/your/directory/data.csv').
Writing DataFrame to CSV with Specific DelimiterBy default, the to_csv() function uses a comma as the field delimiter. However, you can specify a different delimiter using the sep parameter.
For example, let's write our DataFrame to a CSV file using a semicolon as the delimiter:
df.to_csv('data_semicolon.csv', sep=';')This will create a CSV file named data_semicolon.csv with the data separated by semicolons.
Name;Age;Country John;28;USA Anna;24;Sweden Peter;33;GermanyNote: The sep parameter accepts any character as a delimiter. However, common delimiters are comma, semicolon, tab (\t), and space (' ').
This flexibility of pandas allows you to easily write your DataFrame to a CSV file that suits your needs, whether it's a standard CSV or a CSV with a specific delimiter.
Writing DataFrame to CSV Without IndexBy default, when you write a DataFrame to a CSV file using the to_csv() function, pandas includes the DataFrame's index. However, there may be scenarios where you don't want this. In such cases, you can set the index parameter to False to exclude the index from the CSV file.
Here's an example:
import pandas as pd # Create a simple dataframe df = pd.DataFrame({ 'A': ['foo', 'bar', 'baz'], 'B': ['alpha', 'beta', 'gamma'] }) print(df) df.to_csv('no_index.csv', index=False)The print(df) command will output:
A B 0 foo alpha 1 bar beta 2 baz gammaBut the no_index.csv file will look like this:
A,B foo,alpha bar,beta baz,gammaAs you can see, the CSV file does not include the DataFrame's index.
If you open the CSV file in a text editor, you may not see the DataFrame's index. However, if you open the CSV file in a spreadsheet program like Excel, you will see the index as the first column.
Handling Special CasesThere are a few special cases you may come across when writing a DataFrame to a CSV file.
Handling NaN ValuesBy default, pandas will write NaN values to the CSV file. However, you can change this behavior using the na_rep parameter. This parameter allows you to specify a string that will replace NaN values.
Here's an example:
import pandas as pd import numpy as np # Create a simple dataframe with NaN values df = pd.DataFrame({ 'A': ['foo', np.nan, 'baz'], 'B': ['alpha', 'beta', np.nan] }) df.to_csv('nan_values.csv', na_rep='NULL')In the nan_values.csv file, NaN values are replaced with NULL:
,A,B 0,foo,alpha 1,NULL,beta 2,baz,NULL Writing a Subset of the DataFrame to CSVSometimes, you may want to write only a subset of the DataFrame to the CSV file. You can do this using the columns parameter. This parameter allows you to specify a list of column names that you want to include in the CSV file.
Here's an example:
import pandas as pd # Create a simple dataframe df = pd.DataFrame({ 'A': ['foo', 'bar', 'baz'], 'B': ['alpha', 'beta', 'gamma'], 'C': [1, 2, 3] }) df.to_csv('subset.csv', columns=['A', 'B'])The subset.csv file will include only the 'A' and 'B' columns:
,A,B 0,foo,alpha 1,bar,beta 2,baz,gammaRemember, pandas is a powerful library and provides many options for writing DataFrames to CSV files. Be sure to check out the official documentation to learn more.
ConclusionIn this tutorial, we have explored the power of pandas and its ability to write DataFrame to a CSV file. We've learned the basic method of writing a DataFrame to a CSV file, how to specify a delimiter, and how to write a DataFrame to a CSV file without the index. We've also looked at handling special cases in writing a DataFrame to a CSV file.
Ned Batchelder: Hammerspoon
For a long time now I’ve displayed a small textual info box in the otherwise unused upper-left corner of my mac screen. It was originally based on GeekTool, but a new laptop got me to re-think it, and now it’s implemented with Hammerspoon.
Hammerspoon is a Mac automation tool driven by Lua programs. It has an extensive API for integration with Mac facilities, so it can automate many aspects of the OS, potentially replacing a number of Mac utilities.
GeekTool was always a little odd and configured much of its behavior in fiddly property panels. Hammerspoon is fully driven by .lua files, so it fits my programmers’ world view better. GeekTool also seems unmaintained these days.
On my Mac, I hide the menu bar and move the dock to the left side. This maximizes the vertical space available to my own windows. But it leaves a small corner unused in the upper left. I have a small Python program that collects information often displayed in the menu bar (date, time, battery level, sound volume, etc). With Hammerspoon I can create a small canvas and display the program’s output text.
It looks like this:
Here’s the Lua code that runs the Python from Hammerspoon and draws the canvas:
-- ~/.hammerspoon/init.lua-- Text-mode "menu bar indicator" replacement
canvas = nil
function createCanvas()
if canvas then
canvas:hide()
end
local screen = hs.screen.primaryScreen()
local frame = screen:frame()
local fullFrame = screen:fullFrame()
canvas = hs.canvas.new({
x = fullFrame.x,
y = frame.y,
w = frame.x - fullFrame.x,
h = 175,
})
canvas[1] = {
type = "rectangle",
action = "fill",
fillColor = {hex="#D0D0D0"},
}
canvas[2] = {
type = "text",
frame = {x=2, y=0, h="100%", w="100%"},
textFont = "SF Pro Text",
textSize = 14,
textColor = {hex="#000000"},
}
canvas:show()
canvas:sendToBack()
drawInfo()
end
function drawInfo()
local openPop = io.popen("/usr/local/bin/python3.10 ~/bin/textstatuses.py")
canvas[2].text = openPop:read("*a")
openPop:close()
end
-- Start over when any screen geometry changes.
watcher = hs.screen.watcher.newWithActiveScreen(createCanvas):start()
-- Redraw every 10 seconds.
timer = hs.timer.doEvery(10, drawInfo)
-- Redraw when any audio setting changes.
for i, dev in ipairs(hs.audiodevice.allOutputDevices()) do
dev:watcherCallback(drawInfo):watcherStart()
end
This has a few advantages over GeekTool: it’s entirely self-contained in a text file I can commit to git, it can listen for events to be more reactive, it can compute its location to take the menubar into account, and so on.
Theoretically, Hammerspoon can also replace other Mac widgets like Caffeine, Rectangle Pro, and so on. I haven’t tried replacing them all, but it’s probably in my future.
One interesting side-effect: learning Lua!
Stack Abuse: The Python Magic Methods: __str__ vs __repr__
Python, being a high-level, interpreted language, is known for its easy readability with great design principles. However, when you delve deeper into Python, some things may seem complex if you're coming from a language that doesn't have a similar feature. One such feature is the concept of magic methods, and in this Byte, we're going to demystify what __str__ and __repr__ magic methods are, their differences, and why and when to use each.
What are Magic Methods?Magic methods in Python are special methods that add "magic" to your classes. They're always surrounded by double underscores (e.g. __init__ or __lt__). These methods are also known as dunder methods, short for "double under." Magic methods are not meant to be invoked directly by you, but the invocation happens internally from the class on a certain action. For instance, when you add two numbers using the + operator, internally, the __add__ method will be called.
class Number: def __init__(self, num): self.num = num def __add__(self, other): return self.num + other.num num1 = Number(2) num2 = Number(3) print(num1 + num2) # Output: 5In the example above, you can see that the __add__ method is being used to enable the use of the + operator. This is the magic of magic methods!
Magic methods can make your classes act more like Python built-in types, and make your code more intuitive and cleaner.
Understanding __str__ MethodThe __str__ method in Python represents the "informal" or nicely printable string representation of an object. This method is called by the str() built-in function and by the print function to convert the object into a string.
Let's take a look at an example where we define a Person class with a __str__ method:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'Person(name={self.name}, age={self.age})' p = Person('John Doe', 30) print(p)The output of this code would be:
Person(name=John Doe, age=30)In this example, the __str__ method returns a string that represents the Person object in a human-readable form.
Understanding __repr__ MethodOn the other hand, the __repr__ method returns a string that describes a precise, unambiguous representation of an object. The main goal of this method is to be explicit about the object's information. It's meant to be used in debugging and development. The __repr__ method is called by the repr() built-in function.
Here's an example where we define a Person class with a __repr__ method:
class Person: def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return f'Person(name={self.name!r}, age={self.age!r})' p = Person('John Doe', 30) print(repr(p))The output of this code would be:
Person(name='John Doe', age=30)In this example, the __repr__ method returns a string that if evaluated, would produce an object equivalent to p. Notice the use of !r in the format string to ensure the output string uses repr() instead of str(). This is part of the attempt to make the output unambiguous.
Note: If you don't define a __str__ method in your class, Python will call the __repr__ method when attempting to print an object.
Differences Between __str__ and __repr__In Python, __str__ and __repr__ are two magic methods that serve different purposes. At first glance, they might seem similar as they both return a string representation of the object. However, the key difference between them lies in their intended audience and the level of detail they provide.
The __str__ method is meant to provide a concise, human-readable description of an object. It's what you’ll see when you print an object. On the other hand, __repr__ is intended to provide a complete and unambiguous representation of the object, which is more useful for developers. It's what you’ll see when you display the object in the console.
Here's an example to illustrate this difference:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'{self.name} is {self.age} years old.' def __repr__(self): return f'Person({self.name}, {self.age})' p = Person('John', 30) print(p) # John is 30 years old. p # Person(John, 30)Here, print(p) invokes the __str__ method and returns a human-readable string. However, when you type p in the console, Python calls the __repr__ method and returns a more detailed string that could be used to recreate the object.
Why and When to Use __str__The __str__ method is primarily used for creating a human-readable representation of the object. It's a great way to provide a simple summary or description of the object that can easily be understood by end users.
You might want to use __str__ when you're printing objects for logging or debugging purposes, or when you want to display a friendly message to the user. For example, if you're developing a game, you might use __str__ to print a player's stats in a readable format.
Here's how you might use __str__ in a game:
class Player: def __init__(self, name, level, health): self.name = name self.level = level self.health = health def __str__(self): return f'Player {self.name} is at level {self.level} with {self.health} health points.' player = Player('Hero', 10, 100) print(player) # Player Hero is at level 10 with 100 health points.In this example, the __str__ method returns a string that provides a concise summary of the player's current status. This makes it easy for users to understand the player's status at a glance.
Why and When to Use __repr__The __repr__ method in Python is a special method that returns a string representing a printable version of an object. But when would you use it? Well, __repr__ is intended to be unambiguous and complete. This means that if you have an object, the __repr__ of that object should contain all the information necessary to recreate the object if fed back into the interpreter.
This makes __repr__ incredibly useful for debugging and logging, as it can provide a more detailed overview of an object compared to __str__. If you're working with complex data structures, or need a complete representation of your object for troubleshooting, __repr__ is the way to go.
Note: In the absence of a defined __str__ method, Python will default to using __repr__ when the print() function is called on an object.
Examples: Using the __str__ MethodNow that we've discussed the __repr__ method, let's switch gears and look at some examples of how you might use the __str__ method in Python. Remember, __str__ is meant to return a nicely printable string representation of an object, making it great for end-user output.
Let's define a simple Person class with __str__ method:
class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'Person(name={self.name}, age={self.age})'Now, let's create an instance of Person and print it:
p = Person('John', 28) print(p)Output:
Person(name=John, age=28)As you can see, the print() function calls the __str__ method and prints a user-friendly string representation of our Person object. This can be very useful when you want to present object information in a readable and clean format.
Examples: Using the __repr__ MethodLet's dive into some examples to understand the usage of repr in Python. Consider a class called "Person" with a few attributes.
class Person: def __init__(self, name, age): self.name = name self.age = ageIf we create an instance of this class and try to print it, we'll get an unhelpful message.
p = Person('John Doe', 30) print(p)Output:
<__main__.Person object at 0x7f3f8e7e3d30>This is where repr comes in handy. Let's override the repr method in our class.
class Person: def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return f'Person(name={self.name}, age={self.age})'Now, when we create an instance of the class and print it, we get a much more meaningful output.
p = Person('John Doe', 30) print(p)Output:
Person(name=John Doe, age=30)The __repr__ method should return a string that is a valid Python expression. It's meant to be unambiguous and complete. This means that if you were to copy its output and run it, you should get the same object that was printed.
Conclusion__str__ and __repr__ are special methods in Python that allow us to control how objects are converted to strings. While __str__ is meant for creating a readable string representation for end users, __repr__ is designed to generate an unambiguous string representation for developers. Understanding the difference between these two methods and when to use each one is crucial for writing clean, effective Python code.
Whether you're debugging or displaying data, these magic methods can make your life a lot easier. Remember, it's always a good practice to implement __repr__ for your classes, and implement __str__ if you think it would be useful to have a string version of the object that's user-friendly.