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()
Related
I want to be able to dynamically override all calls sent to methods on the parent class. So far I've only been able to do it for methods not defined in a parent class.
I.e. Given a parent class:
class A:
def foo(self):
print("foo")
def bar(self):
print("bar")
I want to create a Spy class that inherits from A will print "This is a spy" before calling any method on A, including foo.
class Spy(A):
pass # What do I do here?
s = Spy()
>>> s.foo()
This is a spy
foo
Current implementation
My current implementation of Spy is this:
class Spy(A):
def __getattr__(self, method):
print("This is a spy")
return getattr(super(A, self), method)
However, this only works for methods not defined in the parent class:
>>> s.baz()
This is a spy
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getattr__
AttributeError: 'super' object has no attribute 'baz'
When I call a method that exists already, it doesn't work:
>>> s.foo()
foo
>>> s.bar()
bar
The following code snippet should do what you want. I have excluded methods starting with __ because these can be problematic (for example, overriding __class__ with a function will cause an error).
class A:
def foo(self):
print("foo")
def bar(self, x):
print(x)
class Spy(A):
def functionwrapper(self, functionname):
originalfunc = getattr(super(), functionname)
def wrap(*args, **kwargs):
print("This is a spy: ", end="")
originalfunc(*args, **kwargs)
return wrap
def __init__(self):
for methodname in [method for method in dir(A) if (method[0:2] != "__")]:
setattr(self, methodname, self.functionwrapper(methodname))
s = Spy()
s.foo()
s.bar("variable")
Output
This is a spy: foo
This is a spy: variable
When I call a method that exists already, it doesn't work
That's because __getattr__ is only gonna get called when the default attribute access fails. So if the parent has that method, it gets found and your __getattr__ won't get called.
You need to intercept the __getattribute__ instead:
from types import MethodType
from inspect import isfunction
class A:
def foo(self):
print("foo")
class Spy(A):
def bar(self):
print("barrrrrrrrr")
def __getattribute__(self, name):
# check it instance's namespace
instance_dict = object.__getattribute__(self, "__dict__")
if name in instance_dict:
return instance_dict[name]
# check its class' namespace
if name in type(self).__dict__:
return object.__getattribute__(self, name)
# check parents
for cls in type(self).mro()[1:]:
if name in cls.__dict__:
member = cls.__dict__[name]
if isfunction(member):
# your code here
print("This is a spy")
return MethodType(cls.__dict__[name], self)
return member
raise AttributeError(f"{type(self)} object has no attribute '{name}'")
s = Spy()
s.foo()
s.bar()
print("-----------------------------------")
s.boo()
output:
This is a spy
foo
barrrrrrrrr
-----------------------------------
Traceback (most recent call last):
File "...", line 32, in <module>
s.boo()
File "...", line 25, in __getattribute__
raise AttributeError(f"{type(self)} object has no attribute '{name}'")
AttributeError: <class '__main__.Spy'> object has no attribute 'boo'
So this is one way of doing it and I agree that it might be overkill.
You do that in three steps:
Check to see it is in the instance's namespace: like the data attributes you usually set in __init__.
Check to see it is in its class' namespace: You don't want yout print statement here because it's not sent to the parent.
Check its parents
test.py:
import functools
import inspect
TXT = "This is a spy"
def deco(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
print(TXT)
return func(*args, **kwargs)
return wrapper
class A:
def foo(self):
print("foo")
def bar(self):
print("bar")
class Spy(A):
def __init__(self):
self.patch()
#classmethod
def patch(cls):
superclass = cls.__bases__[0]
for name, value in inspect.getmembers(superclass, inspect.isfunction):
setattr(cls, name, deco(value))
def main():
s = Spy()
s.foo()
s.bar()
if __name__ == "__main__":
main()
Test:
$ python test.py
This is a spy
foo
This is a spy
bar
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.
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()
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 want to choose from which class I inherit at runtime, either class A or B, depending on an argument to my init function in AorB. I already tried the following code, but the methods are not overloaded the way I want them to be overloaded: AorB("B").a() returns A.a() instead of B.a(). How do I choose from which class I inherit at runtime?
Update:
From the reaction below I tried the following code. Now I want to inherit AorB in class C, which doesn't work yet:
class A(object):
def a(self):
return "I'm A.a"
def b(self):
return "I'm A.b"
class B(object):
def a(self):
return "I'm B.a"
def c(self):
return "I'm B.c"
def AorB(classname, cache={}):
if not classname in cache:
Base = globals()[classname]
class AorB(Base):
def __init__(self):
print(classname)
Base.__init__(self)
cache[classname] = AorB
return cache[classname]()
class C(AorB):
def __init__(self, classname):
AorB.__init__(classname)
if __name__ == "__main__":
a = AorB("A")
print("A.a:", a.a())
print("A.b:", a.b())
b = AorB("B")
print("B.a:", b.a())
print("B.c:", b.c())
c = C("B")
print("C.a:", c.a())
print("C.c:", c.c())
yields
Traceback (most recent call last):
File "classtest.py", line 28, in <module>
class C(AorB):
TypeError: Error when calling the metaclass bases
function() argument 1 must be code, not str
instead of:
A
('A.a:', "I'm A.a")
('A.b:', "I'm A.b")
B
('B.a:', "I'm B.a")
('B.c:', "I'm B.c")
B
('C.a:', "I'm B.a")
('C.c:', "I'm B.c")
class A(object):
def a(self):
return "I'm A.a"
def b(self):
return "I'm A.b"
class B(object):
def a(self):
return "I'm B.a"
def c(self):
return "I'm B.c"
def make_AorB(Base, classname):
class AorB(Base):
def __init__(self):
print(classname)
Base.__init__(self)
return AorB
def make_C(Base, classname):
class C(Base):
def __init__(self):
Base.__init__(self)
def d(self):
return "I'm C.d"
return C
def make_factory(getbase, make_cls):
def factory(classname):
if not classname in factory.cache:
Base = getbase(classname)
factory.cache[classname] = make_cls(Base, classname)
return factory.cache[classname]()
factory.cache = {}
return factory
AorB = make_factory(lambda classname: globals()[classname], make_AorB)
C = make_factory(lambda classname: AorB.cache[classname], make_C)
if __name__ == "__main__":
a = AorB("A")
print(a.__class__, a.__class__.__bases__)
print("A.a:", a.a())
print("A.b:", a.b())
b = AorB("B")
print(b.__class__, b.__class__.__bases__)
print("B.a:", b.a())
print("B.c:", b.c())
c = C("B")
print(c.__class__, c.__class__.__bases__)
print("C.a:", c.a())
print("C.c:", c.c())
print("C.d:", c.d())
yields
A
(<class '__main__.AorB'>, (<class '__main__.A'>,))
('A.a:', "I'm A.a")
('A.b:', "I'm A.b")
B
(<class '__main__.AorB'>, (<class '__main__.B'>,))
('B.a:', "I'm B.a")
('B.c:', "I'm B.c")
B
(<class '__main__.C'>, (<class '__main__.AorB'>,))
('C.a:', "I'm B.a")
('C.c:', "I'm B.c")
('C.d:', "I'm C.d")
I'm not 100% sure (of your use-case), but you may be able to use the variation of type to do something like the following (where something is some conditional):
def produce_C(kls, *args, **kwdargs):
return type('C', (globals()[kls],), {})(*args, **kwdargs)
Which is going to confuse the type system though... (possibly amend the class name to be C_from_A or C_from_B - but ugh)
Here is the problem with your code (after the update)
C can't inherit from AorB, which is a function:
class C(AorB):
def __init__(self, classname):
AorB.__init__(classname)
Since you want to bass the baseclass in a call to C, you can just make C a
function that calls AorB in turn:
def C(basename):
return AorB(basename)