Reputation: 1610
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
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
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
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
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