Assign module method to a Class variable or Instance variable - python

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.

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

Related

python child class method calling overridden classmethod with non-classmethod

I am trying to do the following in python3:
class Parent:
#classmethod
def show(cls, message):
print(f'{message}')
#classmethod
def ask(cls, message):
cls.show(f'{message}???')
class Child(Parent):
#property
def name(self):
return 'John'
def show(self, message):
print(f'{self.name}: {message}')
instance = Child()
instance.ask('what')
But it then complains
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in ask
TypeError: Child.show() missing 1 required positional argument: 'message'
even so child.show works as expected. So it seems that child.ask is calling Parent.show... I tried to mark Child.show as classmethod too, but then the cls.name is not showing the expected output:
class Child2(Parent):
#property
def name(self):
return 'John'
#classmethod
def show(cls, message):
print(f'{cls.name}: {message}')
instance2 = Child2()
instance2.ask('what')
this shows
<property object at 0xfc7b90>: what???
Is there a way to override a parent classmethod with a non-classmethod, but keeping other parent classmethod to call the overridden one?
I found it hard to follow for the second half of the question but there was an issue I saw and it might help you solve your problem.
When you said even so child.show works as expected. So it seems that child.ask is calling Parent.show, thats not what is happening.
When you called instance.ask("what"), it called the #classmethod decorated method of the Child class (which is inherited from the parent). This ask method is passing the class Child as the first argument, (not the instance you created). This means the line
cls.show(f'{message}???')
is equivalent to
Child.show(f'{message}???') # because cls is the Class not the instance
The show method inside the Child class is an instance method and expects the first argument to be the actual instance (self) but the string f'{message}???' is being passed to it and it expects a second message string to be passed so that's why its is throwing an error.
Hope this helped

Decorator on class makes class NoneType when importing

