Zion
Zion

Reputation: 1610

functions and class attributes (python)

def add_age(cls,age):
    cls.yrs_old = age
    return cls


class Test:
    age = add_age

a = Test()
a.age(5)


print(a.yrs_old)

So I was toying around with python and this is something I've never encountered before

here I defined a global function called add_age assigned add_age to the age attribute of Test

what surprises me is that add_age takes 2 arguments cls and age so I tried this

a = Test
a.age(a,5)
print(a.yrs_old)

works as expected however doing this:

a = Test()
a.age(a,5)
print(a.yrs_old)

throws an error TypeError: add_age() takes 2 positional arguments but 3 were given

so then I said well let's try taking out one argument so I did this:

a = Test()
a.age(5)
print(a.yrs_old)

The class itself is automatically passed as the first argument.almost like self and the age attribute now acts as regular class method

which to me as a python newbie is mind boggling

can someone give some insight on what is happening?

Upvotes: 0

Views: 203

Answers (4)

david
david

Reputation: 2638

You are seeing warts of the python language:

1) Objects are automatically passed a "self" parameter, which is hidden in the call but not in the declaration.

2) Static methods of classes can be called against a class definition, even if the class has not been instantiated.

3) When you call a static method of a non-instantiated class, there isn't a "self" object that can be automatically passed to it, and you have to provide an object to fill the first parameter.

4) In the original definition of the language, class definition objects weren't exactly the same as ordinary instantiated objects, and the language couldn't pass a class definition object where a self object was expected.

In the first part of your example, you passed a class object as the first parameter to the class method of the class definition.

In the second part of your example, the language silently passed a self-reference to the first parameter of the object method of the instantiated object, preventing you from using the first parameter slot.

Upvotes: 1

poke
poke

Reputation: 387647

Take a look at this example:

>>> def test (x):
        print(id(x))

>>> class Foo:
        bar = test

>>> a = Foo()
>>> a.bar()
60589840
>>> id(a)
60589840

As you can see, the id printed for x inside test is the same one as the id of the instance a. So what gets passed as the first argument is actually the object instance: self.

Just like when you define methods within a class construct, function members of a class are all methods. It does not matter how you assign them; you can even do that after the type has been created:

>>> def hello (self):
        print('Hello world', id(self))

>>> Foo.hello = hello
>>> a.hello()
Hello world 60589840

So in the end, what you are seeing is just the implicit self being passed to methods.

Upvotes: 1

user3917838
user3917838

Reputation:

Try this.

class Test:
    def __init__(self):
        self.age = add_age

Usually you shouldn't be defining functions at the global level like that.

EDIT: Hmmm, when I copy paste the code, it works perfectly for me...

Upvotes: 1

TigerhawkT3
TigerhawkT3

Reputation: 49318

That is the difference between making a reference to the class (with Test) on one hand, and instantiating an object of the class and making a reference to that object (with Test()) on the other. When you're working with the class, you need to pass two arguments, which you do, and it works. However, when you work with an object of the class, the first formal parameter (formal parameters are the parameters you define, i.e. cls and age) is taken to mean the object itself. It's usually called self for clarity, but you can call it anything you like, such as cls as you've done here.

Upvotes: 1

Related Questions