Every Dunder Method in Python
pythonmorsels.com
pythonmorsels.com
Unfortunately in almost every case, dunder methods made it harder for me to collaborate with other people on the same codebase. And I get it. Tinkering with `__setattr__` leads to recursion errors if you're not careful, `__call__` introduces state in an unexpected place (wait, foo is a class instance? But but it behaves just like a function...). It's one of the only "stupid Python tricks" I can think of that lacks a clear analog in other programming languages, so polyglots without a strong Python background tend to hate them. I've tried to make the case on two separate occasions that dunder methods represent a more object-oriented approach to dealing with systemic complexity than type dispatch, and I stand by this. But I concede that the benefits of making the codebase nicer and more ergonomic in some places invariably requires writing class definitions that look horrendous in other places. So it's not so much that dunder methods reduce that complexity, so much as try to contain it and hopefully prevent it from spilling out into the main execution logic.
P.S. - Trey, if you're reading this, hello from a former TA!
Hi former TA! It's been a long time.
> Unfortunately in almost every case, dunder methods made it harder for me to collaborate with other people on the same codebase.
I see dunder methods as methods that should usually (with the exception of __init__, __repr__, and __eq__) be used much more often than they're read.
I typically use dunder methods when implementing a library or framework that's meant to be used by others who would be looking at its documentation but not necessarily its code.
For example the dramatic library I published over the weekend relies on __enter__, __exit__, and __del__ (which is a very uncommon one typically): https://pypi.org/project/dramatic/
Users of that code shouldn't need to look at the code.
For code that's modified by newer Python users often, using dunder methods might be more difficult to justify. Though you might be able to get away with making a library which that could could use.
Knowing that made hem less scary
On the other hand... https://en.wikipedia.org/wiki/Dunder
> The frequent use of a double underscores in internal identifiers in Python gave rise to the abbreviation dunder; this was coined by Mark Jackson[3] and independently by Tim Hochberg,[4] within minutes of each other, both in reply to the same question in 2002.[5][6]
I always thought it was some sort of reference to the structure of the package or something lol. I like it though!
A better way to think about it is that the double-underscore naming convention simply means "reserved by the core Python team" -- they offer ways to hook into widely-implemented protocols in the Python runtime.
That also means you should never invent your own dunder methods or dunder attributes. This is another mistake new Python programmers -- and even some experienced Python programmers, even some Python framework writers! -- tend to make, "imitating" this naming convention to mark parts of their own code as "magic." Don't do that: the whole idea is that the core Python team reserved this namespace (`__*__`) for Python-wide protocols.
[1]: https://amontalenti.com/2013/04/11/python-double-under-doubl...
Blocks are what I miss most when moving to a Python project from Ruby, and context managers aren't some sort of replacement for them, but sometimes it is nice to construct your own context manager with custom setup/teardown logic.
See also @contextlib.contextmanager [1], which makes simple cases less verbose.
[1]: https://docs.python.org/3/library/contextlib.html#contextlib...
I don't think this is good advice. Example: the dunder methods implemented and used by the Scientific Python/PyData community (__array__, __array_ufunc__, __array_func__, etc.) are so, so, so important to that ecosystem.
https://docs.python.org/3/reference/lexical_analysis.html#re...
This has been in the specification since, at latest, Python 2.0, and perhaps earlier.
Any you see defined outside the Python data model are entirely historical, written in contradiction to the spec. Many of them also can't be changed because there is so much code in the wild that depends on them.
*Do not define your own dunder methods!*
But in general, Joe Random Developer's library is not that important to justify nibbling away at a function namespace that has no mechanism for keeping two libraries from choosing the same name with different semantic meaning.
Such declarations MUST be preceded by the comment:
# I DECLAREIf not, dunder methods do not seem appropriate.
> System-defined names, informally known as “dunder” names. These names are defined by the interpreter and its implementation (including the standard library). Current system names are discussed in the Special method names section and elsewhere. More will likely be defined in future versions of Python. Any use of __*__ names, in any context, that does not follow explicitly documented use, is subject to breakage without warning.
And their SEO is terrible, I've started to preface all python searches with 'site:python.org' to not end up on crappy monetization blogs..m
That's a problem with your tools, not the Python docs. Use a better search engine.
You might find DevDocs to be useful: https://devdocs.io/
If you use DuckDuckGo as your primary search engine, you can use `!py` for the Python 2 docs, and `!py3` for Python 3.
More importantly, in that custom search, it's behind the datetime module (which "models" some time-related behavior), the ssl module (which has a multiplexing "model" for wrapping socket "objects"), and many other results that are just stdlib modules with no consideration for the fact that the query doesn't seem to be asking about specific APIs or modules.
It's not a great search phrase, and writing a proper search engine is hard, but when I use other tools (e.g., binding !py in qutebrowser's search customization to a ddg search with the site:docs.python.org restriction) I don't have to spend any time crafting a perfect query and still get exactly the results I want.
I think we have bit of a miscommunication here.
I'm well aware of the advanced capabilities of various search engines - in fact, Google's removal of some of those around the time Google+ came out is what caused me to start looking for other search engines.
Anyhow, while DuckDuckGo supports the `site:<url>` syntax, bangs are different, as they take you to the third-party site's search itself instead of providing a list of results on DDG.
Here's the list of all supported bang commands: https://duckduckgo.com/bangs
I was noting that 1st-party search is often lacking, and you can build that same bang-syntax idea into your browser as a configuration option. In QuteBrowser it's literally a bang, and in other options usually it's triggered by a space after the identifier.
In your browser's settings, you can set the shortcut for this search engine as "@python"
Now whenever you're searching, just start typing @python, hit tab to complete, and then type your search term.
I use this all the time in Firefox, and I'm 90% sure it works in Chrome, too.
It would be more natural to declare the algebraic structure of one or more related classes.
For example, this is something you would do to override addition:
class C:
...
def __add__(self, other: C):
...
The way that would feel more natural to me is something like: class C:
...
algebraic_structure Monoid<C>:
def +(fst: C, snd: C) -> C:
...
Which would also work for more complicated structure, for example: class Datetime:
...
class Timedelta:
...
algebraic_structure AffineSpace<Datetime, Timedelta>:
def point_minus(fst: Datetime, snd: Datetime) -> Timedelta:
...
def vector_add(fst: Timedelta, snd: Timedelta) -> Timedelta:
...
def vector_inverse(vec: Timedelta) -> Timedelta:
...
Where you could automatically "deduce" unspecified methods.___tunder___
A simple _wondur method is probably a bit more common though.
https://www.youtube.com/watch?v=VMj-3S1tku0
Excellent intro video btw
But it can cause problems. For example, the Mypy type checker refuses to acknowledge that this is possible: https://github.com/python/mypy/issues/1020
To me, it's a dirty blue collar language that gets work done.
I think Ruby is far ahead of Python in elegance. I often have to use 7 Python lines for what Ruby can do in 3.
I'm open to being told I'm wrong :)
It's by far not a perfect language, but you have to appreciate how far it goes to avoid special cases.
Python painted itself into a corner when they came up with a clever way to auto generate __init__ implementations but then also sometimes you need to modify the auto generated implementation. I kind of like their solution relative to alternatives I've seen... C++ is the worst offender to my mind in this case, because it has extremely complicated auto generation rules and the moment you want to do something special, you are now writing two dozen lines of code to make up for the fact that you got in the way of the auto-gen logic.