I'm trying to understand why a decorator on my class modifies the class in such way that it appears to be 'NoneType' when trying to import the class from another script.
In my_class.py I have:
my_dict = dict()
def register(cls):
name = cls.__name__
my_dict[name] = cls
#register # will be commented
class MyClass:
def my_method(self):
print("running class method")
print("my_dict: ", my_dict)
In another module my_main.py I import the class like
from my_class import MyClass
print(type(MyClass))
print(MyClass.my_method)
If I run it with $ python3 my_main.py I get the following output:
my_dict: {'MyClass': <class 'my_class.MyClass'>}
<class 'NoneType'>
Traceback (most recent call last):
File "my_main.py", line 4, in <module>
print(MyClass.my_method)
AttributeError: 'NoneType' object has no attribute 'my_method'
By commenting the #register line in my_class.py the my_main.py runs without error and outputs:
my_dict: {}
<class 'type'>
<function MyClass.my_method at 0x7ff06f254f28>
..but obviously my_dict is no longer filled. Is there a way of registering my_class with the given decorator AND accessing the attributes of the class after importing it in another script?
A decorator is no more than just a normal function.
I don't want to describe a lot about how to write a decorator rightly. But at least, you should let your decorator return a class.
That is similar when you write a decorator for a function. Decorator should at least return a function.
For example:
def decorator(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
print("Hi")
return method(*args, **kwargs)
return wrapper
When you use this decorator to decorate a function, you are actually pass that function to decorator: new_function = decorator(original_function).
Which means new_function is wrapped by wrapper. That is how decorator works. When you execute decorated function, it actually execute the wrapper:
print("Hi")
return method(*args, **kwargs) # pass all args to original function and return its return value.
In your code, your decorator just returns None.

Why isn't self passed once an attribute is changed to refer to an external function?

If one changes a object attribute which refers to an internal method to an external function after the object is created, self is not passed to the new function, even if the attribute (which contains a function) is called like before.
class Person:
def greet(self):
print("hello")
do = greet
def wave(person):
print("bye")
alice = Person()
alice.do() #prints 'hello'
#change do to point an external function
alice.do = wave
alice.do() #Error: Missing argument
The exact error which I get is:
hello
Traceback (most recent call last):
File "C:\Users\Owner\Desktop\miniLiveMethodSwitch.py", line 15, in <module>
alice.do() #Error: Missing argument
TypeError: wave() missing 1 required positional argument: 'person'
If one move the external function into the class as a internal method,
class Person:
def greet(self):
print("hello")
def wave(person):
print("bye")
do = greet
Then the code works as expected. Why isn't self passed once the attribute is changed to refer to an external function? What is the proper way to call the function so that self is passed?
When Python finds alice.do in the class dict, it invokes the descriptor protocol, calling the function's __get__ method to produce a bound method object that will be the result of the alice.do lookup. This bound method object knows what self should be and will automatically insert the self argument into the argument list when forwarding arguments to the underlying function. (This happens positionally, not by the self name.)
When Python finds alice.do in alice's instance dict, the descriptor protocol is not invoked, and alice.do is just the raw function. self is not injected.
Try this:
class Person:
def greet(self):
print("hello")
do = greet
def wave(person):
print("bye")
alice = Person()
alice.do() #prints 'hello'
print(type(alice.do))
#change do to point an external function
alice.do = wave
print(type(alice.do))
The output is: method and then function:
hello
<class 'method'>
<class 'function'>

TypeError: unbound method fun1() must be called with c1 instance as first argument (got nothing instead)

In Python, I'm implementing Inheritance. The code is as given below:
class c1(object):
def __init__(self):
pass
def fun1(self):
print 'In Fun1'
class c2(c1):
def __init__(self):
c1.__init__(self)
def fun2(self):
c1.fun1()
print 'In Fun2'
obj = c2()
obj.fun2()
When I run it, I'm getting the following error:
Traceback (most recent call last):
File "C:/Users/madhuras/Documents/inheritance_example.py", line 15, in <module>
obj.fun2()
File "C:/Users/madhuras/Documents/inheritance_example.py", line 11, in fun2
c1.fun1()
TypeError: unbound method fun1() must be called with c1 instance as first argument (got nothing instead)
Why am I getting this error?
Thanks in Advance!
You cannot call the method fun1 of c1 using c1.fun1() , since its an instance method (and not a class method). You can use super to access fun1() (or even self.fun1() ) . Same for the __init__() method (though it is working currently) , better is to use super() for that as well.
Try the below -
class c1(object):
def __init__(self):
pass
def fun1(self):
print 'In Fun1'
class c2(c1):
def __init__(self):
super(c2, self).__init__()
def fun2(self):
super(c2, self).fun1() #you can also do self.fun1() , but explicitly asking python to call super class's fun1 maybe better
print 'In Fun2'
obj = c2()
obj.fun2()
The error is in the definition of fun2, where you meant to write
self.fun1()
or maybe, if you want to specifify the implementation,
c1.fun1(self)
instead of
c1.fun1()
c1 is the name of a class, not of an instance.

exceptions.AttributeError: class KeyAgent has no attribute 'delele_customer_node()'

I've 2 files: customer.py & agent.py. It looks like this:
customer.py:
from agent import KeyAgent
class CustomerController(object):
def __init__(self, container):
self.key = container.agent
def delete(self, path):
KeyAgent.delele_customer_node()
agent.py:
class KeyAgent(service.Service):
def __init__(self):
pass
def delele_customer_node():
....
Python is throwing this exception while running:
exceptions.AttributeError: class KeyAgent has no attribute 'delele_customer_node()'
Even though I've imported KeyAgent class from agent.py why method delele_customer_node() is not accessible from delete() of customer.py?
You must have misspelled the method name (delele? or delete?). The KeyAgent class does have a method delete_customer_node (I will assume that was a typo).
>>> class KeyAgent(object):
... def delete_customer():
... pass
...
>>> KeyAgent.delete_customer
<unbound method KeyAgent.delete_customer>
That means, the method is there. However your code is quite broken. Unless you use the staticmethod or classmethod decorators, the first argument of a method "must be" self, and you need to instantiate the class to call it. See what happens if you try to call this method directly:
>>> KeyAgent.delete_customer()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method delete_customer() must be called with KeyAgent instance as first argument (got nothing instead)

Categories