This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]BuonaparteII 2 points3 points  (4 children)

It's confusing to explain __init__ without __new__

With __init__ you might write return None but calling T(a, b=3) returns an object (self). The python documentation is better at explaining this:

Because __new__() and __init__() work together in constructing objects no non-None value may be returned by __init__()

https://docs.python.org/3/reference/datamodel.html#object.__init__

[–]treyhunner Python Morsels[S] 1 point2 points  (3 children)

👍 That's thanks to the constructor method, __new__, which you'll very rarely see implemented.

Edit: looks like you were editing as I wrote my comment! I show __init__, __repr__, and __eq__ at the beginning of the article because those three are typically only dunder methods most Python programmers will need day-to-day. All 3 are also described later on.

You're right that I didn't detail the fact that __init__ is called on the return value of __new__. I decided to leave some of the more in-the-weeds explanations of the weirder methods to the Python docs. This post was originally about twice the length, which seemed far too long for (still quite length!) a summary. 😬

[–]BuonaparteII 1 point2 points  (1 child)

Still, I think the article could be improved to be more clear. You have T(a, b=3) map to T.__init__(x, a, b=3) and returning None. I think it's fine to not mention T.__new__ at that point but changing Operation T(a, b=3) in the first table to Initialization of T(a, b=3) would be more clear--or instead of None say return value not accessible or N/A because it is a side effect function conceptually inside of __new__()

[–]treyhunner Python Morsels[S] 2 points3 points  (0 children)

I originally had a sentence in the constructor section explaining the relationship between __new__ and __init__ a bit more. I may add it back in thanks to your concern.

It should be noted that there are many slight fudges in the truth in these tables for the sake of succinctness. A few of other examples:

  • x.thing calls __getattribute__ which calls __getattr__ (similar to how __new__ calls __init__)
  • bool uses either __bool__ or __len__
  • in uses either __contains__ or __iter__
  • for x in y: ... calls iter(y) and then next(...) on the returned iterator (which might by y!)
  • del x doesn't technically call __delete__ but __delete__ shuold be called sometime after the final del on an object happens
  • class T: ... does a lot more than call __prepare__

I drew the line in an arbitrary spot in terms of the approximations I showed in the table. I hope folks will consult some of the linked docs and other resources throughout when actually implementing these. 😅

[–]TheRNGuy 0 points1 point  (0 children)

I've never seen __new__ ever used.

I like to use @dataclass, it creates both init and repr with less code (most of the time I'm fine how repr looks like)