Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: must __init__(self, foo) always be followed by self.foo = foo?

Tags:

python

self

init

I've been striving mightily for three days to wrap my head around __init__ and "self", starting at Learn Python the Hard Way exercise 42, and moving on to read parts of the Python documentation, Alan Gauld's chapter on Object-Oriented Programming, Stack threads like this one on "self", and this one, and frankly, I'm getting ready to hit myself in the face with a brick until I pass out.

That being said, I've noticed a really common convention in initial __init__ definitions, which is to follow up with (self, foo) and then immediately declare, within that definition, that self.foo = foo.

From LPTHW, ex42:

class Game(object): 
    def __init__(self, start): 
        self.quips = ["a list", "of phrases", "here"]
        self.start = start

From Alan Gauld:

def __init__(self,val): self.val = val

I'm in that horrible space where I can see that there's just One Big Thing I'm not getting, and I it's remaining opaque no matter how much I read about it and try to figure it out. Maybe if somebody can explain this little bit of consistency to me, the light will turn on. Is this because we need to say that "foo," the variable, will always be equal to the (foo) parameter, which is itself contained in the "self" parameter that's automatically assigned to the def it's attached to?

like image 516
JeanSibelius Avatar asked Mar 16 '12 00:03

JeanSibelius


2 Answers

You might want to study up on object-oriented programming.

Loosely speaking, when you say

class Game(object):
    def __init__(self, start):
        self.start = start

you're saying:

  • I have a type of "thing" named Game

  • Whenever a new Game is created, it will demand me for some extra piece of information, start. (This is because the Game's initializer, named __init__, asks for this information.)

  • The initializer (also referred to as the "constructor", although that's a slight misnomer) needs to know which object (which was created just a moment ago) it's initializing. That's the first parameter -- which is usually called self by convention (but which you could call anything else...).

  • The game probably needs to remember what the start I gave it was. So it stores this information "inside" itself, by creating an instance variable also named start (nothing special, it's just whatever name you want), and assigning the value of the start parameter to the start variable.

    • If it doesn't store the value of the parameter, it won't have that informatoin available for later use.

Hope this explains what's happening.

like image 128
user541686 Avatar answered Oct 23 '22 10:10

user541686


I'm not quite sure what you're missing, so let me hit some basic items.

There are two "special" intialization names in a Python class object, one that is relatively rare for users to worry about, called __new__, and one that is much more usual, called __init__.

When you invoke a class-object constructor, e.g. (based on your example) x = Game(args), this first calls Game.__new__ to obtain memory in which to hold the object, and then Game.__init__ to fill in that memory. Most of the time, you can allow the underlying object.__new__ to allocate the memory, and you just need to fill it in. (You can use your own allocator for special weird rare cases like objects that never change and may share identities, the way ordinary integers do for instance. It's also for "metaclasses" that do weird stuff. But that's all a topic for much later.)

Your Game.__init__ function is called with "all the arguments to the constructor" plus one stashed in the front, which is the memory allocated for that object itself. (For "ordinary" objects that's mostly a dictionary of "attributes", plus the magic glue for classes, but for objects with __slots__ the attributes dictionary is omitted.) Naming that first argument self is just a convention—but don't violate it, people will hate you if you do. :-)

There's nothing that requires you to save all the arguments to the constructor. You can set any or all instance attributes you like:

class Weird(object):
    def __init__(self, required_arg1, required_arg2, optional_arg3 = 'spam'):
        self.irrelevant = False
    def __str__(self):
        ...

The thing is that a Weird() instance is pretty useless after initialization, because you're required to pass two arguments that are simply thrown away, and given a third optional argument that is also thrown away:

x = Weird(42, 0.0, 'maybe')

The only point in requiring those thrown-away arguments is for future expansion, as it were (you might have these unused fields during early development). So if you're not immediately using and/or saving arguments to __init__, something is definitely weird in Weird.

Incidentally, the only reason for using (object) in the class definition is to indicate to Python 2.x that this is a "new-style" class (as distinguished from very-old-Python "instance only" classes). But it's generally best to use it—it makes what I said above about object.__new__ true, for instance :-) —until Python 3, where the old-style stuff is gone entirely.

like image 41
torek Avatar answered Oct 23 '22 10:10

torek