Search code examples
pythonclassfunctioninstantiationself

Class instantiation and 'self' in python


I know a ton has been written on this subject. I cannot, however, absorb much of it. Perhaps because I'm a complete novice teaching myself without the benefit of any training in computer science. Regardless, maybe if some of you big brains chime in on this specific example, you'll help other beginners like me.

So, I've written the following function which works just fine when I call it (as a module?) as it's own file called 'funky.py':

I type the following into my terminal:

python classy.py

and it runs fine.

def load_deck():
    suite = ('Spades', 'Hearts')
    rank = ('2', '3')
    full_deck = {}
    i = 0
    for s in suite:
        for r in rank:
            full_deck[i] = "%s of %s" % (r, s)
            i += 1
    return full_deck

print load_deck()

When I put the same function in a class, however, I get an error.

Here's my code for 'classy.py':

class GAME():
    def load_deck():
        suite = ('Spades', 'Hearts')
        rank = ('2', '3')
        full_deck = {}
        i = 0
        for s in suite:
            for r in rank:
                full_deck[i] = "%s of %s" % (r, s)
                i += 1
        return full_deck
MyGame = GAME()
print MyGame.load_deck()

I get the following error:

Traceback (most recent call last):
File "classy.py", line 15, in <module>
print MyGame.load_deck()
TypeError: load_deck() takes no arguments (1 given)

So, I changed the definition line to the following and it works fine:

def load_deck(self):

What is it about putting a function in a class that demands the use of 'self'. I understand that 'self' is just a convention. So, why is any argument needed at all? Do functions behave differently when they are called from within a class?

Also, and this is almost more important, why does my class work without the benefit of using init ? What would using init do for my class?

Basically, if someone has the time to explain this to me like i'm a 6 year-old, it would help. Thanks in advance for any help.


Solution

  • Defining a function in a class definition invokes some magic that turns it into a method descriptor. When you access foo.method it will automatically create a bound method and pass the object instance as the first parameter. You can avoid this by using the @staticmethod decorator.

    __init__ is simply a method called when your class is created to do optional setup. __new__ is what actually creates the object.

    Here are some examples

    >>> class Foo(object):
        def bar(*args, **kwargs):
            print args, kwargs
    
    >>> foo = Foo()
    >>> foo.bar
    <bound method Foo.bar of <__main__.Foo object at 0x01C9FEB0>>
    >>> Foo.bar
    <unbound method Foo.bar>
    >>> foo.bar()
    (<__main__.Foo object at 0x01C9FEB0>,) {}
    >>> Foo.bar()
    
    Traceback (most recent call last):
      File "<pyshell#29>", line 1, in <module>
        Foo.bar()
    TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
    >>> Foo.bar(foo)
    (<__main__.Foo object at 0x01C9FEB0>,) {}