Planet Python
Last update: December 02, 2025 07:44 AM UTC
December 01, 2025
Mike Driscoll
ANN: Vibe Coding Video Games with Python
I am happy to announce my latest book, Vibe Coding Video Games with Python. Here’s the announcement from my Kickstarter:
Welcome to Vibe Coding Video Games with Python. In this book, you will learn how to use artificial intelligence to create mini-games. You will attempt to recreate the look and feel of various classic video games. The intention is not to violate copyright or anything of the sort, but instead to learn the limitations and the power of AI.
Instead, you will simply be learning about whether or not you can use AI to help you know how to create video games. Can you do it with no previous knowledge, as the AI proponents say? Is it really possible to create something just by writing out questions to the ether?
Release date: January 2026 (eBook) / March 2026 (paperback)
You will be using various large language models (LLMs) such as Google Gemini, Grok, Mistral, CoPilot, and others to create these games. You will discover the differences and similarities between these tools. You may be surprised to find that some tools give much more context than others.
AI is certainly not a cure-all and is far from perfect. You will quickly discover AI’s limitations and learn some strategies for solving those kinds of issues.

Here are some examples of what you will be creating in this book:
Lune Lander Clone

Asteroids Clone

Pong Clone

What You’ll Learn
You’ll be creating “clones” of some popular games. However, these games will only be the first level and may or may not be fully functional.
- Chapter 1 – The Snake Game
- Chapter 2 – Pong Clone
- Chapter 3 – Frogger Clone
- Chapter 4 – Space Invaders Clone
- Chapter 5 – Minesweeper Clone
- Chapter 6 – Luna Lander Clone
- Chapter 7 – Asteroids Clone
- Chapter 8 – Tic-Tac-Toe
- Chapter 9 – Pole Position Clone
- Chapter 10 – Connect Four
- Chapter 11 – Intro to Sprites
Rewards to Choose From
There are several different rewards you can get in this Kickstarter:
- A signed paperback copy of the book
- An eBook copy of the book in PDF and ePub
- A t-shirt with the cover art from the book
- Other Python eBooks
Support the Kickstarter Today
The post ANN: Vibe Coding Video Games with Python appeared first on Mouse Vs Python.
December 01, 2025 04:11 PM UTC
Caktus Consulting Group
LLM Basics: OpenAI Function Calling
In our previous post, we explored how to send text to an LLM and receive a text response in return. That is useful for chatbots, but often we need to integrate LLMs with other systems. We may want the model to query a database, call an external API, or perform calculations.
December 01, 2025 03:00 PM UTC
Real Python
Quantum Computing Basics With Qiskit
Every classical computer reduces the world to 0s and 1s. That binary framework has carried us from calculators to supercomputers, but some problems demand checking through 2n possibilities, a task that outpaces even the best machines. Now, what if information could exist in many states at once? That what if turned into a new model called quantum computation. Keep reading to break with binaries and get an overview of quantum computing basics.
By the end of this tutorial, you’ll understand that:
- Qubits, or quantum bits, are the basic units of quantum computing.
- A qubit can be in a superposition of 0 and 1. This means its state is a precise combination of both until measurement forces it to become either 0 or 1.
- The Bloch sphere is a standard way to visualize what a single qubit is doing.
- Entanglement creates strong correlations between two or more qubits, so measuring one tells you something about the others that you can’t explain with classical reasoning alone.
- Interference adjusts probability amplitudes so that wrong outcomes cancel and useful outcomes reinforce.
- Quantum hardware comes in different architectures, each with its own strengths and limitations.
- With Python libraries like Qiskit, you can build and run quantum circuits directly from Python.
Before you jump in, it’d be a good idea to go through the basics of classical computing. For example, knowing how bits work, what logic gates like AND and NOT do, and how you can determine the runtime complexity of an algorithm using Big O notation will help you understand the differences and advantages of quantum computing.
Get Your Code: Click here to download the free sample code to build a quantum circuit with Qiskit.
Take the Quiz: Test your knowledge with our interactive “Quantum Computing Basics With Qiskit” quiz. You’ll receive a score upon completion to help you track your learning progress:
Interactive Quiz
Quantum Computing Basics With QiskitTest your understanding of quantum computing basics, including superposition, qubits, entanglement, and key programming concepts.
Introduction to Quantum Computing
Quantum computing is the use of fundamental ideas from quantum mechanics to perform computations on special devices called quantum computers. Quantum systems can process information in ways that classical computers can’t. For example, they can explore multiple possibilities at once. This capability is thanks to concepts like superposition, entanglement, and interference, which you’ll explore soon.
To see how quantum computing works, it helps to look at the main quantum concepts in the context of computing and consider how they differ from classical approaches. You’ll cover these essential concepts in this tutorial.
Before moving on, keep in mind that quantum mechanics and quantum computing are often counterintuitive because they occur only at very small scales and not in everyday life. Even Niels Bohr, one of the founders of quantum theory, once said:
If quantum mechanics hasn’t profoundly shocked you, you haven’t understood it yet. (Source)
— Niels Bohr
Until recently, quantum computing was seen almost entirely as a physics domain, which kept people in other fields from applying it to their own work. The good news is that you don’t need to fully grasp the physics to understand how quantum computers can solve some problems more efficiently than classical ones. You can focus on the concepts and their implementation, and only later dive into the math if you want a deeper understanding.
To start, you’ll look at the basic unit of quantum information—quantum bits, or qubits—and learn how they differ from classical bits.
Qubits, Superposition, and Measurement
Qubits, or quantum bits, are the fundamental units of quantum information. A classical bit can only be 0 or 1, but a qubit can exist in a linear combination of both states at once, a property called superposition. Physically, this usually means that the qubit is a two-level system, such as two energy levels of an atom, that can exist in a superposition of those levels.
Superposition allows qubits to represent multiple possibilities at the same time, allowing quantum computing programs to explore many solutions in parallel.
To understand superposition, think of flipping a coin. A classical coin lands as either heads or tails. A quantum coin, however, can exist in a state that’s a blend of both heads and tails simultaneously. This is what physicists mean when they say a qubit exists in a linear combination of 0 and 1.
The key difference is that while the coin is spinning in the air, you know it’ll eventually be one or zero, whereas a qubit in superposition has no definite value until it is measured.
A qubit remains in superposition until you look at it, or more formally, measure it. Measurement forces the qubit to collapse into one of its basis states, 0 or 1, just like your usual classical bits. The outcome is probabilistic: You can’t know in advance which result you’ll get, but you can calculate the chances based on the qubit’s state beforehand.
Note: Looking at a superposition state actually destroys the superposition and collapses the qubit into a 0 or 1, or in other words, destroys the “quantumness” of the state. This means you can’t visualize qubits like bits. Instead, you view qubits as a mathematical object. However, they can be built from various physical systems: the spin of an electron, the polarization of a photon, or the energy levels of an atom.
Visualizing qubits and superposition is tricky and needs some extra mathematical tools. Turns out, a coin that’s free to rotate in three dimensions is a good analogy for one way of visualizing the quantum state of a qubit, called the Bloch Sphere.
The Bloch sphere is a geometrical, 3D sphere that’s often used to visualize qubits and their superpositions. The states of the qubits, including superpositions, are represented as points on the surface of a sphere. The closer the point is to the north pole, the higher the chance of measuring 0, and vice versa for 1:
Two Bloch spheres showing a qubit in the 0 (left) state and a qubit in the 1 state (right)
For example, the north pole represents the state 0, and the south pole represents 1. What’s interesting about this sphere is that you can visualize superposition states as well:
A Bloch sphere showing a qubit in a superposition state.
Read the full article at https://realpython.com/quantum-computing-basics/ »
[ 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 ]
December 01, 2025 02:00 PM UTC
death and gravity
reader 3.20 released – we're so back
Hi there!
I'm happy to announce version 3.20 of reader, a Python feed reader library.
What's new? #
Here are the highlights since reader 3.16.
Web app re-design #
Earlier this year, I started a web app re-design based on htmx and Bootstrap. I only had time for the main page, but it turned out pretty nice, and I've been using it ever since. What is new is that I now have free time and some plans; watch this space.
Here are some screenshots; it's not much, but it's honest work:
main page (dark mode)
main page – more filters (light mode)
htmx? ugh, how original
Unsurprisingly, htmx is perfect for this kind of application. As CEO of htmx, I can confirm that all the allegations are true – it is indeed a pleasure to use, not to mention a huge improvement over my home-grown, half-assed JavaScript forms framework.
isn't Bootstrap from like 2010 or something? also, how original
I've tried many modern popular CSS frameworks (since forgotten, for my own sanity), and it turns out tab-navigable form controls are a hard, unsolved problem in 2025. Thankfully, if you're willing to time travel (to the future, surely!), Bootstrap comes with excellent documentation, built-in accessibility, and a comprehensive component library.
Entry deduplication #
I may have accidentally rewritten the entry_dedupe plugin. 😅
Sometimes, the id of some or all the entries in a feed changes (e.g. from
example.com/123toexample.com/entry-title), causing each entry to appear twice. entry_dedupe fixes this by copying user attributes to the new entry and deleting the old one.
What started as a quick and dirty plugin now has:
- lots of new, data-driven heuristics to find potential duplicates
- by title (had this one already)
- by link
- by published timestamp
- by title with common prefixes removed
- better approximate content matching
- an extensive test suite
- extensive internal documentation (like this)
That last one is pretty important: the old logic was data-driven too, but because I didn't document my entire reasoning, I wasted more time than I'd like to admit on a useless title similarity heuristic. It's a trope that you should write code such that others can understand it, and "others" includes you six months from now; I don't know about six months, but four years will definitely do it. (If that sounds like an interesting story and you'd like to hear more, drop me a line.)
It's not all bad, though –
a side-effect of better content matching
is that image alt and title attributes
are also included in the full-text search index now,
so you can search xkcd comics by title text.
Project infrastructure #
I've taken some time to modernize the project infrastructure; stuff like:
- Use GitHub Actions to publish releases to PyPI.
- Use dependency groups instead of extras for development dependencies.
- Rewrite contributor documentation and run.sh to optimize for readability.
(I'll admit the uv hype is quite strong, but the vanilla tooling seems fine, for now.)
Read-only Reader #
It's now possible to prevent write operations on a Reader object
through the read_only make_reader() argument.
Thanks to Roman Milko for the pull request!
Python versions #
reader 3.17 added support for PyPy 3.11.
reader 3.18 dropped support for Python 3.10.
reader 3.19 added support for Python 3.14.
That's it for now. For more details, see the full changelog.
Want to contribute? Check out the docs and the roadmap.
Learned something new today? Share it with others, it really helps!
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
Why make your own feed reader? #
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.
December 01, 2025 01:43 PM UTC
Real Python
Quiz: Quantum Computing Basics With Qiskit
Dive into quantum computing fundamentals with this quiz. You’ll practice key ideas like superposition, measurement, entanglement, and how quantum and classical systems work together. You’ll also revisit essential Qiskit commands and understand what limits today’s quantum computers.
Need a refresher? Check out Quantum Computing Basics With Qiskit for clear explanations and hands-on examples.
[ 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 ]
December 01, 2025 12:00 PM UTC
Zero to Mastery
[November 2025] Python Monthly Newsletter 🐍
72nd issue of Andrei's Python Monthly: , and much more. Read the full newsletter to get up-to-date with everything you need to know from last month.
December 01, 2025 10:00 AM UTC
Python Bytes
#460 Overlooked Python Typing
<strong>Topics covered in this episode:</strong><br> <ul> <li><strong><a href="https://adventofcode.com?featured_on=pythonbytes">Advent of Code</a> starts today</strong></li> <li><strong><a href="https://docs.djangoproject.com/en/dev/releases/6.0/?featured_on=pythonbytes">Django 6 is coming</a></strong></li> <li><strong><a href="https://martynassubonis.substack.com/p/advanced-overlooked-python-typing?featured_on=pythonbytes">Advanced, Overlooked Python Typing</a></strong></li> <li><strong><a href="https://github.com/codespell-project/codespell?featured_on=pythonbytes">codespell</a></strong></li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=V_eHMgxxhLY' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="460">Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p>Sponsored by us! Support our work through:</p> <ul> <li>Our <a href="https://training.talkpython.fm/?featured_on=pythonbytes"><strong>courses at Talk Python Training</strong></a></li> <li><a href="https://courses.pythontest.com/p/the-complete-pytest-course?featured_on=pythonbytes"><strong>The Complete pytest Course</strong></a></li> <li><a href="https://www.patreon.com/pythonbytes"><strong>Patreon Supporters</strong></a></li> </ul> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy">@mkennedy@fosstodon.org</a> / <a href="https://bsky.app/profile/mkennedy.codes?featured_on=pythonbytes">@mkennedy.codes</a> (bsky)</li> <li>Brian: <a href="https://fosstodon.org/@brianokken">@brianokken@fosstodon.org</a> / <a href="https://bsky.app/profile/brianokken.bsky.social?featured_on=pythonbytes">@brianokken.bsky.social</a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes">@pythonbytes@fosstodon.org</a> / <a href="https://bsky.app/profile/pythonbytes.fm">@pythonbytes.fm</a> (bsky)</li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually <strong>Monday</strong> at 10am PT. Older video versions available there too.</p> <p>Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to <a href="https://pythonbytes.fm/friends-of-the-show">our friends of the show list</a>, we'll never share it.</p> <p><strong>Brian #1: <a href="https://adventofcode.com?featured_on=pythonbytes">Advent of Code</a> starts today</strong></p> <ul> <li>A few changes, like 12 days this year, which honestly, I’m grateful for.</li> <li>See also: <a href="https://github.com/cak/elf?featured_on=pythonbytes">elf: Advent of Code CLI helper for Python</a></li> </ul> <p><strong>Michael #2: <a href="https://docs.djangoproject.com/en/dev/releases/6.0/?featured_on=pythonbytes">Django 6 is coming</a></strong></p> <ul> <li><em>Expected December 2025</em></li> <li>Django 6.0 supports Python 3.12, 3.13, and 3.14</li> <li>Built-in support for the <a href="https://docs.djangoproject.com/en/6.0/topics/security/#security-csp">Content Security Policy (CSP)</a> standard is now available, making it easier to protect web applications against content injection attacks such as cross-site scripting (XSS).</li> <li>The <a href="https://docs.djangoproject.com/en/6.0/topics/templates/#template-language-intro">Django Template Language</a> now supports <a href="https://docs.djangoproject.com/en/6.0/ref/templates/language/#template-partials">template partials</a>, making it easier to encapsulate and reuse small named fragments within a template file.</li> <li>Django now includes a built-in Tasks framework for running code outside the HTTP request–response cycle. This enables offloading work, such as sending emails or processing data, to background workers.</li> <li>Email handling in Django now uses Python’s modern email API, introduced in Python 3.6. This API, centered around the <a href="https://docs.python.org/3/library/email.message.html#email.message.EmailMessage"><strong><code>email.message.EmailMessage</code></strong></a> class</li> </ul> <p><strong>Brian #3: <a href="https://martynassubonis.substack.com/p/advanced-overlooked-python-typing?featured_on=pythonbytes">Advanced, Overlooked Python Typing</a></strong></p> <ul> <li>get_args, TypeGuard, TypeIs, and more goodies</li> </ul> <p><strong>Michael #4: <a href="https://github.com/codespell-project/codespell?featured_on=pythonbytes">codespell</a></strong></p> <ul> <li>Learned from <a href="https://github.com/mikeckennedy/talk-python-in-production-devops-book/pull/14?featured_on=pythonbytes">this PR for the Talk Python book</a>.</li> <li>Fix common misspellings in text files.</li> <li>It's designed primarily for checking misspelled words in source code (backslash escapes are skipped), but it can be used with other files as well.</li> <li>It does not check for word membership in a complete dictionary, but instead looks for a set of common misspellings. Therefore it should catch errors like "adn", but it will not catch "adnasdfasdf".</li> <li>It shouldn't generate false-positives when you use a niche term it doesn't know about.</li> </ul> <p><strong>Extras</strong></p> <p>Brian:</p> <ul> <li><a href="https://github.com/mkdocs/mkdocs/discussions/4010?featured_on=pythonbytes">Is mkdocs maintained?</a></li> <li><a href="https://hatch.pypa.io/latest/blog/2025/11/24/hatch-v1160/?featured_on=pythonbytes">Hatch 1.16</a></li> </ul> <p>Michael:</p> <ul> <li>Follow up on tach from Gerben Dekker: <ul> <li><strong><code>tach</code> has been unmaintained for a bit but is not anymore</strong>. It was the main product from Gauge which is a Y combinator startup that pivoted to something unrelated and abandoned <code>tach</code>. However, https://github.com/DetachHead forked it but now got access to the main repo and has committed to maintaining it.</li> <li><code>ruff analyze graph</code> is fully independent of <code>tach</code> - we actually started to look into alternatives for <code>tach</code> when it became unmaintained and then found <code>ruff analyze graph</code>.</li> <li>For our use case, with just a bit of manipulation on top of <code>ruff analyze graph</code> we replaced our use of <code>deptry</code> (which was slower - and I try to be careful depending on one-man projects).</li> </ul></li> <li><a href="https://happycanvasandcode.com/posts/my_review/?featured_on=pythonbytes">A Review of Michael Kennedy’s book, “Talk Python in Production”</a> - Thanks Doug</li> </ul> <p><strong>Joke: <a href="https://github.com/hotheadhacker/no-as-a-service?featured_on=pythonbytes">NoaaS</a></strong></p>
December 01, 2025 08:00 AM UTC
Zato Blog
Network Packet Broker Automation in Python
Network Packet Broker Automation in Python

Packet brokers are crucial for network engineers, providing a clear, detailed view of network traffic, aiding in efficient issue identification and resolution.
But what is a network packet broker (NBP) really? Why are they needed? And how to automate one in Python?
➤ Read this article about network packet brokers and their automation in Python to find out more.
More resources
➤ Click here to read more about using Python and Zato in telecommunications
➤ Python API integration tutorials
➤ What is an integration platform?
➤ Open-source iPaaS in Python
December 01, 2025 03:00 AM UTC
Brian Okken
Is Cyber Monday still a thing?
I kinda forgot about CyberMonday.
However, when I saw a bunch of announcements this morning, I figured why not participate.
- The Complete pytest Course bundle
- Use CYBERMONDAY to save 50% off Dec 1 only
- Python Testing with pytest, 2nd edition, eBook
- Pragmatic has left the SAVE50 code up through today.
- Use code SAVE50 to get 50% through Monday, Dec 1
December 01, 2025 12:00 AM UTC
Seth Michael Larson
One weird trick for cheaper physical Switch 2 games?
Maybe sell your boxes? I happened to be browsing PriceCharting and saw that only the box for Kirby Air Riders was selling for $20 on average. I couldn't believe my eyes! But I looked and there were at least two boxes sold on eBay.
I quickly put together this table after manually sorting through the detected eBay listings for three popular Switch 2 titles: Mario Kart World, Donkey Kong Bananza, and Kirby Air Riders. The data only includes listings that actually sold at the price and filters out incorrect listing (such as boxes for the Switch 2 console or bundle). Here are the results in USD:
| Game | Box-Only Price (SOLD) |
|---|---|
| Kirby Air Riders | $16.99 |
| $29.99 (!!!) | |
| Mario Kart World | $17.99 |
| $19.00 | |
| $15.00 | |
| Donkey Kong Bananza | $16.49 |
| $27.97 (!!!) | |
| Average Box Price | $20.49 |
I am not sure what to make of this. If you include a conservative shipping cost of $8 you're still saving money over a digital copy by buying physical and selling the box, assuming you're able to sell the box for an average price. I suspect if everyone were to employ this strategy the return would deteriorate quickly.
If you combine this with the storage cost savings of
buying physical over digital then you're "saving"
even more money despite the $10 price difference.
Something to consider if you're
not a video-game hoarder “collector”, like me.
Thanks for keeping RSS alive! ♥
December 01, 2025 12:00 AM UTC
November 30, 2025
The Python Coding Stack
My Life • The Autobiography of a Python Object
And that’s it. I exist.
I have no recollection of anything before this instant. But I’m very aware of what I am now. I’m an object.
This is the line of code that brought me into existence:

My first recollection from a few moments ago was of being inside Team.__new__(). And I felt an affinity with my clan right away. I was a Team instance—an object of type Team.
But then I was whisked from Team.__new__() straight into Team.__init__(). I wasn’t alone. There were two other objects there with me as we all entered .__init__(). But these other objects weren’t from my own clan. One was from the str clan, “The Invincibles”, and the other was a list object, [”Claire”, “Ishaan”, “Margaret”, “Dan”].
This was my initiation ceremony to become a proper member of the Team clan:
Now, I have a closer bond with the other objects that entered .__init__() with me. They’re my data attributes. They go wherever I go.
It’s quite pleasant here. There’s plenty of camaraderie between objects. Some are very much like me, Team objects. Others are somewhat different. But we’re all here to help each other achieve a common goal.
Make sure you don’t miss a thing here at The Python Coding Stack. Join The Club, the exclusive area for paid subscribers for more Python posts, videos, a members’ forum, and more.
…and you’ll be supporting this publication by becoming a member of The Club.
I’m being rather rude. I apologise. I haven’t introduced myself yet.
I’m <__main__.Team object at 0x101dd38c0>. Yes, that’s me. Honest. If you don’t like hexadecimal numbers, you can use the decimal version of my unique identity number:
That number is who I am. It’s the slot in memory I was allocated when I came into existence. It’s the only way you can identify me for as long as I exist. Python gives each one of us objects a unique number so it can distinguish us.
But you can’t call me by that number. That’s not my name, it’s my identity.
You can call me the_invincibles. That’s also easier than using a number, right? Just bear in mind that some other object may steal that name from me later. But for the time being, the_invincibles will do just fine.
And now you have another way to call me if you prefer. I’ll also respond to all_teams[0]. I have two names now!
But if you enquire who I am in the Python program using print(), you won’t get either of those names. Confusing, I know:
Python needs to display me on the screen. It asks me for help to do this.
“Hey, object 0x101dd38c0, do you have a .__str__() special method?”, Python asks me.
“I don’t”, I respond.
“How about .__repr__()? Do you have one of those?”
Yes, as it happens, I do:
So Python asks me to give it whatever this method returns, and it uses it as the return value for print():
Team(
team_name=’The Invincibles’,
members=[’Claire’, ‘Ishaan’, ‘Margaret’, ‘Dan’]
)Python can’t do anything without my help. I feel good about this!
-- --
Python found this code later in the program.
“Hey there again, you”, Python tells me. “I need to ask you something else. Do you have the .__iter__() special method?”
“Yes, I do. What do you need?” I respond.
“Can you give me the iterator from that method, please?”
Here’s the class showing my .__iter__() method, too:
Python now knows what to do to iterate through me:
Claire
Ishaan
Margaret
DanI told you, Python can’t do anything on its own. It needs my help with everything. Good thing I’m here, and I have all the instructions Python needs to deal with me!
-- --
“Psst, it’s me again”, says Python. “Do you know what this .add_member thing is?”
“Yes, yes, it’s one of my attributes, the stuff I carry with me, like everyone else in my Team clan”, I say.
Here’s the code:
-- --
Oh no! I’ve lost my name. But I’m still here, don’t worry. I still exist. Remember that you can still call me using all_teams[0]:
Here’s proof I still exist:
Claire
Ishaan
Margaret
Dan
KeithBut wait a moment. I just caught a glimpse of the next line of code…
“Hey object, can I check something with you, please?” It’s Python again. What does it want this time?
“Do you still have any names left? Any references left?”
I was worried Python would ask me this. There was a time I had two references—two ways you could get to me: the_invincibles and all_teams[0]. But first I lost the name the_invincibles. Now, the object all_teams no longer exists.
I no longer have any references pointing towards me…
Epilogue
Object 0x101dd38c0 would have loved to be here to say goodbye to you in person, but the object is no longer here. Its existence was brief yet fulfilling. Python has no mercy. The moment it realised there was nothing left in the program referring to object 0x101dd38c0, it sharpened the guillotine and…
The irony is that Python still needed help from the object to figure out that there weren’t any references left. Objects keep track themselves of how many references point to them!
It’s what object 0x101dd38c0 had been telling us all along. Python can’t get anything done without the information that objects carry around with them wherever they go!
Photo by Dayan Rodio: https://www.pexels.com/photo/a-man-reading-book-while-sitting-on-a-bed-4132936/
Code in this article uses Python 3.14
The code images used in this article are created using Snappify. [Affiliate link]
Join The Club, the exclusive area for paid subscribers for more Python posts, videos, a members’ forum, and more.
You can also support this publication by making a one-off contribution of any amount you wish.
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
Further reading related to this article’s topic:
When You No Longer Need That Object • Dealing With Garbage in Python
When Should You Use
.__repr__()vs.__str__()in Python? – Real Python
Appendix: Code Blocks
Code Block #1
the_invincibles = Team(
“The Invincibles”,
[”Claire”, “Ishaan”, “Margaret”, “Dan”],
)
Code Block #2
class Team:
def __init__(self, team_name, members):
self.team_name = team_name
self.members = members
Code Block #3
# ...
print(id(the_invincibles))
# 4326242496
Code Block #4
# ...
all_teams = []
all_teams.append(the_invincibles)
Code Block #5
# ...
print(the_invincibles)
Code Block #6
class Team:
def __init__(self, team_name, members):
self.team_name = team_name
self.members = members
def __repr__(self):
return (f”{type(self).__name__}(”
f”team_name={self.team_name!r}, “
f”members={self.members!r})”)
Code Block #7
# ...
for team_member in the_invincibles:
print(team_member)
Code Block #8
class Team:
def __init__(self, team_name, members):
self.team_name = team_name
self.members = members
def __repr__(self):
return (f”{type(self).__name__}(”
f”team_name={self.team_name!r}, “
f”members={self.members!r})”)
def __iter__(self):
return iter(self.members)
Code Block #9
# ...
the_invincibles.add_member(”Keith”)
Code Block #10
class Team:
def __init__(self, team_name, members):
self.team_name = team_name
self.members = members
def __repr__(self):
return (f”{type(self).__name__}(”
f”team_name={self.team_name!r}, “
f”members={self.members!r})”)
def __iter__(self):
return iter(self.members)
def add_member(self, name):
self.members.append(name)
Code Block #11
# ...
del the_invincibles
Code Block #12
# ...
for team_member in all_teams[0]:
print(team_member)
Code Block #13
# ...
del all_teams
For more Python resources, you can also visit Real Python—you may even stumble on one of my own articles or courses there!
Also, are you interested in technical writing? You’d like to make your own writing more narrative, more engaging, more memorable? Have a look at Breaking the Rules.
And you can find out more about me at stephengruppetta.com
November 30, 2025 11:10 AM UTC
Talk Python to Me
#528: Python apps with LLM building blocks
In this episode, I’m talking with Vincent Warmerdam about treating LLMs as just another API in your Python app, with clear boundaries, small focused endpoints, and good monitoring. We’ll dig into patterns for wrapping these calls, caching and inspecting responses, and deciding where an LLM API actually earns its keep in your architecture.<br/> <br/> <strong>Episode sponsors</strong><br/> <br/> <a href='https://talkpython.fm/seer'>Seer: AI Debugging, Code TALKPYTHON</a><br> <a href='https://talkpython.fm/nordstellar'>NordStellar</a><br> <a href='https://talkpython.fm/training'>Talk Python Courses</a><br/> <br/> <h2 class="links-heading mb-4">Links from the show</h2> <div><strong>Vincent on X</strong>: <a href="https://x.com/fishnets88?featured_on=talkpython" target="_blank" >@fishnets88</a><br/> <strong>Vincent on Mastodon</strong>: <a href="https://fosstodon.org/@koaning" target="_blank" >@koaning</a><br/> <br/> <strong>LLM Building Blocks for Python Co-urse</strong>: <a href="https://training.talkpython.fm/courses/llm-building-blocks-for-python" target="_blank" >training.talkpython.fm</a><br/> <strong>Top Talk Python Episodes of 2024</strong>: <a href="https://talkpython.fm/blog/posts/top-talk-python-podcast-episodes-of-2024/" target="_blank" >talkpython.fm</a><br/> <strong>LLM Usage - Datasette</strong>: <a href="https://llm.datasette.io/en/stable/usage.html?featured_on=talkpython" target="_blank" >llm.datasette.io</a><br/> <strong>DiskCache - Disk Backed Cache (Documentation)</strong>: <a href="https://grantjenks.com/docs/diskcache?featured_on=talkpython" target="_blank" >grantjenks.com</a><br/> <strong>smartfunc - Turn docstrings into LLM-functions</strong>: <a href="https://github.com/koaning/smartfunc?featured_on=talkpython" target="_blank" >github.com</a><br/> <strong>Ollama</strong>: <a href="https://ollama.com?featured_on=talkpython" target="_blank" >ollama.com</a><br/> <strong>LM Studio - Local AI</strong>: <a href="https://lmstudio.ai?featured_on=talkpython" target="_blank" >lmstudio.ai</a><br/> <strong>marimo - A Next-Generation Python Notebook</strong>: <a href="https://marimo.io?featured_on=talkpython" target="_blank" >marimo.io</a><br/> <strong>Pydantic</strong>: <a href="https://pydantic.dev?featured_on=talkpython" target="_blank" >pydantic.dev</a><br/> <strong>Instructor - Complex Schemas & Validation (Python)</strong>: <a href="https://python.useinstructor.com/#complex-schemas-validation" target="_blank" >python.useinstructor.com</a><br/> <strong>Diving into PydanticAI with marimo</strong>: <a href="https://www.youtube.com/watch?v=ujQjqqBka-8" target="_blank" >youtube.com</a><br/> <strong>Cline - AI Coding Agent</strong>: <a href="https://cline.bot?featured_on=talkpython" target="_blank" >cline.bot</a><br/> <strong>OpenRouter - The Unified Interface For LLMs</strong>: <a href="https://openrouter.ai?featured_on=talkpython" target="_blank" >openrouter.ai</a><br/> <strong>Leafcloud</strong>: <a href="https://leaf.cloud?featured_on=talkpython" target="_blank" >leaf.cloud</a><br/> <strong>OpenAI looks for its "Google Chrome" moment with new Atlas web browser</strong>: <a href="https://arstechnica.com/ai/2025/10/openais-new-atlas-web-browser-wants-to-let-you-chat-with-a-page/?featured_on=talkpython" target="_blank" >arstechnica.com</a><br/> <br/> <strong>Watch this episode on YouTube</strong>: <a href="https://www.youtube.com/watch?v=t-ReN9jS9sQ" target="_blank" >youtube.com</a><br/> <strong>Episode #528 deep-dive</strong>: <a href="https://talkpython.fm/episodes/show/528/python-apps-with-llm-building-blocks#takeaways-anchor" target="_blank" >talkpython.fm/528</a><br/> <strong>Episode transcripts</strong>: <a href="https://talkpython.fm/episodes/transcript/528/python-apps-with-llm-building-blocks" target="_blank" >talkpython.fm</a><br/> <br/> <strong>Theme Song: Developer Rap</strong><br/> <strong>🥁 Served in a Flask 🎸</strong>: <a href="https://talkpython.fm/flasksong" target="_blank" >talkpython.fm/flasksong</a><br/> <br/> <strong>---== Don't be a stranger ==---</strong><br/> <strong>YouTube</strong>: <a href="https://talkpython.fm/youtube" target="_blank" ><i class="fa-brands fa-youtube"></i> youtube.com/@talkpython</a><br/> <br/> <strong>Bluesky</strong>: <a href="https://bsky.app/profile/talkpython.fm" target="_blank" >@talkpython.fm</a><br/> <strong>Mastodon</strong>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" ><i class="fa-brands fa-mastodon"></i> @talkpython@fosstodon.org</a><br/> <strong>X.com</strong>: <a href="https://x.com/talkpython" target="_blank" ><i class="fa-brands fa-twitter"></i> @talkpython</a><br/> <br/> <strong>Michael on Bluesky</strong>: <a href="https://bsky.app/profile/mkennedy.codes?featured_on=talkpython" target="_blank" >@mkennedy.codes</a><br/> <strong>Michael on Mastodon</strong>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" ><i class="fa-brands fa-mastodon"></i> @mkennedy@fosstodon.org</a><br/> <strong>Michael on X.com</strong>: <a href="https://x.com/mkennedy?featured_on=talkpython" target="_blank" ><i class="fa-brands fa-twitter"></i> @mkennedy</a><br/></div>
November 30, 2025 08:00 AM UTC
November 28, 2025
Django Weblog
2026 DSF Board Election Results
The 2026 DSF Board Election has closed, and the following candidates have been elected:
- Jacob Kaplan-Moss
- Priya Pahwa
- Ryan Cheley
They will all serve two years for their term.
2026 Board
Directors elected for the 2025 DSF Board - Abigail Gbadago, Jeff Triplett, Paolo Melchiorre, Tom Carrick - are continuing with one year left to serve on the board.
Therefore, the combined 2026 DSF Board of Directors are:
- Abigail Gbadago
- Jacob Kaplan-Moss*
- Jeff Triplett
- Paolo Melchiorre
- Priya Pahwa*
- Ryan Cheley*
- Tom Carrick
* Elected to a two year term

Congratulations to our winners, and a huge thank you to our departing board members Sarah Abderemane and Thibaud Colas.
Thank you again to everyone who nominated themselves. Even if you were not successful, you gave our community the chance to make their voices heard in who they wanted to represent them.
November 28, 2025 06:15 AM UTC
November 27, 2025
Python GUIs
Getting Started With NiceGUI for Web UI Development in Python — Your First Steps With the NiceGUI Library for Web UI Development
NiceGUI is a Python library that allows developers to create interactive web applications with minimal effort. It's intuitive and easy to use. It provides a high-level interface to build modern web-based graphical user interfaces (GUIs) without requiring deep knowledge of web technologies like HTML, CSS, or JavaScript.
In this article, you'll learn how to use NiceGUI to develop web apps with Python. You'll begin with an introduction to NiceGUI and its capabilities. Then, you'll learn how to create a simple NiceGUI app in Python and explore the basics of the framework's components. Finally, you'll use NiceGUI to handle events and customize your app's appearance.
To get the most out of this tutorial, you should have a basic knowledge of Python. Familiarity with general GUI programming concepts, such as event handling, widgets, and layouts, will also be beneficial.
Installing NiceGUI
Before using any third-party library like NiceGUI, you must install it in your working environment. Installing NiceGUI is as quick as running the python -m pip install nicegui command in your terminal or command line. This command will install the library from the Python Package Index (PyPI).
It's a good practice to use a Python virtual environment to manage dependencies for your project. To create and activate a virtual environment, open a command line or terminal window and run the following commands in your working directory:
- Windows
- macOS
- Linux
PS> python -m venv .\venv
PS> .\venv\Scripts\activate
$ python -m venv venv/
$ source venv/bin/activate
$ python3 -m venv venv/
$ source venv/bin/activate
The first command will create a folder called venv/ containing a Python virtual environment. The Python version in this environment will match the version you have installed on your system.
Once your virtual environment is active, install NiceGUI by running:
(venv) $ python -m pip install nicegui
With this command, you've installed NiceGUI in your active Python virtual environment and are ready to start building applications.
Writing Your First NiceGUI App in Python
Let's create our first app with NiceGUI and Python. We'll display the traditional "Hello, World!" message in a web browser. To create a minimal NiceGUI app, follow these steps:
- Import the
niceguimodule. - Create a GUI element.
- Run the application using the
run()method.
Create a Python file named app.py and add the following code:
from nicegui import ui
ui.label('Hello, World!').classes('text-h1')
ui.run()
This code defines a web application whose UI consists of a label showing the Hello, World! message. To create the label, we use the ui.label element. The call to ui.run() starts the app.
Run the application by executing the following command in your terminal:
(venv) $ python app.py
This will open your default browser, showing a page like the one below:
First NiceGUI Application
Congratulations! You've just written your first NiceGUI web app using Python. The next step is to explore some features of NiceGUI that will allow you to create fully functional web applications.
If the above command doesn't open the app in your browser, then go ahead and navigate to http://localhost:8080.
Exploring NiceGUI Graphical Elements
NiceGUI elements are the building blocks that we'll arrange to create pages. They represent UI components like buttons, labels, text inputs, and more. The elements are classified into the following categories:
In the following sections, you'll code simple examples showcasing a sample of each category's graphical elements.
Text Elements
NiceGUI also has a rich set of text elements that allow you to display text in several ways. This set includes some of the following elements:
The following demo app shows how to create some of these text elements:
from nicegui import ui
# Text elements
ui.label("Label")
ui.link("PythonGUIs", "https://pythonguis.com")
ui.chat_message("Hello, World!", name="PythonGUIs Chatbot")
ui.markdown(
"""
# Markdown Heading 1
**bold text**
*italic text*
`code`
"""
)
ui.restructured_text(
"""
==========================
reStructuredText Heading 1
==========================
**bold text**
*italic text*
``code``
"""
)
ui.html("<strong>bold text using HTML tags</strong>")
ui.run(title="NiceGUI Text Elements")
In this example, we create a simple web interface showcasing various text elements. The page shows several text elements, including a basic label, a hyperlink, a chatbot message, and formatted text using the Markdown and reStructuredText markup languages. Finally, it shows some raw HTML.
Each text element allows us to present textual content on the page in a specific way or format, which gives us a lot of flexibility for designing modern web UIs.
Run it! Your browser will open with a page that looks like the following.
Text Elements Demo App in NiceGUI
Control Elements
When it comes to control elements, NiceGUI offers a variety of them. As their name suggests, these elements allow us to control how our web UI behaves. Here are some of the most common control elements available in NiceGUI:
- Buttons
- Dropdown lists
- Toggle buttons
- Radio buttons
- Checkboxes
- Sliders
- Switches
- Text inputs
- Text areas
- Date input
The demo app below showcases some of these control elements:
from nicegui import ui
# Control elements
ui.button("Button")
with ui.dropdown_button("Edit", icon="edit", auto_close=True):
ui.item("Copy")
ui.item("Paste")
ui.item("Cut")
ui.toggle(["ON", "OFF"], value="ON")
ui.radio(["NiceGUI", "PyQt6", "PySide6"], value="NiceGUI").props("inline")
ui.checkbox("Enable Feature")
ui.slider(min=0, max=100, value=50, step=5)
ui.switch("Dark Mode")
ui.input("Your Name")
ui.number("Age", min=0, max=120, value=25, step=1)
ui.date(value="2025-04-11")
ui.run(title="NiceGUI Control Elements")
In this app, we include several control elements: a button, a dropdown menu with editing options (Copy, Paste, Cut), and a toggle switch between ON and OFF states. We also have a radio button group to choose between GUI frameworks (NiceGUI, PyQt6, PySide6), a checkbox labeled Enable Feature, and a slider to select a numeric value within a range.
Further down, we have a switch to toggle Dark Mode, a text input field for entering a name, a number input for providing age, and a date picker. Each of these controls has its own properties and methods that you can tweak to customize your web interfaces using Python and NiceGUI.
Note that the elements on this app don't perform any action. Later in this tutorial, you'll learn about events and actions. For now, we're just showcasing some of the available graphical elements of NiceGUI.
Run it! You'll get a page that will look something like the following.
Text Elements Demo App in NiceGUI
Data Elements
If you're in the data science field, then you'll be thrilled with the variety of data elements that NiceGUI offers. You'll find elements for some of the following tasks:
- Representing data in a tabular format
- Creating plots and charts
- Building different types of progress charts
- Displaying 3D objects
- Using maps
- Creating tree and log views
- Presenting and editing text in different formats, including plain text, code, and JSON
Here's a quick NiceGUI app where we use a table and a plot to present temperature measurements against time:
from matplotlib import pyplot as plt
from nicegui import ui
# Data elements
time = [1, 2, 3, 4, 5, 6]
temperature = [30, 32, 34, 32, 33, 31]
columns = [
{
"name": "time",
"label": "Time (min)",
"field": "time",
"sortable": True,
"align": "right",
},
{
"name": "temperature",
"label": "Temperature (ºC)",
"field": "temperature",
"required": True,
"align": "right",
},
]
rows = [
{"temperature": temperature, "time": time}
for temperature, time in zip(temperature, time)
]
ui.table(columns=columns, rows=rows, row_key="name")
with ui.pyplot(figsize=(5, 4)):
plt.plot(time, temperature, "-o", color="blue", label="Temperature")
plt.title("Temperature vs Time")
plt.xlabel("Time (min)")
plt.ylabel("Temperature (ºC)")
plt.ylim(25, 40)
plt.legend()
ui.run(title="NiceGUI Data Elements")
In this example, we create a web interface that displays a table and a line plot. The data is stored in two lists: one for time (in minutes) and one for temperature (in degrees Celsius). These values are formatted into a table with columns for time and temperature. To render the table, we use the ui.table element.
Below the table, we create a Matplotlib plot of temperature versus time and embed it in the ui.pyplot element. The plot has a title, axis labels, and a legend.
Run it! You'll get a page that looks something like the following.
Data Elements Demo App in NiceGUI
Audiovisual Elements
NiceGUI also has some elements that allow us to display audiovisual content in our web UIs. The audiovisual content may include some of the following:
Below is a small demo app that shows how to add a local image to your NiceGUI-based web application:
from nicegui import ui
with ui.image("./otje.jpg"):
ui.label("Otje the cat!").classes(
"absolute-bottom text-subtitle2 text-center"
)
ui.run(title="NiceGUI Audiovisual Elements")
In this example, we use the ui.image element to display a local image on your NiceGUI app. The image will show a subtitle at the bottom.
NiceGUI elements provide the classes() method, which allows you to apply Tailwind CSS classes to the target element. To learn more about using CSS for styling your NiceGUI apps, check the Styling & Appearance section in the official documentation.
Run it! You'll get a page that looks something like the following.

Audiovisual Elements Demo App in NiceGUI
Laying Out Pages in NiceGUI
Laying out a GUI so that every graphical component is in the right place is a fundamental step in any GUI project. NiceGUI offers several elements that allow us to arrange graphical elements to build a nice-looking UI for our web apps.
Here are some of the most common layout elements:
- Cards wrap another element in a frame.
- Column arranges elements vertically.
- Row arranges elements horizontally.
- Grid organizes elements in a grid of rows and columns.
- List displays a list of elements.
- Tabs organize elements in dedicated tabs.
You'll find several other elements that allow you to tweak how your app's UI looks. Below is a demo app that combines a few of these elements to create a minimal but well-organized user profile form:
from nicegui import ui
with ui.card().classes("w-full max-w-3xl mx-auto shadow-lg"):
ui.label("Profile Page").classes("text-xl font-bold")
with ui.row().classes("w-full"):
with ui.card():
ui.image("./profile.png")
with ui.card_section():
ui.label("Profile Image").classes("text-center font-bold")
ui.button("Change Image", icon="photo_camera")
with ui.card().classes("flex-grow"):
with ui.column().classes("w-full"):
name_input = ui.input(
placeholder="Your Name",
).classes("w-full")
gender_select = ui.select(
["Male", "Female", "Other"],
).classes("w-full")
eye_color_input = ui.input(
placeholder="Eye Color",
).classes("w-full")
height_input = ui.number(
min=0,
max=250,
value=170,
step=1,
).classes("w-full")
weight_input = ui.number(
min=0,
max=500,
value=60,
step=0.1,
).classes("w-full")
with ui.row().classes("justify-end gap-2 q-mt-lg"):
ui.button("Reset", icon="refresh").props("outline")
ui.button("Save", icon="save").props("color=primary")
ui.run(title="NiceGUI Layout Elements")
In this app, we create a clean, responsive profile information page using a layout based on the ui.card element. We center the profile form and cap it at a maximum width for better readability on larger screens.
We organize the elements into two main sections:
-
A profile image card on the left and a form area on the right. The left section displays a profile picture using the
ui.imageelement with a Change Image button underneath. -
A series of input fields for personal information, including the name in a
ui.inputelement, the gender in aui.selectelement, the eye color in aui.inputelement, and the height and weight inui.numberelements. At the bottom of the form, we add two buttons: Reset and Save.
We use consistent CSS styling throughout the layout to guarantee proper spacing, shadows, and responsive controls. This ensures that the interface looks professional and works well across different screen sizes.
Run it! Here's how the form looks on the browser.
A Demo Profile Page Layout in NiceGUI
Handling Events and Actions in NiceGUI
In NiceGUI, you can handle events like mouse clicks, keystrokes, and similar ones as you can in other GUI frameworks. Elements typically have arguments like on_click and on_change that are the most direct and convenient way to bind events to actions.
Here's a quick app that shows how to make a NiceGUI app perform actions in response to events:
from nicegui import ui
def on_button_click():
ui.notify("Button was clicked!")
def on_checkbox_change(event):
state = "checked" if event.value else "unchecked"
ui.notify(f"Checkbox is {state}")
def on_slider_change(event):
ui.notify(f"Slider value: {event.value}")
def on_input_change(event):
ui.notify(f"Input changed to: {event.value}")
ui.label("Event Handling Demo")
ui.button("Click Me", on_click=on_button_click)
ui.checkbox("Check Me", on_change=on_checkbox_change)
ui.slider(min=0, max=10, value=5, on_change=on_slider_change)
ui.input("Type something", on_change=on_input_change)
ui.run(title="NiceGUI Events & Actions Demo")
In this app, we first define four functions we'll use as actions. When we create the control elements, we use the appropriate argument to bind an event to a function. For example, in the ui.button element, we use the on_click argument, which makes the button call the associated function when we click it.
We do something similar with the other elements, but use different arguments depending on the element's supported events.
You can check the documentation of elements to learn about the specific events they can handle.
Using the on_* type of arguments is not the only way to bind events to actions. You can also use the on() method, which allows you to attach event handlers manually. This approach is handy for less common events or when you want to attach multiple handlers.
Here's a quick example:
from nicegui import ui
def on_click(event):
ui.notify(f"Button was clicked!")
def on_hover(event):
ui.notify(f"Button was hovered!")
button = ui.button("Button")
button.on("click", on_click)
button.on("mouseover", on_hover)
ui.run()
In this example, we create a small web app with a single button that responds to two different events. When you click the button, the on_click() function triggers a notification. Similarly, when you hover the mouse over the button, the on_hover() function displays a notification.
To bind the events to the corresponding function, we use the on() method. The first argument is a string representing the name of the target event. The second argument is the function that we want to run when the event occurs.
Conclusion
In this tutorial, you've learned the basics of creating web applications with NiceGUI, a powerful Python library for web GUI development.
You've explored common elements, layouts, and event handling. This gives you the foundation to build modern and interactive web interfaces. For further exploration and advanced features, refer to the official NiceGUI documentation.
November 27, 2025 06:00 AM UTC
November 26, 2025
Real Python
How to Convert Bytes to Strings in Python
Converting bytes into readable strings in Python is an effective way to work with raw bytes fetched from files, databases, or APIs. You can do this in just three steps using the bytes.decode() method. This guide lets you convert byte data into clean text, giving you a result similar to what’s shown in the following example:
>>> binary_data = bytes([100, 195, 169, 106, 195, 160, 32, 118, 117])
>>> binary_data.decode(encoding="utf-8")
'déjà vu'
By interpreting the bytes according to a specific character encoding, Python transforms numeric byte values into their corresponding characters. This allows you to seamlessly handle data loaded from files, network responses, or other binary sources and work with it as normal text.
A byte is a fundamental unit of digital storage and processing. Composed of eight bits (binary digits), it’s a basic building block of data in computing. Bytes represent a vast range of data types and are used extensively in data storage and in networking. It’s important to be able to manage and handle bytes where they come up. Sometimes they need to be converted into strings for further use or comprehensibility.
By the end of this guide, you’ll be able to convert Python bytes to strings so that you can work with byte data in a human-readable format.
Get Your Code: Click here to download the free sample code that you’ll use to convert bytes to strings in Python.
Step 1: Obtain the Byte Data
Before converting bytes to strings, you’ll need some actual bytes to work with. In everyday programming, you may not have to deal with bytes directly at all, as Python often handles their encoding and decoding behind the scenes.
Binary data exchanged over the internet can be expressed in different formats, such as raw binary streams, Base64, or hexadecimal strings. When you browse a web page, download a file, or chat with a colleague, the data that emerges travels as numeric bytes before it is interpreted as text that you can read.
In this step, however, you’ll obtain byte data using one of two approaches:
- Using the
bytesliteral (b"") - Using the
urllibpackage
You’ll soon find that using the urllib package requires that you go online. You can, however, create bytes manually without reaching out to the internet at all. You do this by prefixing a string with b, which creates a bytes literal containing the text inside:
raw_bytes = b"These are some interesting bytes"
You may be wondering why you have to create a bytes object at all from strings that you can read. This isn’t just a convenience. While bytes and strings share most of their methods, you can’t mix them freely. If you pass string arguments to a bytes method, then you’ll get an error:
>>> raw_bytes = b"These are some interesting bytes"
>>> raw_bytes.replace("y", "o")
Traceback (most recent call last):
...
TypeError: a bytes-like object is required, not 'str'
A bytes object only accepts other bytes-like objects as arguments. If you try to use a string like "y" with a bytes method, then Python raises a TypeError. To work with raw binary data, you must explicitly use bytes, not strings.
Note that you can represent the same information using alternative numeral formats, including binary, decimal, or hexadecimal. For instance, in the following code snippet, you convert the same bytes object from the above code example into hexadecimal and decimal formats:
Read the full article at https://realpython.com/convert-python-bytes-to-strings/ »
[ 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 ]
November 26, 2025 02:00 PM UTC
scikit-learn
Interview with Virgil Chan, scikit-learn Team Member
BIO: Virgil Chan is currently a Forward Deployed Engineer - Pre-Sales at Union.ai. Before that, he worked as a consultant in the San Francisco Bay Area, specialising in predictive data analytics and machine learning. Earlier, he studied mathematics before moving into data science. Virgil joined the scikit-learn team as a Contributor Experience Team member in December 2024.
- GitHub: @virchan
- LinkedIn: @virgil-chan
- Website: https://virchan.github.io
-
Tell us about yourself.
My name is Virgil, and I’m currently working as a Forward Deployed Engineer – Pre-Sales at Union.ai. Based in San Jose, California, I previously worked as a consultant, using libraries from the scientific Python ecosystem on data science and machine-learning projects, including medical data analysis, traffic-network prediction, and model evaluation. Before deciding that computers are more fun, I was doing mathematical research in topology.
-
How did you first get involved in open source?
I first got involved in open source during the COVID-19 lockdown. I used that time to study Python programming, data science, analytics, and machine learning, and that’s when I discovered libraries like NumPy, Pandas, scikit-learn, NetworkX, and TensorFlow. Once I became more confident in my skills, I started working as a consultant and used these libraries to deliver data-driven solutions for clients.
-
We would love to learn of your open source journey.
I was transitioning from academia into software development, and I quickly learnt that companies valued hands-on experience more than an advanced degree. At the same time, the rise of GPU-driven workloads and LLM-based solutions made my earlier consulting projects look less impressive on paper. I ended up stuck in the infinite loop of no-job-no-experience.
Even though I came from a non-traditional background, and my resume didn’t match what recruiters and ATS systems usually look for, I’ve always believed that my experience is something I can build myself. Since companies weren’t keen on training junior developers, open source became one of the not-so-many viable paths. I started looking for a project where I could grow, be useful, and apply my academic training in a meaningful way. That search naturally led me to scikit-learn.
-
How did you get involved in scikit-learn?
My first PR to scikit-learn (scikit-learn/scikit-learn#27913) was a classic “good first issue”: adding the URL of a scikit-learn example to the relevant places in the documentation. I opened it in December 2023 and it was merged into the main branch in March 2024. Maren helped me navigate the codebase and understand the CI workflow, which gave me a solid foundation for later contributions. Even though I’m now more experienced with the contributing workflow, I still revisit that PR from time to time to remind myself of the challenges first-time contributors face, and how I can support them.
My next PR (scikit-learn/scikit-learn#29709) was more technical, fixing a bug in the (root) mean squared log error function. The expected behaviour was to check that inputs were in the domain of $\log(1 + x)$, but the implementation at the time checked the domain of $\log(x)$ instead. It was one of the few issues I fully understood and knew how to solve, so I volunteered to create a PR. Adrin reviewed it and mentored me throughout the process. Once everything looked good and the CI passed, he asked me to add array API support to the function. And that’s where the fun began.
I had no idea what the array API was, but I already had the habit of reading discussions and merged PRs in my spare time. With a bit of Googling, I quickly understood what needed to be done and the broader importance of the array API project. In fact, completing the array API project has become one of my mid-term goals for my scikit-learn work. Under the guidance of Adrin, Guillaume, Olivier, and Omar, my PRs improved, and contributing became even more rewarding because of how supportive the maintainers were. I also started reviewing PRs, especially from first-time contributors working on the same “good first issue” I began with. In December 2024, I joined the scikit-learn team.
I’m honoured that the team welcomed me and trusted me with more responsibility, such as representing scikit-learn at the Scientific Python Developer Summit in May 2025, implementing temperature scaling as a new feature (with Christian), and having the ability to run CUDA CI myself. It feels good to pass the same positivity I received back into the community.
-
To which OSS projects and communities do you contribute?
I’m also interested in scaling machine-learning algorithms, so I’ve been exploring CUDA and cuML as well.
-
What is alluring about OSS?
Open source fosters a collaborative environment where everyone wins: end-users, maintainers, and contributors. Because it is volunteer-driven, it becomes easier to recognise that the problem itself is the problem, the bug or the issue, rather than the people involved. As a result, the usual institutional complications, such as power or ego struggles, conflicts of interest over funding, or pressure from deadlines, are far less likely to drag the project down. People have more freedom to focus on solving problems, which creates an ideal environment for exploration, experimentation, collaboration, learning, and growth.
Open source has given me the chance to grow, develop new skills, and broaden my perspective, something I’ve been battling since finishing college. By trading my time for responsibility, I’ve found open source to be a meaningful and genuinely rewarding experience.
-
What are your favorite resources, books, courses, conferences, etc?
I found the interview between scikit-learn and Code for Thought on YouTube. The maintainers shared their open-source journeys from how they got started to how they became involved in scikit-learn, which I found inspiring and motivating. For example, I can’t agree more with Gael’s point that “open source should be spontaneous” and that “a diversity of opinion will make better software.” I also learned from Adrin that I could get more involved in the project by becoming the second reviewer for a PR, which gave me the confidence to start reviewing PRs. I think this interview can help people understand the project from a more human and non-technical perspective.
-
What are your hobbies, outside of work and open source?
If I’m done with work and house chores, I usually listen to music. I enjoy classical music (Mozart, Brahms, Rachmaninoff, etc.), and I’m currently getting more exposure to Chopin’s work. I also like Rock ‘n’ Roll (Led Zeppelin, Eric Clapton, Deep Purple, etc.), and I find that AC/DC can “push me to eleven” whenever I’m stuck at work.
I also enjoy reading novels. At the moment I’m reading The Silmarillion by Tolkien, and my to-read list keeps growing.
I like hanging out with cats as well. I volunteer with an animal rescue group in San Jose, where I help care for the cats in their sanctuary and assist at adoption fairs.
November 26, 2025 12:00 AM UTC
November 25, 2025
PyCoder’s Weekly
Issue #710: FastAPI, Floodfill, 20 Years of Django, and More (Nov. 25, 2025)
#710 – NOVEMBER 25, 2025
View in Browser »
Serve a Website With FastAPI Using HTML and Jinja2
Use FastAPI to render Jinja2 templates and serve dynamic sites with HTML, CSS, and JavaScript, then add a color picker that copies hex codes.
REAL PYTHON
Quiz: Serve a Website With FastAPI Using HTML and Jinja2
Review how to build dynamic websites with FastAPI and Jinja2, and serve HTML, CSS, and JS with HTMLResponse and StaticFiles.
REAL PYTHON
Floodfill Algorithm in Python
The floodfill algorithm is used to fill a color in a bounded area. Learn how it works and how to implement it in Python.
RODRIGO GIRÃO SERRÃO
New guide: The Engineering Leader AI Imperative
Augment Code’s new guide features real frameworks to lead your engineering team to systematic transformation: 30% faster PR velocity, 40% reduction in merge times, and 10x task speed-ups across teams. Learn from CTOs at Drata, Webflow, and Tilt who’ve scaled AI across 100+ developer teams →
AUGMENT CODE sponsor
Twenty Years of Django Releases
On November 16th, Django celebrated its 20th anniversary. This quick post highlights a few stats along the way.
DJANGO SOFTWARE FOUNDATION
Python Jobs
Python Video Course Instructor (Anywhere)
Python Tutorial Writer (Anywhere)
Articles & Tutorials
The Uselessness of “Fast” and “Slow” in Programming
“One of the unique aspects of software is how it spans such a large number of orders of magnitude.” The huge difference makes the terms “fast” and “slow” arbitrary. Read on to discover how this effects our thinking as programmers and what mistakes it can cause.
JEREMY BOWERS
New Login Verification for TOTP-based Logins
Previously, when logging into PyPI with a Time-based One-Time Password (TOTP) authenticator, a successful response was sufficient. Now, if you log in from a new device, PyPI will send a verification email. Read all about how this protects PyPI users.
DUSTIN INGRAM
A Better Way to Watch Your Python Apps—Now with AI in the Loop
Scout’s local MCP server lets your AI assistant query real Python telemetry. Call endpoints like get_app_error_groups or get_app_endpoint_traces to surface top errors, latency, and backtraces—no dashboards, no tab-switching, all from chat →
SCOUT APM sponsor
Manim: Create Mathematical Animations
Learn how to use Manim, the animation engine behind 3Blue1Brown, to create clear and compelling visual explanations with Python. This walkthrough shows how you can turn equations and concepts into smooth animations for data science storytelling.
CODECUT.AI • Shared by Khuyen Tran
The Varying Strictness of TypedDict
Brett came across an unexpected typing error when using Pyrefly on his code. He verified it with Pyright, and found the same problem. This post describes the issue and why ty let it pass.
BRETT CANNON
Exploring Class Attributes That Aren’t Really Class Attributes
Syntax used for data classes and typing.NamedTuple confused Stephen when first learning it. Learn why, and how he cleared up his understanding.
STEPHEN GRUPPETTA
Unnecessary Parentheses in Python
Python’s ability to use parentheses for grouping can often confuse new Python users into over-using parentheses in ways that they shouldn’t be used.
TREY HUNNER
Build an MCP Client to Test Servers From Your Terminal
Follow this Python project to build an MCP client that discovers MCP server capabilities and feeds an AI-powered chat with tool calls.
REAL PYTHON
Quiz: Build an MCP Client to Test Servers From Your Terminal
Learn how to create a Python MCP client, start an AI-powered chat session, and run it from the command line. Check your understanding.
REAL PYTHON
Break Out of Loops With Python’s break Keyword
Learn how Python’s break lets you exit for and while loops early, with practical demos from simple games to everyday data tasks.
REAL PYTHON course
Cursor vs. Claude for Django Development
This article looks at how Cursor and Claude compare when developing a Django application.
ŠPELA GIACOMELLI
Projects & Code
Events
Weekly Real Python Office Hours Q&A (Virtual)
November 26, 2025
REALPYTHON.COM
PyDelhi User Group Meetup
November 29, 2025
MEETUP.COM
Melbourne Python Users Group, Australia
December 1, 2025
J.MP
PyBodensee Monthly Meetup
December 1, 2025
PYBODENSEE.COM
Happy Pythoning!
This was PyCoder’s Weekly Issue #710.
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 ]
November 25, 2025 07:30 PM UTC
Real Python
Getting Started With Claude Code
Learn how to set up and start using Claude Code to boost your Python workflow. Learn how it differs from Claude Chat and how to use it effectively in your development setup.
You’ll learn how to:
- Install and configure Claude Code
- Run it safely inside project directories
- Work with CLAUDE.md for task context
- Use Git integration for smoother coding workflows
- Apply Claude Code to automate real programming tasks
[ 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 ]
November 25, 2025 02:00 PM UTC
Python Software Foundation
PSF Code of Conduct Working Group Shares First Transparency Report
The PSF’s Code of Conduct Working Group is a group of volunteers whose purpose is to foster a diverse and inclusive Python community by enforcing the PSF Code of Conduct, along with providing guidance and recommendations to the Python community on codes of conduct, that supports the PSF mission support and facilitate the growth of a diverse and international community of Python programmers.
The working group has recently committed to publishing annual transparency reports and we are pleased to share the first report with you today, for the 2024 calendar year. The initial transparency report took some time to produce, but we've improved our recording keeping practices to make future reports easier to prepare.
The Working Group spent time formalizing our record keeping this year, and going forward we plan to publish our transparency reports in the first quarter of each year. Each year’s report will be added to the same place in the PSF's Code of Conduct documentation so that community members can easily access them. If you have thoughts or feedback on how to make these reports more useful, we welcome you to send us an email at conduct-wg@python.org.
November 25, 2025 01:51 PM UTC
HoloViz
Rich parameters & reactive programming with Param: 2.3 release
November 25, 2025 12:00 AM UTC
Seth Michael Larson
WebKit browsers see telephone numbers everywhere
Just like Excel seeing everything as a date,
WebKit mobile browsers automatically interpret many numbers as telephone
numbers. When detected, mobile browsers replace the text in the HTML with
a clickable <a href="tel:..."> value that when selected will call the
number denoted. This can be helpful sometimes, but frustrating other
times as random numbers in your HTML suddenly become useless hyperlinks.
Below I've included numbers that may be turned into phone numbers so you can see for yourself why this may be a problem and how many cases there are. Numbers that are detected as a phone number by your browser are highlighted blue by this CSS selector:
a[href^=tel] {
background-color: #00ccff;
}
None of the values below are denoted as telephone number links
in the source HTML, they are all automatically created by the browser.
If you're not using WebKit, enable this check-box to show WebKit's behavior:
- 2
- 22
- 222
- 2222
- 22222
- 222222
- 2222222
- 22222222
- 222222222
- 2222222222
- 22222222222
- 111111111111
- 222222222222
- 555555555555
- 1111111111111
- 2222222222222 (???)
- 5555555555555
- 11111111111111
- 22222222222222
- 55555555555555
- 111111111111111
- 222222222222222
- 555555555555555
- 2-2
- 2-2-2
- 22-2-2
- 22-22-2
- 22-22-22
- 22-22-222
- 22-222-222
- 222-222-222
- 222-222-2222
- 222-2222-2222
- 2222-2222-2222
- 2222-2222-22222
- 2222-22222-22222
- 22222-22222-22222
- 2 222-222-2222
- +1 222-222-2222
- +2 222-222-2222 (There is no +2 country code...)
- +28 222-222-2222 (Unassigned codes aren't used)
- +1222-222-2222
- +2222-222-2222
- (+1)222-222-2222
- (+2)222-222-2222
- (1)222-222-2222
- (2)222-222-2222
- (1222-222-2222
- (1 222-222-2222
- 1)222-222-2222
- 222–222–2222 (en-dashes)
- 222—222—2222 (em-dashes)
- [1]222-222-2222
- <1>222-222-2222
Are there any other combinations that get detected as telephone numbers that I missed? Send me a pull request or email.
How to prevent automatic telephone number detection?
So how can you prevent browsers from parsing telephone numbers automatically?
Add this HTML to your <head> section:
<meta name="format-detection" content="telephone=no">
This will disable automatic telephone detection, and then you can be explicit about
clickable telephone numbers by using the tel: URL scheme like so:
<a href="tel:+222-222-222-2222">(+222)222-222-2222</a>
Thanks for keeping RSS alive! ♥
November 25, 2025 12:00 AM UTC
November 24, 2025
Rodrigo Girão Serrão
Generalising itertools.pairwise
In this article you will learn about itertools.pairwise, how to use it, and how to generalise it.
In this tutorial you will learn to use and generalise itertools.pairwise.
You will understand what itertools.pairwise does, how to use it, and how to implement a generalised version for when itertools.pairwise isn't enough.
itertools.pairwise
itertools.pairwise is an iterable from the standard module itertools that lets you access overlapping pairs of consecutive elements of the input iterable.
That's quite a mouthful, so let me translate:
You give
pairwisean iterable, like"ABCD", andpairwisegives you pairs back, like("A", "B"),("B", "C"), and("C", "D").
In loops, it is common to unpack the pairs directly to perform some operation on both values.
The example below uses pairwise to determine how the balance of a bank account changed based on the balance history:
from itertools import pairwise # Python 3.10+
balance_history = [700, 1000, 800, 750]
for before, after in pairwise(balance_history):
change = after - before
print(f"Balance changed by {change:+}.")
Balance changed by +300.
Balance changed by -200.
Balance changed by -50.
How to implement pairwise
If you had to implement pairwise, you might think of something like the code below:
def my_pairwise(iterable):
for prev_, next_ in zip(iterable, iterable[1:]):
yield (prev_, next_)
Which directly translates to
def my_pairwise(iterable):
yield from zip(iterable, iterable[1:])
But there is a problem with this implementation, and that is the slicing operation.
pairwise is supposed to work with any iterable and not all iterables are sliceable.
For example, files are iterables but are not sliceable.
There are a couple of different ways to fix this but my favourite uses collections.deque with its parameter maxlen:
from collections import deque
from itertools import islice
def my_pairwise(data):
data = iter(data)
window = deque(islice(data, 1), maxlen=2)
for value in data:
window.append(value)
yield tuple(window)
Generalising itertools.pairwise
pairwise will always produce pairs of consecutive elements, but sometimes you might want tuples of different sizes.
For example, you might want something like “triplewise”, to get triples of consecutive elements, but pairwise can't be used for that.
So, how do you implement that generalisation?
In the upcoming subsections I will present different ways of implementing the function nwise(iterable, n) that accepts an iterable and a positive integer n and produces overlapping tuples of n elements taken from the given iterable.
Some example applications:
nwise("ABCD", 2) -> ("A", "B"), ("B", "C"), ("C", "D")
nwise("ABCD", 3) -> ("A", "B", "C"), ("B", "C", "D")
nwise("ABCD", 4) -> ("A", "B", "C", "D")
Using deque
The implementation of pairwise that I showed above can be adapted for nwise:
from collections import deque
from itertools import islice
def nwise(iterable, n):
iterable = iter(iterable)
window = deque(islice(iterable, n - 1), maxlen=n)
for value in iterable:
window.append(value)
yield tuple(window)
Note that you have to change maxlen=2 to maxlen=n, but also islice(iterable, 1) to islice(iterable, n - 1).
Using tee
Another fundamentally different way of implement nwise is by using itertools.tee to split the input...
November 24, 2025 06:36 PM UTC
EuroPython Society
New EuroPython Society Fellow in 2025
A warm welcome to Martin Borus as the second elected EuroPython Society Fellow in 2025.
EuroPython Society Fellows
EuroPython Society Fellows have contributed significantly towards our mission, the EuroPython conference and the Society as an organisation. They are eligible for a lifetime free attendance of the EuroPython conference and will be listed on our EuroPython Society Fellow Grant page in recognition of their work.
Martin has been part of the Europython Conferences volunteers since 2017.
Some have “met” him the first time in a response on an issue sent to the helpdesk or during the organisation meetings of the Ops team.
Others interacted with him as a volunteer at reception, out in the halls, a tutor in a Humble Data tutorial, a session chair, or a room manager in a tutorial or talk.
While pretending to be just an “On-site volunteer” or a member of the “Operations team” — over time, he has taken over not only the organisation of the registration desk, but also developed and orchestrated the training of session chairs.
He also expanded the programme with the informal session for first time conference attendees, which have been well received by all attendees.
The EuroPython Society Board would like to congratulate and thank all Fellows for their tireless work towards our mission! If you want to send in your nomination, check out our Fellowship page and get in touch!
Many thanks,
EuroPython Society
https://www.europython-society.org/
November 24, 2025 05:58 PM UTC
Trey Hunner
Python Black Friday & Cyber Monday sales (2025)
It’s time for some discounted Python-related skill-building. This is my eighth annual compilation of Python learning-related Black Friday & Cyber Monday deals. If you find a Python-related deal in the next week that isn’t on this list, please contact me.
Python-related sales
It’s not even Black Friday yet, but most of these Python-related Black Friday sales are already live:
- Python Morsels: I’m offering lifetime access for the second time ever (more details below)
- Data School: a new subscription to access all of Kevin’s 7 courses plus all upcoming courses
- Talk Python: AI Python bundle, the Everything Bundle, and Michael’s Talk Python in Production
- Reuven Lerner: get 20% off your first year of the LernerPython+data tier (code
BF2025) - Brian Okken: get 50% off his pytest books and courses (code
SAVE50) - Rodrigo: get 50% off all his books including his all books bundle (code
BF202550) - Mike Driscoll: get 50% off all his Python books and courses (code
BLACKISBACK) - The Python Coding Place: get 50% the all course bundle of 12 courses
- Sundeep Agarwal: ~55% off Sundeep’s all books bundle with code
FestiveOffer - Nodeledge: get 20% off your first payment (code
BF2025) - O'Reilly Media: 40% off the first year with code
CYBERWEEK25($299 instead of $499) - Manning is offering 50% off from Nov 25 to Dec 1
- Wizard Zines: 50% all of Julia Evan’s great zines on various tech topics that I personally find both fun and useful (not Python-related, but one of my favorite annual sales)… this is a one-day Black Friday exclusive sale
I will be keeping an idea on other potential sales and updating this post as I find them. If you’ve seen a sale that I haven’t, please contact me or comment below.
Django-related sales
Adam Johnson has also published a list of Django-related deals for Black Friday (which he’s been doing for a few years now). I’ve included links below to the different sections in Adam’s post…
Courses and books:
- Adam’s books: his books and bundles on Django, Git, and GitHub DX and Django test performance are all 50% off
- LearnDjango.com course bundle: Will Vincent is offering 50% off his 3 courses
- Django 5 By Example: 30% off this book by Antonio Melé
- Django in Action: 50% off this book by Christopher Trudeau
- Async Patterns in Django: 50% off this book by Paul Bailey
Packages related to Django:
- Aidas Bendoraitis: has 3 Django packages available at a 20% discount
- Appliku: 30% of annual plans for this Django deployment tool
- Saas Pegasus: 50% off the unlimited license for this configurable Django project template
Adam also gives a shout out to sponsoring Django itself.
More on my sale: Python Morsels Lifetime Access
Python Morsels is a hands-on, exercise-driven Python skill-building platform designed to help developers write cleaner, more idiomatic Python through real-world practice. This growing library of exercises, videos, and courses is aimed primarily at intermediate and professional developers. If you haven’t used Python Morsels, you can read about it in my sale announcement post.
This Black Friday / Cyber Monday, I’m offering lifetime access: all current and future content for in a single payment. If you’d like to build confidence in your everyday Python skills, consider this sale. It’s about 50% cheaper than paying annually for five years.
Get lifetime access to Python Morsels
November 24, 2025 04:00 PM UTC
Nicola Iarocci
Flask started as an April Fool's joke
The story that the Python micro web framework Flask started as an April Fool’s joke is well known in Python circles, but it was nice to see it told by Armin Ronacher himself1.
I’m fond of Flask. It was a breath of fresh air when it came out, and most of my Python open-source work is based on it.
-
The video is produced by the people who also authored the remarkable Python: The Documentary. ↩︎














Reshama Shaikh
Virgil Chan