I came with a situation where the method of class A to be called from class B.
class A(object):
def __init__(self, a):
self.a = a
def abc(self):
print self.a
class B(A):
def __init__(self):
super(B, self).abc()
def method1():
a = A(2)
method1()
b = B()
Expecting Output: 2
Is it possible to call method 'abc' from class B with changing class A and should not create class A object in class B. If yes, then please let me know the solution.
The above program which I tried is giving error.
And the error I am getting is below
Traceback (most recent call last):
File "a.py", line 12, in <module>
b = B()
File "a.py", line 10, in __init__
super(B, self).abc()
File "a.py", line 6, in abc
print self.a
AttributeError: 'B' object has no attribute 'a'
Your B class __init__ method is not taking any argument, while the __init__ of class A require you to pass one (named "a"), and yet, you are not providing it. Neither in your B class or by passing it to A.
However, this would work.
class A(object):
def __init__(self, a):
self.a = a
def abc(self):
print self.a
class B(A):
def __init__(self):
self.a = 10
super(B, self).abc()
Or:
class B(A):
def __init__(self):
super(B, self).__init__(10)
inst = B()
inst.abc() # 10
Here:
class B(A):
def __init__(self):
super(B, self).abc()
The constructor of A is never called, so the initialization done in A.__init__ is missing. It fails in print self.a, because there is no a.
The super constructor should be called.
Furthermore, super(B, self).abc() is the same as self.abc().
If there was a method named abc defined in B, then self.abc() would call the method from B, whereas super(B, self).abc() would call the method from the superclass.
So, since those are the same, I would not use the ugly one. It just makes the code less readable.
With those two fixes:
class B(A):
def __init__(self):
super(B, self).__init__(1000) # whatever default value
self.abc()
Related
I have a class A encapsulating a class B instance and additional stuff. The following is a toy example.
class B(object):
def __init__(self):
self.b = 2
def square(self):
return self.b * self.b
class A(object):
def __init__(self, x):
self.b = B()
a = A(1)
print(a.b.square())
Any time an A instance wants to call a method in B, I always need to do things like 'a.b'. My hope is to get rid of '.b' for user convenience. The following codes do the job.
class B(object):
def __init__(self):
self.b = 2
def square(self):
return self.b * self.b
class A(object):
def __init__(self, x):
self.b = B()
def square(self):
return self.b.square()
a = A(1)
print(a.square())
The problem is that class B is from outside library and there are lots of and different types of things in the dir. I couldn't do it one by one manually like above. Any magical ways to handle that?
Any magical ways to handle that?
It's python, of course there are! You can use __getattr__ function to proxy unknown calls to b:
class B(object):
def shadowed(self):
print('B.shadowed')
def unshadowed(self):
print('B.unshadowed')
class A(object):
def __init__(self):
self._b = B()
def shadowed(self):
print('A.shadowed')
def __getattr__(self, name):
return getattr(self._b, name)
test = A()
test.shadowed()
test.unshadowed()
test.unknown()
Result:
A.shadowed
B.unshadowed
Traceback (most recent call last):
File "/Users/Andrew/Desktop/test.py", line 23, in <module>
test.unknown()
File "/Users/Andrew/Desktop/test.py", line 17, in __getattr__
return getattr(self._b, name)
AttributeError: 'B' object has no attribute 'unknown'
__getattr__ is called when the object doesn't have attribute that's being asked for.
Suppose the class hierarchy is like below, and the parameters inside args are passed along the inheritance chain so that all classes get the parameter they need:
class A(object):
def __init__(self, **args):
print('A.__init__')
self.a = args['a']
super(A, self).__init__(**args)
class B (object):
def __init__(self, **args):
print('B.__init__')
self.b = args['b']
# super(B, self).__init__(**args)
class C(A, B):
def __init__(self, **args):
print('C.__init__')
self.c = args['c']
super(C, self).__init__(**args)
c = C(a=1, b=2, c=3)
print(c.__dict__)
If class B calls super's __init__ with args, that is, uncomment this line
# super(B, self).__init__(**args)
, an exception is raised:
..., in __init__
super(B, self).__init__(**args)
TypeError: object.__init__() takes no parameters
It seems that the last class in the MRO couldn't call super's __init__, or could only call it with zero parameter. Would anyone explain why?
Suppose coder 0 writes class A and B, and then coder 1 writes class C to extend A & B. Since coder 0 doesn't know the actual MRO, how could he decide which class should call super's __init__ with args and which one shouldn't?
Or would anyone share the best practice of multiple inheritance together with parameter passing?
The base class of A and B is object. object is a class that takes no parameters, because it doesn't do anything with them. (You can't write object(1), can you?)
The best practice for solving this is to remove the consumed args before passing them along.
class A(object):
def __init__(self, **args):
print('A.__init__')
self.a = args.pop("a")
super(A, self).__init__(**args)
class B (object):
def __init__(self, **args):
print('B.__init__')
self.b = args.pop("b")
super(B, self).__init__(**args)
class C(A, B):
def __init__(self, **args):
print('C.__init__')
self.c = args.pop("c") # permanently removes 'c' from args
super(C, self).__init__(**args)
c = C(a=1, b=2, c=3)
print(c.__dict__)
I wonder if there is a way in Python to access the class which the object which the method belongs to is being called from. For example:
class A:
def __init__(self):
self.b = B()
def foo(self):
print('A')
class B:
def bar(self):
<something here>.foo()
a = A()
a.b.bar()
Basically I would like B's method bar to invoke A's method foo. And if b was an attribute of some other class C, to invoke C's version of foo instead.
You could add a reference to the class which instantiates B:
class A:
def __init__(self):
# pass self while instantiating B
self.b = B(self)
def foo(self):
print('A')
class B:
def __init__(self, rel_obj):
self.rel_obj = rel_obj
def bar(self):
self.rel_obj.foo() # access foo() using self.rel_obj
Similarly, you could pass an object of class C to invoke C's version of foo method.
What is wrong with the following code?
class A:
def A_M(self): pass
class B:
#staticmethod
def C(): super(B).A_M()
error (Python 2.7.3):
>>> a = A()
>>> a.B.C()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "..x.py", line 36, in C
def C(): super(B).A_M()
NameError: global name 'B' is not defined
Edit:
the solution was simple as this:
class A:
def A_M(self): pass
class B:
#staticmethod
def C(): A().A_M() #use of A() instead of supper, etc.
Important Note that there is an issue with this solution. If you change the name of super class (i.e. A) then you will have to update all uses inside itself as A :)).
class A(object):
def foo(self):
print('foo')
#staticmethod
def bar():
print('bar')
class B(object):
#staticmethod
def bar(obj):
# A.foo is not staticmethod, you can't use A.foo(),
# you need an instance.
# You also can't use super here to get A,
# because B is not subclass of A.
obj.foo()
A.foo(obj) # the same as obj.foo()
# A.bar is static, you can use it without an object.
A.bar()
class B(A):
def foo(self):
# Again, B.foo shouldn't be a staticmethod, because A.foo isn't.
super(B, self).foo()
#staticmethod
def bar():
# You have to use super(type, type) if you don't have an instance.
super(B, B).bar()
a, b = A(), B()
a.B.bar(a)
b.foo()
B.bar()
See this for details on super(B, B).
You need to use a fully-qualified name. Also, in python 2.7, you need to use (object), else super(A.B) will give TypeError: must be type, not classobj
class A(object):
def A_M(self):
pass
class B(object):
#staticmethod
def C():
super(A.B).A_M()
Finally, super(A.B) is essentially object here. Did you mean for B to inherit from A? Or were you simply looking for A.A_M()?
A latecommer, to just encapsulate B in A the easy way is this:
class A:
def A_M(self):
return "hi"
class B:
#staticmethod
def C():
return A().A_M()
a = A()
print a.B().C()
Not sure this is what you need, but the question was still unsolved, so I guessed.
I really need to find some way to superclass's classmethod from subclasses of that superclass.
here is the generalized code:
class A(object):
def __init__(self):
print "A init"
#classmethod
def _method(cls):
print cls
return cls()
class B(A):
def __init__(self):
print "B init"
class C(B):
def __init__(self):
print "C init"
#classmethod
def _method(cls):
print "calling super(C)'s classmethod"
return super(C)._method()
c = C._method()
which results in :
Traceback (most recent call last):
File "C:/Python27x64/testclass", line 26, in <module>
c = C._method()
File "C:/Python27x64/testclass", line 22, in _method
return super(C)._method()
AttributeError: 'super' object has no attribute '_method'
note that from c = C._method(), I am calling uninitialized class C's classmethod. and from C, I call also uninitialized class A or B (traversing through the MRO)'s classmethod.
How can I achieve this?
You need to include the cls variable in the super call:
class C(B):
def __init__(self):
print "C init"
#classmethod
def _method(cls):
print "calling super(C)'s classmethod"
return super(C, cls)._method()