Finding in which class or subclass methods were defined - python

After evaluating the following code:
class A():
def method1():
pass
def method2():
pass
class B(A):
def method3():
pass
def method4():
pass
class C(B):
def method1(): # notice the overriding of A.method1
pass
def method5():
pass
myC = C()
import inspect
# Find all methods
print [attr for attr in inspect.getmembers(myC) if callable(attr[1])]
[('method1', bound method C.method1 of <builtin.C instance at 0x079EA0D0>),
('method2', bound method C.method2 of <builtin.C instance at 0x079EA0D0>), ('method3', bound method C.method3 of <builtin.C instance at 0x079EA0D0>), ('method4', bound method C.method4 of <builtin.C instance at 0x079EA0D0), ('method5', builtin.C instance at 0x079EA0D0>)]
How to retrieve the origin of the methods ?
method1 and method5 come directly from class C definition
method3 and method4 come from subclass B of class C
method2 comes from subclass A of subclass B of class C.
For those who like to known the final goal, I want to display help for the methods directly defined in class C, not in subclasses.

You can check if the attribute is present in myC's class __dict__:
for attr in filter(lambda m: callable(m[1]), inspect.getmembers(myC)):
if attr[0] in myC.__class__.__dict__:
# print help

"""
method1 and method5 come directly from class C definition
method3 and method4 come from subclass B of class C
method2 comes from subclass A of subclass B of class C.
"""
import inspect
class A():
def method1():
pass
def method2():
pass
class D():
def method8():
pass
def method9():
pass
class B(A, D):
def method3():
pass
def method4():
pass
class C(B):
def method1(): # notice the overriding of A.method1
pass
def method5():
pass
# my solution is a recursive method for classes/methods(nested)
def bases(cls):
for _bases in cls.__bases__:
print cls, ">", _bases
if inspect.isclass(_bases):
print ">", _bases, [method for method in dir(_bases) if callable(getattr(_bases, method))]
bases(_bases)
bases(C)
__builtin__.C > __builtin__.B
> __builtin__.B ['method1', 'method2', 'method3', 'method4', 'method8', 'method9']
__builtin__.B > __builtin__.A
> __builtin__.A ['method1', 'method2']
__builtin__.B > __builtin__.D
> __builtin__.D ['method8', 'method9']

Related

Get __qualname__ of inherited function

class A:
def func(self):
print('This was in A')
class B(A):
def text(self):
print('This is in B')
print(B.func.__qualname__)
The output of this would be A.func but I'd like it to be B.func
It would be like functools.wraps but for class functions.
I'm not sure how exactly you'll be using this in logging, it would help if you could elaborate on your question. Here I assume what you want is a function (named print_func), that when passed with an instance method, prints the method name and class name of the actual instance.
In this case, you can just separately print the instance class name and the method name, for example:
class A:
def func(self):
print('This was in A')
class B(A):
def text(self):
print('This is in B')
def print_func(f):
print(f.__self__.__class__.__qualname__, ".", f.__name__)
b = B()
print_func(b.func) # B . func
The __self__ attribute of a bound instance method gives the instance that the method is bound to (in this case, b), and __class__ of an instance gives the class of the instance.
You can also extend the print_func method by checking whether f is a bound instance method, and add additional routines for normal functions, static methods, and class methods. An example would be:
import inspect
def print_func(f):
if inspect.ismethod(f):
# Could be instance method or class method.
self = f.__self__
if inspect.isclass(self):
print("classmethod", self.__qualname__ + "." + f.__name__)
else:
print(self.__class__.__qualname__ + "." + f.__name__)
else:
# The method is either an unbound instance method, a static method,
# or a normal function. Either case, it is not bound to an instance,
# so `__qualname__` should suffice.
# It would be non-trivial to print the actual class from which we
# accessed this function. See examples marked with asterisks (*) below.
print(f.__qualname__)
To see it in action:
class A:
def method(self): pass
#classmethod
def classmethod(cls): pass
#staticmethod
def staticmethod(): pass
class B(A):
pass
def function(): pass
a = A()
b = B()
print_func(A.method) # A.method
print_func(B.method) # A.method *
print_func(a.method) # A.method
print_func(b.method) # B.method
print_func(A.classmethod) # classmethod A.classmethod
print_func(B.classmethod) # classmethod B.classmethod
print_func(a.classmethod) # classmethod A.classmethod
print_func(b.classmethod) # classmethod B.classmethod
print_func(A.staticmethod) # A.staticmethod
print_func(B.staticmethod) # A.staticmethod *
print_func(function) # function
Seems that not so good solution, but it can help you:
class A:
def func(self):
print('This was in A')
class B(A):
def text(self):
print('This is in B')
def func(self):
return super().func()
print(B.func.__qualname__)

