python child class method calling overridden classmethod with non-classmethod - python

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

Related

Is `self` actually mandatory for class methods in Python?

I saw a code snippet in Python 3.6.5 that can be replicated with this simplified example below and I do not understand if this is something concerning or not. I am surprised it works honestly...
class Foo:
def bar(numb):
return numb
A1 = bar(1)
print(Foo)
print(Foo.A1)
print(Foo.bar(17))
In all python guides that I have seen, self appears as the first argument for all the purposes we know and love. When it is not, the methods are decorated with a static decorator and all is well. This case works as it is, however. If I were to use the static decorator on bar, I get a TypeError when setting A1:
Traceback (most recent call last):
File "/home/user/dir/understanding_classes.py", line 1, in <module>
class Foo:
File "/home/user/dir/understanding_classes.py", line 7, in Foo
A1 = bar(1)
TypeError: 'staticmethod' object is not callable
Is this something that is OK keeping in the code or is this a potential problem? I hope the question is not too broad, but how and why does this work?
The first parameter of the method will be set to the receiver. We call it self by convention, but self isn't a keyword; any valid parameter name would work just as well.
There's two different ways to invoke a method that are relevant here. Let's say we have a simple Person class with a name and a say_hi method
class Person:
def __init__(self, name):
self.name = name
def say_hi(self):
print(f'Hi my name is {self.name}')
p = Person('J.K.')
If we call the method on p, we'll get a call to say_hi with self=p
p.say_hi() # self=p, prints 'Hi my name is J.K.'
What you're doing in your example is calling the method via the class, and passing that first argument explicitly. The equivalent call here would be
Person.say_hi(p) # explicit self=p, also prints 'Hi my name is J.K.'
In your example you're using a non-static method then calling it through the class, then explicitly passing the first parameter. It happens to work but it doesn't make a lot of sense because you should be able to invoke a non-static method by saying
f = Foo()
f.bar() # numb = f, works, but numb isn't a number it's a Foo
If you want to put a function inside of a class that doesn't have a receiver, that's when you want to use #staticmethod (or, #classmethod more often)
class Person:
def __init__(self, name):
self.name = name
def say_hi(self):
print(f'Hi my name is {self.name}')
#staticmethod
def say_hello():
print('hello')
p = Person('J.K.')
Person.say_hello()
p.say_hello()

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.

How to make an Python subclass uncallable

How do you "disable" the __call__ method on a subclass so the following would be true:
class Parent(object):
def __call__(self):
return
class Child(Parent):
def __init__(self):
super(Child, self).__init__()
object.__setattr__(self, '__call__', None)
>>> c = Child()
>>> callable(c)
False
This and other ways of trying to set __call__ to some non-callable value still result in the child appearing as callable.
You can't. As jonrsharpe points out, there's no way to make Child appear to not have the attribute, and that's what callable(Child()) relies on to produce its answer. Even making it a descriptor that raises AttributeError won't work, per this bug report: https://bugs.python.org/issue23990 . A python 2 example:
>>> class Parent(object):
... def __call__(self): pass
...
>>> class Child(Parent):
... __call__ = property()
...
>>> c = Child()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute
>>> c.__call__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: unreadable attribute
>>> callable(c)
True
This is because callable(...) doesn't act out the descriptor protocol. Actually calling the object, or accessing a __call__ attribute, involves retrieving the method even if it's behind a property, through the normal descriptor protocol. But callable(...) doesn't bother going that far, if it finds anything at all it is satisfied, and every subclass of Parent will have something for __call__ -- either an attribute in a subclass, or the definition from Parent.
So while you can make actually calling the instance fail with any exception you want, you can't ever make callable(some_instance_of_parent) return False.
It's a bad idea to change the public interface of the class so radically from the parent to the base.
As pointed out elsewhere, you cant uninherit __call__. If you really need to mix in callable and non callable classes you should use another test (adding a class attribute) or simply making it safe to call the variants with no functionality.
To do the latter, You could override the __call__ to raise NotImplemented (or better, a custom exception of your own) if for some reason you wanted to mix a non-callable class in with the callable variants:
class Parent(object):
def __call__(self):
print "called"
class Child (Parent):
def __call__(self):
raise NotACallableInstanceException()
for child_or_parent in list_of_children_and_parents():
try:
child_or_parent()
except NotACallableInstanceException:
pass
Or, just override call with pass:
class Parent(object):
def __call__(self):
print "called"
class Child (Parent):
def __call__(self):
pass
Which will still be callable but just be a nullop.

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)

My classes think that "self" is an argument that needs a value assigned

I'm not sure why this is happening. It seems to think that "self" requires an argument, which doesn't make any sense.
Here's my code:
class Animal:
def __init__(self):
self.quality = 1
class Bear(Animal):
def __init__(self):
Animal.__init__(self)
def getImage(self):
return "bear.ppm"
class Fish(Animal):
def __init__(self):
Animal.__init__(self)
def getImage(self):
return "fish.ppm"
And the error I get is:
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
Bear.getImage()
TypeError: getImage() takes exactly 1 argument (0 given)
You have to instantiate Bear before you call getImage():
b = Bear()
b.getImage()
getImage is an instance method, so it is only designed to be called on a specific instance of the Bear class. The state of that instance is what is passed as the self variable to getImage. Calling b.getImage() is equivalent to this:
b = Bear()
Bear.getImage(b)
So, without an instance of Bear, there is nothing that can be used for the self argument, which is why you see that exception when you called Bear.getImage(). See the documentation on Python instance methods for more information.
If you want to be able to call getImage on the class Bear rather than on a specific instance, you need to make it a static method, using the #staticmethod decorator:
class Bear(Animal):
def __init__(self):
Animal.__init__(self)
#staticmethod
def getImage():
return "bear.ppm"
Then you could call Bear.getImage().
getImage() is an instance method, so it can only be called with a instantiation of Bear class. So here is how you can do it:
Bear().getImage()
or
be = Bear()
be.getImage()

Categories