Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assign module method to a Class variable or Instance variable

In module a.py

def task(): 
    print "task called"

a = task

class A:

    func = task              # this show error unbound method
    #func = task.__call__    # if i replace with this work

    def __init__(self):
        self.func_1 = task

    def test_1(self):
        self.func_1()

    @classmethod
    def test(cls):
        cls.func()


a()
A().test_1()
A.test()

Output:

task called
task called
Traceback (most recent call last):
  File "a.py", line 26, in <module>
     A.test()
  File "a.py", line 21, in test
     cls.func()
TypeError: unbound method task() must be called with A instance as 
first argument (got nothing instead)

In the module, I can easily assign a function to a variable. When inside class try to assign module level function to class variable func = task it shows error, to remove this error I have to replace it with func = task.__call__ But when I assign to instance variable its work self.func_1 = task.

My Question is: why I can't assign a module level function to a class variable without __call__ and when the same function I can assign to an instance variable is working.

like image 748
Kallz Avatar asked Jul 21 '17 08:07

Kallz


1 Answers

Because you mapped a function as unbound method of A, so when you are calling cls.func you first ask something equals to getattr(cls, 'func') which returns <unbound method A.task> BUT, this unbound method needs to be called with class as first argument.

So because in this specific case cls.func means "gives me class attribute func of cls" it can not means to the same time "call class method func" - So Python doesn't translate cls.func() by func(cls).

But within the same time, because func is <unbound method A.task> (bound to A.task) it needs to be called like func(cls) to work.

Check it with something like:

@classmethod
def test(cls):
    print getattr(cls, 'func') # <unbound method A.task>

You could fix it with something like:

def task(cls=None):
    if cls is None:
        print 'task()'
    else:
        print 'A.foo({})'.format(cls)

a = task

class A:
    func = task             # this show error unbound method

    def __init__(self):
        self.func_1 = task

    def test_1(self):
        self.func_1()

    @classmethod
    def test(cls):
        cls.func(cls())

a()
A().test_1()
A.test()

Output:

task()
task()
A.foo(<__main__.A instance at 0x7fd0310a46c8>)

Note that python3 removes unbound methods, this works only with python2.x

like image 152
Arount Avatar answered Oct 16 '22 16:10

Arount