The right way to 'call' a class attribute inside the same class

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Dec 13 00:45:41 EST 2016


On Tuesday 13 December 2016 10:23, Chris Angelico wrote:

> On Tue, Dec 13, 2016 at 10:17 AM, Ben Finney <ben+python at benfinney.id.au>
> wrote:
>> If the differences didn't matter I would agree that “overly pedantic” is
>> fair. But those differences trip up newcomers. Thinking of
>> ‘Foo.__init__’ leads people to wonder where the ‘self’ attribute came
>> from – am I not meant to be constructing it? — and to attempt to return
>> that instance. And when the time comes to lean about ‘__new__’ the
>> confusion continues, because the newcomer has been told that something
>> *else* is the constructor, so what's this?
> 
> In JavaScript, it's normal to talk about "calling a function as a
> constructor". When you do, there is a 'this' object before you start.
> Should we tell the JavaScript folks to be more pedantic, because
> 'this' should end up existing? 

You've ignored Ben's critical point that if we call __init__ the "constructor", 
what do we call __new__? (Perhaps we should call __new__ the "initialiser" for 
maximum confusion.)

I don't understand the point of bringing up Javascript. Ben has already said 
that we shouldn't feel the need to mindlessly copy C++ terminology. Is it your 
position that we *should* copy Javascript terminology? Why Javascript and not 
C++ or ObjectiveC? Even when it goes against the obvious English mnemonic?

    __init__ is the INITialiser (it initialises an existing instance);

    __new__ creates/constructs a NEW instance


> Does it really even matter when memory
> gets allocated and the object's identity assigned? 

Actually, yes it does. Try constructing an immutable object like a subclass of 
float, str or int from the __init__ method. Attaching attributes to the 
instance doesn't count.


class PositiveInt(int):
    def __init__(self, arg):
        arg = abs(arg)
        return super().__init__(self, arg)


So much fail...



> Before __init__
> gets called, the object isn't "truly there" - its fundamental
> invariants may not yet have been established, and key attributes might
> not have been set up.

"Fundamental invariants" is tricky though -- are they *truly* fundamental? This 
is Python -- I can easily make a Python-based class that lacks the attributes 
that its methods assume will be there, or delete them after creation.

But there is one thing which truly is fundamental: the instance creation, the 
moment that object.__new__ returns a new instance. Before calling that, the 
instance genuinely doesn't exist; after object.__new__ returns, it genuinely 
does. (Even if it isn't fully initialised.)

Unlike __init__, object.__new__ is atomic: it either succeeds, or it doesn't. 
So object.__new__ really is the constructor of instances, and so following 
standard practice, MyClass.__new__ which inherits from object ought to be 
called the same thing. (Even if it is no longer atomic, due to being written in 
Python.)


> Once __init__ finishes, there is an expectation
> that attributes and invariants are sorted out.
> 
> This is like the old "Python doesn't have variables" thing.
> Ultimately, every language has slightly different semantics (otherwise
> they'd be trivial transformations, like Ook and Brainf*), so you have
> to learn that the "constructor" might have slightly different
> semantics. Accept it - embrace it. Learn it.

We're not debating what other languages should call __new__ and __init__ or 
whatever their equivalents are. We're debating what Python should call them.

For some prior art, consider ObjectiveC. To create a new instance of a class, 
you call:

[[MyClass alloc] init]

where alloc is the "allocator" (it basically just allocates memory, and very 
little else) and init is the "initialiser" (because it initialises the newly 
created instance.

In more modern versions of ObjectiveC, there's a short-cut:

[MyClass new]

where new calls alloc then init for you.

In Ruby, people think of two distinct things as the "constructor", depending on 
what you are doing. [Source: my resident Ruby expert at work.] If you are 
talking about *writing* a class, the constructor is the initialize method:

class MyClass
    def initialize
        ...
    end
end

But if you are talking about *creating an instance* it is the new method:

MyClass.new

which itself automatically calls initialize behind the scenes. (I'm told that 
it is possible to override new, but nobody does it.)


It seems to me that both of these are quite similar to Python's model.



-- 
Steven
"Ever since I learned about confirmation bias, I've been seeing 
it everywhere." - Jon Ronson



More information about the Python-list mailing list