Im trying to write a simple setup to call a parent class #classmethod but getting error's...Please let me know what is the issue here?
class Parent(object):
def __init__(self):
print "Parent initialized"
#classmethod
def resource_setup(cls):
print "parent method"
class Child(Parent):
def __init__(self):
print "Child initialized"
#classmethod
def childmethod(cls):
super(Parent, cls).resource_setup()
print "child method"
c = Child()
Child().childmethod()
Output:
Child initialized
Child initialized
Traceback (most recent call last):
File "C:\Users\ANAND\workspace\Scratch\Scratch\Parent.py", line 30, in <module>
Child().childmethod()
File "C:\Users\ANAND\workspace\Scratch\Scratch\Parent.py", line 25, in childmethod
super(Parent, cls).resource_setup()
AttributeError: 'super' object has no attribute 'resource_setup'
Question:
1. When I create a child object c = Child(), I don't see the display of "parent initialized"..I expect the Parent class to be initialized.
2.How can I call the parent method #classmethod from the child #classmethod?
You need to call super(Child, cls). The first argument to super should be the class whose superclass you want to call the method on, not the superclass itself.
Your question 1 is unrelated. If you want the child __init__ to call the parent __init__, you need to do so explicitly using super, just as you did with parentmethod and childmethod. (That is, do super(Child, self).__init__() from within the child's __init__.
When I create a child object c = Child(), I don't see the display of "parent initialized"..I expect the Parent class to be initialized.
A: In python, derived class's __init__ does not call the base class's __init__ implicitly, you need to call it explicitly. Not as the constructor in c++ do, which may cause a bit confuse if you are a c++ programer.
How can I call the parent method #classmethod from the child #classmethod?
A: Use super(Child, cls) not super(Parent, cls).
Related
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
What is difference between both class naming conventions, both resulting in the same output?
class class_name:
def func():
print('1st class')
class class_name():
def func():
print('2nd class')
The syntax with parenthesis defines an inheritance relationship. But to inherit no class is the same as using just the class.
Python DOC on class inheritance
Suppose you had a class called parent:
class parent:
def f1(self):
print("F1 in action!")
And you wanted to use things from parent in another class (usually a relationship like parent: generalisation, child: specialisation). You can derive from that class.
class child(parent):
def f2(self):
print("F2 in action!")
# Now use child.f2 OR child.f1!
c = child()
c.f1()
c.f2()
This is new class syntax vs old class syntax
From Python's wiki:
A "New Class" is the recommended way to create a class in modern Python.
A "Classic Class" or "old-style class" is a class as it existed in Python 2.1 and before. They have been retained for backwards compatibility. This page attempts to list the differences.
The syntax for the two types looks the same; "new-style", "old style", "classic", and variants are just descriptions that various people have used; Python hasn't yet settled on a specific official choice for the terminology.
The minor syntactic difference is that New Style Classes happen to inherit from object.
Regarding #Felix question, consider the following:
class A: pass
class B(): pass
class C(object): pass
print A.__mro__
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
A.__mro__
AttributeError: class A has no attribute '__mro__'
print B.__mro__
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
B.__mro__
AttributeError: class B has no attribute '__mro__'
print C.__mro__
(<class '__main__.C'>, <type 'object'>)
You explicity have to inhert from object; there is no default inheritance.
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.
I am trying to subclass ConfigParser. When trying to access _sections it says
'acc' object has no attribute '_sections'
Example Code(python 2.7):
import ConfigParser
class acc(object, ConfigParser.RawConfigParser):
def __init__(self, acc_file):
super(acc,self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
The problem has been described much better here.
This is Python's Method Resolution Order.
So, what actually happens is that the MRO "chaining" does not behave well when encoutering an old style class. Or to be specific - classes that do not call super
In your code, configParser.RawConfigParser__init__(..) is never called. To fix this you could call it manually (simply add ConfigParser.RawConfigParser.__init__(self ...) in acc's init), although I'm not sure this is recommended or even valid.
Your other option is to make all classes conform to the new style, and invoke super, or old style, and initialize explicitly.
The only thing that seems to be working is if all classic-style classes are after all new-style classes in the output of Class.mro(), and specifically after object. This will prevent super from calling them.
The other answer isn't very safe because of this scenario:
class TheClassicClass:
def __init__(self):
print "instantiating clasic class!"
self._msg = "the classic class!"
def m(self):
print self._msg
class acc(ConfigParser.RawConfigParser, TheClassicClass, object):
def __init__(self, acc_file):
super(acc,self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
a.m()
Fix to other answer: adding these lines to acc's __init__ should inform to explicitly instantiate the classes:
ConfigParser.RawConfigParser.__init__(self)
TheClassicClass.__init__(self)
To confirm the problem in your code, let's try to reproduce this problem in a simple setup...
We'll make old (classic) classes:
class AClassic:
def __init__(self):
print "init aclassic"
class BClassic:
def __init__(self):
print "init bclassic"
self.name = "bclassic"
def m(self):
print "print from " + self.name
And new style classes that invoke super:
class ANew(object):
def __init__(self):
print "init anew"
super(ANew, self).__init__()
class BNew(object):
def __init__(self):
print "init bnew"
super(BNew, self).__init__()
def m(self):
print "print from bnew"
In the simplest case, we're manually calling __init__ to make sure classes are instantiated. This would look something like:
class TestOldStyle(AClassic, BClassic):
def __init__(self):
AClassic.__init__(self)
BClassic.__init__(self)
self.m()
print "old style"
TestOldStyle()
In this case, depth-first, left-to-right, is the order of the MRO.
Output:
old style
init aclassic
init bclassic
print from bclassic
Now let's try new style, which looks like this with super:
# will init ANew and BNew
class TestNewSuper(ANew, BNew, object):
def __init__(self):
super(TestNewSuper, self).__init__()
TestNewSuper()
Since both classes call super, they are both instantiated and the output is:
init anew
init bnew
Now, we try using some "hybrids" of a classic-style classes (ConfigParser.RawConfigParser is a classic-style-class, and cannot call super:
# will init ANew , but not BClassic
class TestOldStyleSuper(ANew, BClassic):
def __init__(self):
super(TestOldStyleSuper, self).__init__()
self.m()
Output:
init anew
And immediately an exception:
Traceback (most recent call last):
File "./inhert.py", line 35, in <module>
TestOldStyleSuper()
File "./inhert.py", line 33, in __init__
self.m()
File "./inhert.py", line 15, in m
print "print from " + self.name
AttributeError: 'TestOldStyleSuper' object has no attribute 'name'
This happens since BClassic is not instantiated.
More examples of unexpected behaviour:
# init ANew, BClassic
class TestHybrid(ANew, BClassic, BNew):
def __init__(self):
super(TestHybrid, self).__init__()
self.m()
TestHybrid()
will initialize ANew and BClassic, but not BNew:
init anew
init bclassic
print from bclassic
And creating an incosistent MRO:
# no consistent MRO exception
class TestHybrid(ANew, object, BNew):
def __init__(self):
super(TestHybrid, self).__init__()
self.m()
TestHybrid()
Exception:
Traceback (most recent call last):
File "./inhert.py", line 33, in <module>
class TestHybrid(ANew, object, BNew):
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases BNew, object
Reverse the order of subclassing as follows:
import ConfigParser
class acc(ConfigParser.RawConfigParser, object):
def __init__(self, acc_file):
super(acc, self).__init__()
self.lol = 1
print self.has_section(self.lol)
a=acc(1)
This question already has an answer here:
I can't get super() to work in python 2.7
(1 answer)
Closed 8 years ago.
Consider the following classes, running python2.7:
class S(object):
def __init__(self):
print 'Si'
self.reset()
def reset(self):
print 'Sr'
self.a=0
class U1(S):
def reset(self):
print 'U1r'
self.b=0
super(S,self).reset()
The desired functionality is that
creating an instance of the base class calls its reset method;
creating an instance of the derived class calls its reset method, and also invokes the base class's reset method.
I get (1):
>>> print S().a
Si
Sr
0
but not (2):
>>> print U1().b
Si
U1r
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tt.py", line 4, in __init__
self.reset()
File "tt.py", line 14, in reset
super(S,self).reset()
AttributeError: 'super' object has no attribute 'reset'
What's the cleanest way to get what I want? I presume the error has something to do with the order in which class membership is getting constructed, but I can't figure it out from the documentation. . .
You should be calling super(U1, self).reset() in U1.reset(). When you use super, you should always pass the name of the current class as the first argument, not the name of the parent class. As stated in the docs:
super(type[, object-or-type])
Return a proxy object that delegates method calls to a parent or sibling class of type
super will look up the method on the parent or sibling of the type you provide. When you provide the parent class, it will try to find implementations of reset on parents/siblings of the parent, which will fail.
Should be:
super(U1, self).reset()
In my head, I read "super(U1,..." as "parent of U1" to keep it straight.