2

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.

Community
  • 1
  • 1
Zion
  • 1,570
  • 6
  • 24
  • 36

2 Answers2

3

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.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • just like a `@staticmethod` right?. Im fairly new to python just a couple of months. so i'll have to look up **metaclass** and **descriptors** thanks for you input – Zion Aug 19 '15 at 18:16
  • 1
    @Zion Take a look at [this question](http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python) to get started with Python metaclasses. – That1Guy Aug 19 '15 at 19:20
  • @That1Guy funny enough I did just that before you recommended it. that 3000+ upvoted answer was pretty sweet. it made me understand it a bit. bit I can't figure out how to implement the metaclass. I literally tried copy pasting the code just to see how it works but. it doesn't work. I copy/pasted the example he gave with turning all your attribute names uppercase but it doesn't work on my `IDLE` – Zion Aug 19 '15 at 19:36
  • 2
    @Zion I wouldn't worry about metaclasses for a long time. In my experience, only about 1% of the metclasses that are written are needed. And only 1% of those are done correctly anyway. – That1Guy Aug 19 '15 at 19:38
  • @That1Guy I dunno. I like to learn python deeper and eventually make a framework one day. plus I have this Obsessive Compulsive need to understand something I can't!. but yeah thanks though. I'll look around more for more info on metaclasses. – Zion Aug 19 '15 at 19:45
1

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>
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176