> Python has done a good job of hiding its legacy ugliness (such as __init__, __new__, and similar aberrations), swettening its syntax to accomodate developers with good taste.
What exactly is the problem with __init__ or __new__? @dataclass is very nice syntactic sugar, but are we arguing here that having access to initializer/allocator/constructor dunder methods is "legacy ugliness"? This is the core of pythonic built-in aware python. Bizarre.
I like that magic method names generally all follow the same form so it's obvious that they are magic methods and not intended to be part of the public API. Whether that form uses double underscores or something else doesn't really matter to me as they are not being called directly.
"Magic methods" is just the name people use for these special methods (another is dunder methods). They're "magic" because you do not call them by name, rather they are invoked by the interpreter in specific situations.
Yes, it is magic the same way other magic methods are, that is, because it is syntactically special and callable (and primarily called by) a syntax other than <instance>.<method>(...) or <class>.<method>(instance, ...)
Specifically, in the case of constructors, via <class>(...).
> it's obvious that they are magic methods and not intended to be part of the public API
Is there an alternative API? No. This is public API regardless of anyone's intentions. Though "it's weird" is really not a very strong argument against it.
Public API refers to the user of the class, not the implementer. You never call __init__ directly. __new__ allows modifying object creation itself for the implementer, but the user of the class will never call it.
There are private and public methods. Private methods are only supposed to be called within other methods, as in privately. Public methods are the ones that are normally called through the code, repl, by the user or whatever. You are not supposed to write `myclass.__add__(x)` anywhere except where you define the class itself and its methods.
Internal is more convention as the language doesn't really do anything with it, but it does with munged, and magic methods are specifically for things implemented in the language.
Internal and munged don't exactly map to private and protected, but are kinda similar ish.
Oh right. I am not really into python, had read some doc about naming conventions some poitns.
In any case I actually like how one can use underscores to point on how exposed some method is supposed to be. Makes it simpler to actually know what to skip and what not.
There are several alternative API's. @dataclass is one, Pydantic offers another, there's also attrs, plus less general things like namedtuple.
Admittedly it's obnoxious when you've got habits for one and you're on a team that uses another--totally flies in the face of the zen re: "there should be only one obvious way to do things".
...but that was always a rather ambitious goal anyway. I'm ok navigating the forest of alternative API's if it means not being locked into something that I can only change by choosing an entirely different language. I'm happy that it's very easy to tell when somebody is mucking about with python internals vs when they're mucking about with some library or other.
That's kind of the point. These methods are never meant to be called directly. They're used to desugar.
I think it's fairly short sighted to criticize these. FWIW, I also did that the first time I wrote Python. Other languages that do similar things provide a useful transparency.
Anyone using a monospacdd font, a font where underscores are separated, or a run-together proportional font that they are even a little bit familiar with, because of the relative width of underscores and other characters.
Which, between them, covers approximately everyone.
I mean, maybe you should use a font that doesn't make it hard to figure that out. Next you're gonna say we should ban 0s and Os, Is and ls, because your font doesn't allow you to figure out which one it is.
...by being as shockingly contrasting with their environment as possible. Maybe your aesthetics differ, but I find it quite ugly. If it wasn't, it wouldn't grab my attention so well. Wearing something shockingly ugly is a great way to not be mistaken for a deer.
By contrast, I find a well camouflaged deer quite beautiful--once I notice it at all. The beauty comes from the way that it is so clearly a thing of its surroundings. Nothing at all like a bright orange hat.
> Wearing something shockingly ugly is a great way to not be mistaken for a deer.
Sure... yes the bright orange is ugly, but it's not the ugliness that prevents you from getting shot, it's the bright unnatural color. Other hunters aren't thinking "oh that's really ugly, it must not be something I can shoot" they're thinking "bright orange means person, I should not fire my rifle in that direction".
> If it wasn't, it wouldn't grab my attention so well.
Are you saying that if you thought the bright orange was pretty it wouldn't occur to you not to fire your gun in its direction?
If I thought that bright orange was a part of a healthy forest ecosystem, I would likely see beauty in it. And if my intent was to shoot denizens of such an ecosystem, then yeah bright orange would make a poor indicator for "don't shoot here". You'd be better off with something ugly, something which clearly doesn't belong.
Amazing. If you need to ask this question I am guessing you have spent too long with Python . No other language uses this underscore madness for special methods or otherwise. Because it’s just , I don’t know a more appropriate word for it , stupid.
What's actually amazing is how you're unable to answer a straight question.
If you call something "stupid" it doesn't really convey anything meaningful, especially not in the way you're using there, it comes across as "I don't actually have a reason I don't like it, I just don't".
The programming languages world is broad/varied enough that any statement like "no other language does this!" is almost certainly wrong (outside of esoteric languages, which python and php most certainly are not)
You don't call __init__ directly so the ugliness is limited to the definition which seems fairly minimal to me. It seems to be a naming convention for methods that are not invoked by name anywhere and that's at least informative when you're reading the class definition.
IMO this is less horrendous than e.g. go's insistence that exported functions are indicated by a capital letter - that really affects code using the module not just the definition.
I have found to believe that ugly and beautiful are irrelevant. As long as it's doing the job and not cause major headaches. Life is too short to worry about syntax.
I had honestly not considered that the author might have been talking about the visual aesthetic of the dunder methods themselves. Honestly that has me a bit stumped; all I can say is that if you don't like underscores, don't pick python.
I would think the dumber thing is having to create an empty file called __init__.py for a package to register as a package. And relative imports being completely fubar.
> The __init__.py files are required to make Python treat directories containing the file as packages (unless using a namespace package, a relatively advanced feature).
I wrote it in about an hour and a half. It seems to work in Python 3.6 and 3.12. Now you never need to write another double-underscore magic method again.
As if other language don't have their warts.. Ruby and Javascript for example have hideous syntax, comparing this with some special naming is quite bizarre.
> What exactly is the problem with __init__ or __new__? @dataclass is very nice syntactic sugar, but are we arguing here that having access to initializer/allocator/constructor dunder methods is "legacy ugliness"?
My read was that the "ugliness" was in the method naming, and specifically the double underscores, not the availability of the methods themselves.
Probably the system of giving special meaning to what are otherwise ordinary identifiers. __ isn't a reserved keyword in Python or anything. But there's a set of conventions you're supposed to follow when naming methods and attributes, and they're visibly artificial. It would be cleaner to make the features that are a special part of the language definition also a special part of the language syntax instead of running them in indistinguishably with other nonsimilar things.
In C++, if you want to define the binary + operator on a class, you give the class a method with the special name `operator+`. In Python, to do the same thing, you give the class a method with the pseudo-special name `__add__`. You don't think the Python way is worse?
Have you considered how much familiarity might shape your reaction to the two? Both are specific patterns with arbitrary restrictions which new learners have to internalize, and that’s a fairly advanced task most people won’t hit until they are familiar with the language syntax.
Here’s a counter argument: the Python methods are normal method declarations whereas C++ developers have to learn that “operator=“ means something different than what “operator =“ (or any other assignment statement) would mean, and that this is the only context where you can use those reserved symbols in method names.
To be clear, I don’t think either of these is a big deal - the concepts and usage are harder to learn than the declaration syntax – but I think it’s incredibly easy to conflate familiarity with ease of acquisition for things like this.
> Have you considered how much familiarity might shape your reaction to the two?
It doesn't.
> Both are specific patterns with arbitrary restrictions which new learners have to internalize, and that’s a fairly advanced task most people won’t hit until they are familiar with the language syntax.
No, the Python methods are just ordinary methods with valid names. What 'arbitrary restrictions' are you referring to?
The arbitrary restriction is that you have to just learn a specific pattern and follow it. I can’t have my Python class use “addition” if I hate underscores or my C++ code use “mathematical_division” if I’m horribly pedantic. In both cases, it’s just a particular design decision by the language developers I have to learn, and in both cases I won’t think much of it after the initial acclimation.
Funnily enough, just starting your class method/variable name with __ does do magic things as a pseudo-keyword. Specifically, it mangles the name for callers outside of that class -- `self.__foo` would need to be accessed as `obj._ClassName__foo`. It's python's approach to having private methods, while remaining somewhat ideologically opposed to them existing.
This doesn't apply to the dunder methods, though. They're magically exempt from this magical mangling, so you could call them directly if you wanted. ¯\_(ツ)_/¯
> You don't think the Python way is worse?
They seem about equivalent? I don't see any real reason to pick one or the other, beyond personal preferences.
It prevents accidentally overriding the member in subclasses which can prevent private implementation details of parent classes from being accidentally interfered with by child classes.
As composition over inheritance becomes more idiomatic, there are probably less places where this matters, but as long as inheritance is used at all it can be useful.
It makes it nearly impossible to reference that attribute by accident. Basically, if class Foo defines "self.__bar", the Python interpreter sees the leading "__" and mangles it. It's the lightweight equivalent of a private attribute in other languages, with the difference that Python will let you attempt to access it anyway. But if you do, and it makes your code utterly brittle and prone to explosion, no one will give you sympathy or help you fix it. They're much more likely to tell you not to do that.
It's as close as they'll get to private methods while still having the "we're all consenting adults" philosophy that doesn't prevent you doing something dumb. Or if you use this and it breaks, you can't say you weren't warned.
What problem is that addressing? It doesn't solve the problem that `__add__` is a valid identifier -- so is `add` -- and it also doesn't solve the problem that there is no connection between the symbol `+` and the name `add`.
I've been doing Python professionally for a while (~10-ish years in backend web dev and file processing), and while I don't like how exactly the author described dunder methods as "legacy", it's still not a language facility I reach out to often because, well, it's sort of odd coming from a C# background. The author has blog posts about Java, which isn't that different from C#, so maybe that's the reason.
Maybe as I grow to think of the "big picture" architecture-wise with my code, I will start incorporating dunders, but until then...
What exactly is the problem with __init__ or __new__? @dataclass is very nice syntactic sugar, but are we arguing here that having access to initializer/allocator/constructor dunder methods is "legacy ugliness"? This is the core of pythonic built-in aware python. Bizarre.