by BerislavLopac on 4/23/25, 7:21 AM with 176 comments
by edwardjxli on 4/23/25, 12:47 PM
I did not expect to wake up at 4am seeing my post on front page HN, but here we are nevertheless :D
As the intro mentioned, these started off as 14 small tweets I wrote a month prior to starting my blog. When I finally got that set up, I just thought, "hey, I just spent the better part of two weeks writing these nifty Python tricks, might as well reuse them as a fun first post!"
That's why the flow might seem a little weird (as some pointed out, proxy properties are not really a Python "feature" in of itself). They were just whatever I found cool that day. I tried to find something more esoteric if it was a Friday, and something useful if it was a Monday. I was also kinda improving the entire series as it was going on, so that was also a factor.
Same goes with the title. These were just 14 feature I found interesting while writing Python both professionally and as a hobby. Some people mentioned these are not very "advanced" per se, and fair enough. I think I spent a total of 5 second thinking of a title. Oh well!
by robviren on 4/23/25, 11:28 AM
by tzury on 4/23/25, 1:04 PM
I am coding in all 4, with some roughly 28 years now, and I don't like what is becoming of Python
There is a reason why python become that popular and widely adapted and used, and it is not the extra layers of type checking, annotations and the likes.
this looks familiar to me, but from other languages.
response := get_user_input()
I am aware of the factI am in minority, and not trying to change anyone's mind, simply what this voice to be heard from time to time.All in all, a very comprehensive list of some of the recent introduced features.
There is an older list on SO which readers might also find useful:
https://stackoverflow.com/questions/101268/hidden-features-o...
by lyu07282 on 4/23/25, 10:31 AM
* did you know __init__.py is optional nowadays?
* you can do relative imports with things like "from ..other import foo"
* since 3.13 there is a @deprecated decorator that does what you think it does
* the new generics syntax also works on methods/functions: "def method[T](...)" very cool
* you can type kwargs with typeddicts and unpack: "def fn(*kwargs: Unpack[MyKwargs])"
* dataclasses (and pydantic) support immutable objects with: "class MyModel(BaseModel, frozen=True)" or "@dataclass(frozen=True)"
* class attributes on dataclasses, etc. can be defined with "MY_STATIC: ClassVar[int] = 42" this also supports abstract base classes (ABC)
* TypeVar supports binding to enforce subtypes: "TypeVar['T', bound=X]", and also a default since 3.13: "TypeVar['T', bound=X, default=int]"
* @overload is especially useful for get() methods to express that the return can't be none if the default isn't None
* instead of Union[a, b] or Optional[a] you can write "a | b" or "a | None" nowadays
* with match you can use assert_never() to ensure exhaustive matching in a "case _:" block
* typing has reveal_type() which lets mypy print the type it thinks something is
* typing's "Self" allows you to more properly annotate class method return types
* the time package has functions for monotonic clocks and others not just time()
anyone know more things?
by globular-toast on 4/23/25, 9:18 AM
If you are really interested in "advanced Python", though, I would recommend the book Fluent Python by Ramalho. I have the first edition which is still highly relevant, including the async bits (you just have to translate the coroutines into async syntax). There is a second edition which is more up to date.
I would also recommend checking out the functools[0] and itertools[1] modules in the standard library. Just go and read the docs on them top to bottom.
It's also worth reading the first few sections of Python Data Model[2] and then bookmarking this page.
[0] https://docs.python.org/3/library/functools.html
by lordnacho on 4/23/25, 10:15 AM
The more fancy stuff you add to it, the less attractive it becomes. Sure, most of these things have some sort of use, but I reckon most people do not get deep enough into python to understand all these little things.
by addoo on 4/23/25, 2:44 PM
Several comments disliking the walrus operator, like many of the features on this list I also hated it… until I found a good use for it. I almost exclusively write strictly typed Python these days (annotations… another feature I originally hated). The walrus operator makes code so much cleaner when you’re dealing with Optionals (or, a Union with None). This comes up a lot with regex patterns:
if (match := pattern.search(line)) is not None:
print(match.group())
Could you evaluate match on a separate line before the conditional? Sure. But I find this is a little clearer that the intended life of match is within the conditional, making it less tempting to reuse it elsewhere.Not a Python feature specifically, but I’d also love to see code that uses regex patterns to embrace named capturing groups more often. .group(“prefix”) is a lot more readable than .group(1).
by hiichbindermax on 4/23/25, 10:30 AM
by Loranubi on 4/23/25, 8:58 AM
Metaclasses are however quite complex (or at least lead to complex behavior) and I mostly avoid them for this reason.
And 'Proxy Properties' are not really a feature at all. Just a specific usage of dunder methods.
by lucideer on 4/23/25, 1:55 PM
1. Typing overloads: TS has typed overloads I think largely as an affordance to an unfortunate feature of Javascript. In my experience overloads are an anti-pattern or at best code smell. It's nice that you can type them if you're cleaning up an existing codebase that uses them but I would consider them tech debt.
2. Keyword-only and Positional-only Arguments: This is the opposite of the 1st feature (ability to make method signatures more strict) but man is the syntax cryptically terse. I'd love to use this everywhere but I'd be concerned about readability.
3. Future Annotations: Thank you for this section - forward references have been a real pain for me recently & this is the first explanation that's scratches the service of the "why" (rather than just focusing on case-by-case solutions), which is much more helpful. Bring on PEP 649.
4. Generics: Cries in legacy 3.10 codebase
5. Protocols: As a Typescript guy, this seems very cosy & familiar. And not very Pythonic. I'm not sure how to feel.
14. Metaclasses:
> if you are that 1% which has a unique enough problem that only metaclasses can solve, they are a powerful tool that lets you tinker with the internals of the Python object system.
OR if you're one of the many devs that believes their problem is unique & special & loves to apply magical overengineered solutions to simple common problems, then the next person who inherits your code is going to really love your metaclasses. They sure make tracing codepaths fun.
by djoldman on 4/23/25, 12:18 PM
https://blog.edward-li.com/tech/advanced-python-features/#2-...
def bar(a, /, b):
...
# == ALLOWED ==
bar(1, 2) # All positional
bar(1, b=2) # Half positional, half keyword
# == NOT ALLOWED ==
bar(a=1, b=2) # Cannot use keyword for positional-only parameter
by TekMol on 4/23/25, 8:34 AM
for server in servers:
if server.check_availability():
primary_server = server
break
else:
primary_server = backup_server
deploy_application(primary_server)
As it is shorter to do this: primary_server = backup_server
for server in servers:
if server.check_availability():
primary_server = server
break
deploy_application(primary_server)
by William_BB on 4/23/25, 12:13 PM
by davnn on 4/23/25, 9:57 AM
by steinuil on 4/23/25, 11:44 AM
I think this list should also include descriptors[0]: it's another metaprogramming feature that allows running code when accessing or setting class attributes similar to @property but more powerful. (edit: nvm, I saw that they are covered in the proxy properties section!)
I think the type system is quite good actually, even if you end up having to sidestep it when doing this kind of meta-programming. The errors I do get are generally the library's fault (old versions of SQLAlchemy make it impossible to assign types anywhere...) and there's a few gotchas (like mutable collections being invariant, so if you take a list as an argument you may have to type it as `Sequence[]` or you'll get type errors) but it's functional and makes the language usable for me.
I stopped using Ruby because upstream would not commit on type checking (yes I know you have a few choices if you want typing, but they're a bit too much overhead for what I usually use Ruby for, which is writing scripts), and I'm glad Python is committing here.
by pjmlp on 4/23/25, 8:57 AM
The itertools package.
by krelian on 4/23/25, 10:47 AM
by stitched2gethr on 4/23/25, 1:40 PM
I'll be honest, I've never understood this language feature (it exists in several languages). Can someone honestly help me understand? When is a function with many potential signatures more clear than just having separate function names?
by wismwasm on 4/23/25, 9:18 AM
I would add assert_never to the pattern matching section for exhaustiveness checks: https://typing.python.org/en/latest/guides/unreachable.html#...
by macleginn on 4/23/25, 8:40 AM
by smjburton on 4/23/25, 12:47 PM
'''
# ===== Don't write this =====
response = get_user_input()
if response:
print('You pressed:', response)
else: print('You pressed nothing')
# ===== Write this instead =====if response := get_user_input():
print('You pressed:', response)
else: print('You pressed nothing')
'''The first implementation is immediately clear, even if you're not familiar with Python syntax. If you don't know what the ":=" operator does, the code becomes less readable, and code clarity is traded away in favor of being slightly more concise.
by hk1337 on 4/23/25, 4:50 PM
by mcdeltat on 4/23/25, 12:57 PM
Although I think the example of type alises in section 4 is not quite right. NewType creates a new "subtype" which is not equivalent to the original type. That's different to TypeAlias, which simply assigns a name to an existing type. Hence NewType is still useful in Python 3.12+.
by mvATM99 on 4/23/25, 9:33 AM
One personal favorite of mine is __all__ for use in __init__.py files. It specifies which items are imported whenever uses from x import *. Especially useful when you have other people working on your codebase with the tendency to always import everything, which is rarely a good idea.
by librasteve on 4/24/25, 6:51 AM
- multi subs & methods = "typing overloads"
- named & positional args
- stubs = "future annotations"
- subsets = "Generics"
- protocols
- wrappers = "context managers"
- given / when = "structural pattern matching"
- my declaration = "walrus op"
- short circuit evaluation
- operator chaining
- fmt
- concurrency
If you are a Python coder and you feel the need for some of this, I humbly suggest you take a look at https://raku.orgby nurettin on 4/23/25, 3:12 PM
by OutOfHere on 4/25/25, 3:48 AM
by red_admiral on 4/23/25, 9:34 AM
Playing with the typesystem and generics like this makes me worry I'm about to have a panic attack.
Give me code that I can understand and debug easily, even when I didn't write it; don't do implicit magical control flow changes unless you have a very good excuse, and then document both the how and the why - and you'll get a product that launches earlier and has fewer bugs.
Sometimes, a few more if statements here and there make code that is easier to understand, even if there's a clever hack that could cut a line or two here and there.
by fxj on 4/23/25, 9:12 AM
just my 2 ct
by Starlevel004 on 4/23/25, 9:16 AM
by hrtk on 4/23/25, 10:24 AM
by shaunpud on 4/23/25, 10:50 AM
by drumnerd on 4/23/25, 10:07 AM