The itertools package.
never used it but it seems worth a try
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.
just my 2 ct
What’s wrong about learning things by looking at code from more experienced people?
I would add assert_never to the pattern matching section for exhaustiveness checks: https://typing.python.org/en/latest/guides/unreachable.html#...
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
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.
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.
too many overloads can be a code smell IMHO, it's ok when you are implementing a very common pattern and want proper types (decorators come to mind, so that both @decorator and @decorator() work, where the decorator might also have optional args) but I think in cases like the example in the article it should almost always be two separate functions
When I code, I try to make everything I write not clever. I am not saving a byte here and there because I am not typing in a program from a magazine in the 1980s (I was there, I am not going back). No code golf. I did my time on the Timex-Sinclair with its miserable two kilobytes of memory and reserved keywords as special-function strokes at a character each.
Each line should do one thing, in general, and it ought to be obvious. Cleverness is held in reserve for when it is truly needed, namely data structures and algorithms.
One of my accomplishments which seems to have mattered only to me is when my apartment-finding/renting system, written entirely in Perl, was transferred into the hands of students, new programming students. Perl is famously "write-once, read-never" and seems to have a culture favoring code golf and executable line noise. Still, the students got back to me and told me how easily-ported everything was, because I had done just one thing per line, avoided $_ and other such shortcuts, and other practices. They were very happy to take it over because I had avoided being cryptic and terse.
# setup
yield resource.
# teardown
Not that it makes it any less magical, but at least it's a consistent python patternThe main one is that it makes error handling and clean-up simpler, because you can just wrap the yield with a normal try/catch/finally, whereas to do this with __enter__ and __exit__ you have to work out what to do with the exception information in __exit__, which is easy to get wrong:
https://docs.python.org/3/reference/datamodel.html#object.__...
Suppressing or passing the exception is also mysterious, whereas with contextlib you just raise it as normal.
Another is that it makes managing state more obvious. If data is passed into the context manager, and needs to be saved between __enter__ and __exit__, that ends up in instance variables, whereas with contextlib you just use function parameters and local variables.
Finally, it makes it much easier to use other context managers, which also makes it look more like normal code.
Here's a more real-world-like example in both styles:
https://gist.github.com/tomjnixon/e84c9254ab6d00542a22b7d799...
I think the first is much more obvious.
You can describe it in english as "open a file, then try to write a header, run the code inside the with statement, write a footer, and if anything fails truncate the file and pass the exception to the caller". This maps exactly to the lines in the contextlib version, whereas this logic is all spread out in the other one.
It's also more correct, as the file will be closed if any operation on it fails -- you'd need to add two more try/catch/finally blocks to the second example to make it as good.
def f(i=0) -> None:
j = i + 1
k = 1
reveal_type(i)
reveal_type(j)
reveal_type(k)
Output: Revealed type is "Any"
Revealed type is "Any"
Revealed type is "builtins.int"
def f(i=0) -> None:
reveal_type(i)
The inferred type is not `float` nor `int`, but `Any`. Mypy will happily let you call `f("some string")`.Pyright correctly deduces the type as int.
In any case it's a bad example as function signatures should always be typed.
> # fun x -> x + 1;;
> - : int -> int = <fun>
>
It could be:
def f(i=0) -> None:
if i is None:
do_something()
else:
do_something_else()
Yeah, I know it's retarded. I don't expect high quality code in a code base missing type annotation like that. Assuming `i` is `int` or `float` just makes incrementally adoption of a type checker harder.Pyright probably works if you use it for a new project from the start or invest a lot of time "fixing" an existing project. But it's a totally different tool and it's silly to criticise mypy without understanding its use case.
I tried Pyright but as you say on an existing project you need a looot of time to "fix" it.
You don’t know but you are addicted to types
Come to the light - Haskell!
Or embrace logic + functional programming: Curry. https://curry-language.org/
I needed the array indices to be int64 and specified them as such during initialization.
Downstreams, however, it would look at the actual index values and dynamically cast them to int32 if it judged there would be no loss in precision. This would completely screw up the roundtrip through a module implemented in C.
Being an intermittent bug it was quite a hell.
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.
That breaks down as soon as you need to work with anyone else's code that uses the "fancy stuff".
* 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?
It has an effect, and is usually worth including anyway. I used to omit it by default; now I include it by default. Also, you say "nowadays" but it's been almost 13 years now (https://peps.python.org/pep-0420/).
> since 3.13 there is a @deprecated decorator that does what you think it does
Nice find. Probably worth mentioning it comes from the `warnings` standard library.
> the time package has functions for monotonic clocks and others not just time()
There's quite a bit in there, but I question how many people need it.
Anyway, it's always surprising to me how when other people make these lists, such a large fraction is taken up by tricks with type annotations. I was skeptical of the functionality when the `typing` standard library was introduced; I've only grown more and more wary of it, even as people continue to insist to me that it's somehow necessary.
If you want to help, there's a section on the Python forum (https://discuss.python.org/c/documentation/26) and a Discord server, and issues with documentation can also be reported on the main Python GitHub issue tracker (https://github.com/python/cpython/labels/docs).
If only. I suspect very few Python programmers can even fully explain what `a + b` does.
If `a` and `b` are instances of classes, many would say it's equivalent to `a.__add__(b)` or `type(a).__add__(a, b)`, but in fact it's much more complex.
If a and b are lists, the latter modifies the existing list (which may be referenced elsewhere) instead of creating a new one.
I think Python is the only language I've encountered that uses the + operator with mutable reference semantics like this. It seems like a poor design choice.
I do with the concept of truthiness and falsiness would be taken out and shot though. It's been responsible for far too many nasty bugs IME and it only cuts out a few extra characters. Not a great trade off.
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.
I don't know about code inside companies, but most new open source projects I encounter use typing. Many old ones have been converted too.
> Is duck typing frowned upon?
No. You can use Protocols to define what shape you expect your duck to be. There's some discussion about whether you should use abstract classes or protocols, though.
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
https://docs.python.org/3.12/reference/compound_stmts.html#f...
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!
One I think you missed is getters and setters on attributes!
'''
# ===== 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.
'''
iterable = iter(thing)
while val := next(iterable, None): print(val)
'''
is a lot cleaner in my opinion compared to
'''
iterable = iter(thing)
val = next(iterable, None) while val is not None: print(val) val = next(iterable, None)
'''
Reason why I did not use this example outright was because I wasn't sure if people were familiar with the iter api, so I just chose a simpler example for the blog.
First of all, it takes a minute to search "python :=", and the construct itself is pretty simple. It's been part of the language since 2018[0]. I don't think "not knowing the language" is a good reason to avoid it.
Second, the walrus operator limits the variable's scope to the conditional, which can reduce certain bugs. It also makes some scenarios (like if/elif chains) clearer.
I recommend checking out the PEP for some real-world examples.
Don't use `match`, macros, lifetimes, ... in rust, someone coming from another language without them might not get what it means. Instead write the equivalent C-looking code and don't take advantage of any rust specific things.
Don't use lisp, someone coming from another language might not be able to read it.
Etc..
At one point if you write code and want to be productive, you need to accept that maybe someone that is not familiar with the language you're using _might_ have to look up syntax to understand what's going on.
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+.
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...
TekMol•4h ago
macleginn•4h ago
wesselbindt•4h ago
Loranubi•4h ago
stevesimmons•3h ago
hk__2•3h ago
TekMol•1h ago
zahlman•1h ago
sowhat25•1h ago
paolosimone•1h ago
HelloNurse•36m ago