Inheriting a non-abstract class and turning all of its functions into abstractmethods

An upstream interface was given to me with all of its functions defined as non-abstract when in reality they should be decorated with #abstractmethods. I want to receive an error when I did not implement one of its functions when it's called. To do this, I would create a wrapper class and manually go through each of its defined functions and do something like this:
from abc import ABC, abstractmethod
class Foo(object):
def foo(self):
print("Foo")
class AbstractFoo(Foo, ABC):
#abstractmethod
def foo(self):
return super().foo()
class ConcreteFoo(AbstractFoo):
def foo(self):
print("Concrete Foo")
super().foo()
f = ConcreteFoo()
f.foo()
Which outputs:
Concrete Foo
Foo
I would like some way of just doing this to all functions defined by Foo. Obviously, inherited magic functions like __str__ and __repr__ should be forwarded appropriately.
Does anyone know a nice, pythonic way of doing this?
def validate_base_class_implemntation(cls):
base_cls_funcs = []
for attr in cls.__bases__[0].__dict__:
if callable(getattr(cls, attr)):
base_cls_funcs.append(attr)
cls_funcs = []
for attr in cls.__dict__:
if callable(getattr(cls, attr)):
cls_funcs.append(attr)
missing_funcs = [x for x in base_cls_funcs if x not in cls_funcs]
if len(missing_funcs) > 0:
print("Not implemented functions are: {}".format(','.join(missing_funcs)))
raise Exception("Not implement function exception!")
return cls
class Foo(object):
def foo(self):
print("Foo")
def boo(self):
print("Wow")
#validate_base_class_implemntation
class ConcreteFoo(Foo):
def foo(self):
print("Concrete Foo")
super().foo()
f = ConcreteFoo()
f.foo()
Not sure in 100% if that what you meant.
this decorator checks that the class decorated implements all the base class function(in your case, they are not decorated with abstract). if there is a function that your decorated class does not implement, it raises exception.
You can modify the original class Foo and turn all its methods into abstract methods and then define a blank subclass of Foo with metaclass=ABCMeta in order to handle the checks:
from abc import ABCMeta, abstractmethod
from types import FunctionType
class AbstractFoo(Foo, metaclass=ABCMeta):
pass
names = set()
for k, v in vars(Foo).items():
if k.startswith('__') and k.endswith('__'):
continue
elif isinstance(v, FunctionType):
names.add(k)
v.__isabstractmethod__ = True
AbstractFoo.__abstractmethods__ = frozenset(names)
Side note: This approach relies on dunder attributes being used by abc and as such can break without deprecation.

How can I make a class which has a public method that cannot be inherited by its sub-class?

I want to make a class A like this
class A:
#someDecorator
def f(self):
print('A.f')
def g(self):
print('A.g')
And I can use it like this
a = A()
a.f() #prints A.f
a.g() #prints A.g
But if I declare a class B as a subclass of A, attribute f of class B will become invisible
class B(A):
pass
b = B()
b.g() #prints A.g
b.f() #error: no such attribute
How can I make this #someDecorator?
One way is to check __class__ of the method, and return if it matches A.
def disable_method(func):
def func_wrapper(name):
if name.__class__ == A:
return func(name)
else:
raise AttributeError("Cannot call inherited method")
return func_wrapper
class A:
#disable_method
def f(self):
print('A.f')
def g(self):
print('A.g')
class B(A):
pass
a = A()
a.f()
#A.f
b = B()
b.f()
#AttributeError: Cannot call inherited method
Edit: Looking at this again it is possible to check whether the method's class has a superclass by __bases__. It should work as long as its being used as a decorator.
def disable_method(func):
def func_wrapper(name):
if name.__class__.__bases__ == object:
return func(name)
else:
raise AttributeError("Cannot call method from superclass")
return func_wrapper

