Zion
Zion

Reputation: 1610

Instance variables and functions

I made a post here functions and class attributes (python)

When you define a class attribute and gave it a function like this:

example 1

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)

self is automatically passed as the first argument of the add_age function.

However toying around with it more doing the same thing but this time passing the function as an instance attribute like this:

example 2

def test_func(self):
    self.class_attribute = "test"



class Test:
    def __init__(self,func):
        self.func = func


a = Test(test_func)

print(a.func())

Answers in the linked post stated that all functions in the class are automatically passed a self if the class is instantiated like this:

a = Test(test_func)

Now what's strange here is had I put test_func in a class attribute it works just like my very first example.

If you pass the function in the constructor/init like this:

def test_func(self):
    self.class_attribute = "test" 

class Test:
    def __init__(self,func):
        self.func = func

and call it like this:

a = Test(test_func)

print(a.func())

a.func is suddenly acting like a static method as opposed to example 1 where the function defined inside the class attribute becomes a regular class method.

What's going on?.

I thought all functions within a class are implicitly passed a self argument.

Upvotes: 2

Views: 1910

Answers (2)

Anand S Kumar
Anand S Kumar

Reputation: 90879

From documentation -

Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok.

This means that only methods that are assigned to classes are instance methods for the instances of the class.

Example -

>>> class A:
...     def a(self):
...             print("Hmm")
...
>>> b = A()
>>> b.a()
Hmm
>>> b.a
<bound method A.a of <__main__.A object at 0x006D13D0>>

But as soon as you assign a separate function object to the instance variable, it is no longer an instance method , since is is not defined at the class level, it is only defined for that particular instance , Example -

>>> def c():
...     print("Hello")
...
>>> b.a = c
>>> b.a()
Hello
>>> b.a
<function c at 0x0017B198>

As you can see, when you directly assigned the function to the instance variable (instead of assigning it to class variable , it is now a normal instance attribute, that references a function object, and not an instance method.

You can also assign functions to class variables after the definition of class , and the instances would automatically get them as instance methods, Example -

>>> class A:
...     def a(self):
...             print("Hmm")
...
>>> def c(a):
...     print("Hello - ", a)
...
>>> b = A()
>>> A.b = c
>>> b.b
<bound method A.c of <__main__.A object at 0x006D13D0>>
>>> b.b()
Hello  <__main__.A object at 0x006D13D0>

Upvotes: 1

chepner
chepner

Reputation: 530922

After the body of the class statement is evaluated, the metaclass wraps each function in a descriptor which takes care of the distinction between instance, class, and static methods. When you assign the function to an instance attribute, you bypass that machinery, so that the attribute refers to a plain function object.

Upvotes: 3

Related Questions