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.
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
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.
In writing a Python (2.5) program, I tried to create a class and, in its __init__ function, automatically create an instance of another class with its name as an argument to the __init__ function, something like this:
class Class1:
def __init__(self,attribute):
self.attribute1=attribute
class Class2:
def __init__(self,instanceName):
#any of Class2's attributes
exec instanceName + '=Class1('attribute1')'
# this should produce an instance of Class1 whose name is instanceName
But when I make an instance of Class2, instance=Class2('instance2'), and try to get attribute1 of instance2 (which should have been created from Class2's __init__ function) I get an error message:
Traceback (most recent call last):
File "<pyshell#29>", line 1, in <module>
print instance2.attribute1
NameError: name 'instance2' is not defined
I don't know what the problem is, since name='instance3' and
exec name+'=Class1('attribute1') does work, though this is probably because I don't have much experience with Python. How would I be able to do something like this automatically when an instance is created?
I have to run, so hopefully, someone else can fix any mistakes in this post:
class Class1:
def __init__(self, attribute):
self.attribute1 = attribute
class Class2:
def __init__(self, instanceName):
setattr(self, instanceName, Class1(...)) # replace ... with whatever parameters you want
Long time reader, first time asker. Anyway, Here's the code I'm working with:
class Person(object):
def __init__(self, s):
self.name = s
self.secret = 'I HAVE THE COOKIES'
#classmethod
def shout(self):
print self.name.upper()
class Kid(Person):
def __init__(self, s):
super(Kid,self).__init__(s)
self.age = 12
b = Person('Bob')
k = Kid('Bobby')
print b.name
print k.name
print k.age
print k.secret
k.shout()
Which results in this output and error:
Bob
Bobby
12
I HAVE THE COOKIES
Traceback (most recent call last):
File "a.py", line 22, in <module>
k.shout()
File "a.py", line 8, in shout
print self.name.upper()
AttributeError: type object 'Kid' has no attribute 'name'
I assumed that Kid would be able to use the Person's shout method substituting its (the kid's) "self" for parent (where the method lives). Apparently, that's not the case. I know I could declare name outside of init, but that's both unable to accomodate inputted data and a no-no. Another alternative would be to redefine shout for every child of Person, but that's a lot of repeated code that I'm trying to avoid.
Thanks very much in advance!
The issue is that #classmethod is a method on a class. It does not have access to an instance's attributes. Specifically the method is actually passed the class object, thus self is misnamed. You should really call shout's argument cls. If you remove the #classmethod then this would all make sense and your code would work as expected.
As it is, you can think of k.shout() as equivalent to Kid.shout().
I'm working through 'Dive Into Python' on Google App Engine and came across this error while attempting to call one class's methods from another:
ERROR __init__.py:463] create() takes exactly 1 argument (2 given)
Traceback (most recent call last):
File "main.py", line 35, in get
dal.create("sample-data");
File "dataAccess/dal.py", line 27, in create
self.data_store.create(data_dictionary);
TypeError: create() takes exactly 1 argument (2 given)
Here's my main class:
# filename: main.py
from dataAccess.dal import DataAccess
class MySampleRequestHandler(webapp.RequestHandler):
"""Configured to be invoked for a specific GET request"""
def get(self):
dal = DataAccess();
dal.create("sample-data"); # problem area
MySampleRequestHandler.get() tries to instantiate and invoke DataAccess which is defined else where:
# filename: dal.py
from dataAccess.datastore import StandardDataStore
class DataAccess:
"""Class responsible for wrapping the specific data store"""
def __init__(self):
self.data_store = None;
data_store_setting = config.SETTINGS['data_store_name'];
if data_store_setting == DataStoreTypes.SOME_CONFIG:
self.data_store = StandardDataStore();
logging.info("DataAccess init completed.");
def create(self, data_dictionary):
# Trying to access the data_store attribute declared in __init__
data_store.create(data_dictionary);
I thought I could call DataAccess.create() with 1 parameter for its argument, especially according to how Dive into Python notes about class method calls:
When defining your class methods, you must explicitly list self as the first
argument for each method, including __init__. When you call a method of an
ancestor class from within your class, you must include the self argument.
But when you call your class method from outside, you do not specify anything
for the self argument; you skip it entirely, and Python automatically adds the
instance reference for you.
In self.data_store.create(data_dictionary), the self.data_store refers to the object created by self.data_store = StandardDataStore() in the __init__ method.
It looks like the create method of a StandardDataStore object doesn't expect an additional argument.
It should be self.data_store.create(data_dictionary);