Python inheritance - One constructor that calls a method that's overriden in child classes, which method is used?

I'm writing a class in python that has multiple subclasses in it I have:
class Parent:
def __init__(self, parameters):
self.MethodA(parameters)
def MethodA(parameters):
doStuff
class child1(Parent):
def MethodA(parameters):
doOtherStuff
Which method will be used when I make an object of type child1?
Try it and see:
class Parent(object):
def __init__(self, params):
self.method(params)
def method(self, params):
print "Parent's method called with", params
class Child(Parent):
def method(self, params):
print "Child's method called with", params
Child('foo')
outputs:
Child's method called with foo
child1.MethodA() would be called. Methods in most dynamic languages are essentially always virtual since the lookup of self is done at runtime.
It can be usefull for you - method resolution order.
All methods in Python are effectively virtual.
>>> class Parent(object):
def __init__(self):
self.MethodA()
def MethodA(self):
print 'A method'
>>> class child1(Parent):
def MethodA(self):
print 'child1 method'
>>> x = child1()
child1 method
>>> x.MethodA()
child1 method

Inheritance in Python Such That All Base Functions Are Called

Basically, what I want is to do this:
class B:
def fn(self):
print 'B'
class A:
def fn(self):
print 'A'
#extendInherit
class C(A,B):
pass
c=C()
c.fn()
And have the output be
A
B
How would I implement the extendInherit decorator?
This is not a job for decorators. You want to completely change the normal behaviour of a class, so this is actually a job for a metaclass.
import types
class CallAll(type):
""" MetaClass that adds methods to call all superclass implementations """
def __new__(meta, clsname, bases, attrs):
## collect a list of functions defined on superclasses
funcs = {}
for base in bases:
for name, val in vars(base).iteritems():
if type(val) is types.FunctionType:
if name in funcs:
funcs[name].append( val )
else:
funcs[name] = [val]
## now we have all methods, so decorate each of them
for name in funcs:
def caller(self, *args,**kwargs):
""" calls all baseclass implementations """
for func in funcs[name]:
func(self, *args,**kwargs)
attrs[name] = caller
return type.__new__(meta, clsname, bases, attrs)
class B:
def fn(self):
print 'B'
class A:
def fn(self):
print 'A'
class C(A,B, object):
__metaclass__=CallAll
c=C()
c.fn()
A metaclass is a possible solution, but somewhat complex. super can do it very simply (with new style classes of course: there's no reason to use legacy classes in new code!):
class B(object):
def fn(self):
print 'B'
try: super(B, self).fn()
except AttributeError: pass
class A(object):
def fn(self):
print 'A'
try: super(A, self).fn()
except AttributeError: pass
class C(A, B): pass
c = C()
c.fn()
You need the try/except to support any order of single or multiple inheritance (since at some point there will be no further base along the method-resolution-order, MRO, defining a method named fn, you need to catch and ignore the resulting AttributeError). But as you see, differently from what you appear to think based on your comment to a different answer, you don't necessarily need to override fn in your leafmost class unless you need to do something specific to that class in such an override -- super works fine on purely inherited (not overridden) methods, too!
I personally wouldn't try doing this with a decorator since using new-style classes and super(), the following can be achieved:
>>> class A(object):
... def __init__(self):
... super(A, self).__init__()
... print "A"
...
>>> class B(object):
... def __init__(self):
... super(B, self).__init__()
... print "B"
...
>>> class C(A, B):
... def __init__(self):
... super(C, self).__init__()
...
>>> foo = C()
B
A
I'd imagine method invocations would work the same way.

Categories

Resources