I'm having trouble actually following the source code, mostly because simply using grep on it isn't working.
Is there any way to find out from which class a certain method was inherited? For example, if there is class A, which has method foo() and bar(), and there is class B which has biz(), and class C which inherits all of it's attributes and methods from both A and B, how would I be able to determine which class foo() came from (without strictly looking at the source manually)?
You can always access the super-classes of a class by looking in the __bases__ attribute for that class. Given three simple classes A, B, C (which for Py2.7 must inherit from object) :
class A:
def foo():
pass
def bar():
pass
class B:
def biz(): pass
class C(A, B):
pass
You can then iterate through the __bases__ for C and check whether an attribute, function foo (for example), exists in one of these classes.
To do this you can check for membership of foo in the __dict__ that holds the names for each class object in __bases__:
for base in C.__bases__:
if 'foo' in base.__dict__:
print("Attribute: '{}' defined in {}".format("foo", base))
Which prints:
Attribute: 'foo' defined in <class '__main__.A'>
For a more complete and general view:
# get all attributes of class C
vars = [classVar for classVar in dir(C) if not classVar.startswith('__')]
# iterate through all bases:
for base in C.__bases__:
for attr in vars:
if attr in base.__dict__:
print("Attribute: '{}' defined in {}".format(attr, base))
Which returns:
Attribute: 'bar' defined in <class '__main__.A'>
Attribute: 'foo' defined in <class '__main__.A'>
Attribute: 'biz' defined in <class '__main__.B'>
For all classes in the chain of inheritance you can switch __bases__ to __mro__ which indicates the method resolution order; a tuple of objects python searches when trying to resolve attribute references.
If we add a function to class C and make another class D that inherits from C:
class C(A, B):
def another(): pass
class D(C): pass
To get a view of where each function is defined, just switch __bases__ with __mro__ in the loop:
# hold D's attributes (functions names, variables.)
attrs = [var for var in dir(D) if not var.startswith('__')]
# vars contents: ['another', 'bar', 'biz', 'foo']
for base in D.__mro__:
for attr in attrs:
if attr in base.__dict__:
print("Attribute: '{}' defined in {}".format(attr, base))
Now, this follows the mro tuple (equal to (__main__.D, __main__.C, __main__.A, __main__.B, object) in this specific case) and yields a similar output:
Attribute: 'another' defined in <class '__main__.C'>
Attribute: 'bar' defined in <class '__main__.A'>
Attribute: 'foo' defined in <class '__main__.A'>
Attribute: 'biz' defined in <class '__main__.B'>
You can do checking through the parent classes as determined by the method resolution order and checking the members of each parent class:
for i in C.__mro__:
if 'bar' in i.__dict__:
print 'bar is in class ' + i.__name__
So, given the following classes definitions:
class A(object):
def foo(self): pass
def bar(self): pass
class B(object):
def biz(self): pass
class C(A,B): pass
Running the above code will give
bar is in class A
Related
Is there a way to get all overriden functions of a subclass in Python?
Example:
class A:
def a1(self):
pass
def a2(self):
pass
class B(A):
def a2(self):
pass
def b1(self):
pass
Here, I would like to get a list ["a2"] for an object of class B (or for the class object itself) since class B overrides only a single method, namely a2.
You can access the parent classes with cls.__bases__, find all attributes of the parents with dir, and access all the attributes of the class itself with vars:
def get_overridden_methods(cls):
# collect all attributes inherited from parent classes
parent_attrs = set()
for base in cls.__bases__:
parent_attrs.update(dir(base))
# find all methods implemented in the class itself
methods = {name for name, thing in vars(cls).items() if callable(thing)}
# return the intersection of both
return parent_attrs.intersection(methods)
>>> get_overridden_methods(B)
{'a2'}
You can make use of the __mro__ tuple, which holds the method resolution order.
For your example:
>>> B.__mro__
( <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
So you could loop over that tuple and check if a B method is also in one of the other classes.
class A:
def a1(self):
pass
def a2(self):
pass
class B(A):
def a2(self):
super().a2()
pass
def b1(self):
pass
obj = B()
obj.a2() # ***first give the output of parent class then child class***
Given a nested class B:
class A:
class B:
def __init__(self):
pass
ab = A.B()
How can I get the full name of the class for ab? I'd expect a result like A.B.
You could get the fully qualified name, __qualname__, of its __class__:
>>> ab.__class__.__qualname__
'A.B'
Preferably by using type (which calls __class__ on the instance):
>>> type(ab).__qualname__
I made a post here functions and class attributes (python)
When you define
a class attribute and gave it a function like this:
example 1
def add_age(cls,age):
cls.yrs_old = age
return cls
class Test:
age = add_age
a = Test()
a.age(5)
print(a.yrs_old)
self is automatically passed as the first argument of the add_age function.
However toying around with it more doing the same thing
but this time passing the function as an instance attribute like this:
example 2
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
a = Test(test_func)
print(a.func())
Answers in the linked post stated that all functions in the class are automatically passed a self if the class is instantiated like this:
a = Test(test_func)
Now what's strange here is had I put test_func in a class attribute it works just like my very first example.
If you pass the function in the constructor/init like this:
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
and call it like this:
a = Test(test_func)
print(a.func())
a.func is suddenly acting like a static method as opposed to example 1 where the function defined inside the class attribute becomes a regular class method.
What's going on?.
I thought all functions within a class are implicitly passed a self argument.
After the body of the class statement is evaluated, the metaclass wraps each function in a descriptor which takes care of the distinction between instance, class, and static methods. When you assign the function to an instance attribute, you bypass that machinery, so that the attribute refers to a plain function object.
From documentation -
Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok.
This means that only methods that are assigned to classes are instance methods for the instances of the class.
Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> b = A()
>>> b.a()
Hmm
>>> b.a
<bound method A.a of <__main__.A object at 0x006D13D0>>
But as soon as you assign a separate function object to the instance variable, it is no longer an instance method , since is is not defined at the class level, it is only defined for that particular instance , Example -
>>> def c():
... print("Hello")
...
>>> b.a = c
>>> b.a()
Hello
>>> b.a
<function c at 0x0017B198>
As you can see, when you directly assigned the function to the instance variable (instead of assigning it to class variable , it is now a normal instance attribute, that references a function object, and not an instance method.
You can also assign functions to class variables after the definition of class , and the instances would automatically get them as instance methods, Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> def c(a):
... print("Hello - ", a)
...
>>> b = A()
>>> A.b = c
>>> b.b
<bound method A.c of <__main__.A object at 0x006D13D0>>
>>> b.b()
Hello <__main__.A object at 0x006D13D0>
Class Foo is defined with a metaclass Meta. The metaclass loops over the class attributes and prints them to screen.
Class Bar subclasses Foo. However, the metaclass does not print the inherited attributes from Bar.
Why doesn't the metaclass have access to Foo's attributes inherited in Bar? What am I not understanding about python's metaclass system?
Here is the sample code in 2.7:
class Meta(type):
def __init__(cls, name, bases, attrs):
print "bases = {}".format(bases)
items = {k:v for k,v in attrs.iteritems() if not k.startswith('__')}
for k,v in items.iteritems():
print k, v
class Foo(object):
__metaclass__ = Meta
hi = 1
# This prints:
# bases = (<type 'object'>,)
# hi 1
class Bar(Foo):
pass
# This prints:
# bases = (<class '__main__.Foo'>,)
Foo.hi
#prints 1
Bar.hi
#prints 1
The attrs parameter to __init__ only contains the attributes for that class, not for its bases.
A Bar object does not have an attribute hi. Instead, when you ask for Bar.hi the lookup will start at Bar, find out that it doesn't have hi, then look in base Foo to find it.
As #orlp says, attrs contains only the class dictionary for the class being created. You still have access to hi, however, because it's in the __dict__ attribute of one of Foo's bases. That is, you could do something similar to what you have, but recurse through the base classes and print out the entries in each base class dictionary.
Another approach is to use dir(), which should roughly return a list of all attributes a class has. I say roughly because a class can implement __getattr__ or __getattribute__ to return attributes "on the fly", meaning that the class may not have a well-defined set of attributes for dir() to return -- see the full disclaimer here. But in many common cases, something like the following will work:
class Meta(type):
def __init__(cls, name, bases, attrs):
print "bases = {}".format(bases)
for attr in dir(cls):
if not attr.startswith('_'):
print attr, getattr(cls, attr)
class Foo(object):
__metaclass__ = Meta
hi = 1
class Bar(Foo):
pass
Which prints:
bases = (<type 'object'>,)
hi 1
bases = (<class '__main__.Foo'>,)
hi 1
assume following class definition:
class A:
def f(self):
return 'this is f'
#staticmethod
def g():
return 'this is g'
a = A()
So f is a normal method and g is a static method.
Now, how can I check if the funcion objects a.f and a.g are static or not? Is there a "isstatic" funcion in Python?
I have to know this because I have lists containing many different function (method) objects, and to call them I have to know if they are expecting "self" as a parameter or not.
Lets experiment a bit:
>>> import types
>>> class A:
... def f(self):
... return 'this is f'
... #staticmethod
... def g():
... return 'this is g'
...
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0x800f21320>>
>>> a.g
<function g at 0x800eb28c0>
>>> isinstance(a.g, types.FunctionType)
True
>>> isinstance(a.f, types.FunctionType)
False
So it looks like you can use types.FunctionType to distinguish static methods.
Your approach seems a bit flawed to me, but you can check class attributes:
(in Python 2.7):
>>> type(A.f)
<type 'instancemethod'>
>>> type(A.g)
<type 'function'>
or instance attributes in Python 3.x
>>> a = A()
>>> type(a.f)
<type 'method'>
>>> type(a.g)
<type 'function'>
To supplement the answers here, in Python 3 the best way is like so:
import inspect
class Test:
#staticmethod
def test(): pass
isstatic = isinstance(inspect.getattr_static(Test, "test"), staticmethod)
We use getattr_static rather than getattr, since getattr will retrieve the bound method or function, not the staticmethod class object. You can do a similar check for classmethod types and property's (e.g. attributes defined using the #property decorator)
Note that even though it is a staticmethod, don't assume it was defined inside the class. The method source may have originated from another class. To get the true source, you can look at the underlying function's qualified name and module. For example:
class A:
#staticmethod:
def test(): pass
class B: pass
B.test = inspect.getattr_static(A, "test")
print("true source: ", B.test.__qualname__)
Technically, any method can be used as "static" methods, so long as they are called on the class itself, so just keep that in mind. For example, this will work perfectly fine:
class Test:
def test():
print("works!")
Test.test()
That example will not work with instances of Test, since the method will be bound to the instance and called as Test.test(self) instead.
Instance and class methods can be used as static methods as well in some cases, so long as the first arg is handled properly.
class Test:
def test(self):
print("works!")
Test.test(None)
Perhaps another rare case is a staticmethod that is also bound to a class or instance. For example:
class Test:
#classmethod
def test(cls): pass
Test.static_test = staticmethod(Test.test)
Though technically it is a staticmethod, it is really behaving like a classmethod. So in your introspection, you may consider checking the __self__ (recursively on __func__) to see if the method is bound to a class or instance.
I happens to have a module to solve this. And it's Python2/3 compatible solution. And it allows to test with method inherit from parent class.
Plus, this module can also test:
regular attribute
property style method
regular method
staticmethod
classmethod
For example:
class Base(object):
attribute = "attribute"
#property
def property_method(self):
return "property_method"
def regular_method(self):
return "regular_method"
#staticmethod
def static_method():
return "static_method"
#classmethod
def class_method(cls):
return "class_method"
class MyClass(Base):
pass
Here's the solution for staticmethod only. But I recommend to use the module posted here.
import inspect
def is_static_method(klass, attr, value=None):
"""Test if a value of a class is static method.
example::
class MyClass(object):
#staticmethod
def method():
...
:param klass: the class
:param attr: attribute name
:param value: attribute value
"""
if value is None:
value = getattr(klass, attr)
assert getattr(klass, attr) == value
for cls in inspect.getmro(klass):
if inspect.isroutine(value):
if attr in cls.__dict__:
bound_value = cls.__dict__[attr]
if isinstance(bound_value, staticmethod):
return True
return False
Why bother? You can just call g like you call f:
a = A()
a.f()
a.g()