Reputation: 5931
In the code below:
class Klass_1:
a = 1
class Klass_2(Klass_1):
b = 2
k = Klass_2()
It is possible to access both k.a
and k.b
, and hasattr(k, ...)
is True
for both 'a' and 'b'.
How to determine if an attribute is located in origin class Klass_2
or inherited class Klass_1
?
Upvotes: 1
Views: 323
Reputation: 107287
You can search in __dict__
attribute of the class object or its super classes. Here is a general function:
In [23]: def find_origin(instance, arg):
if arg in type(instance).__dict__:
return 'class'
elif any(arg in c.__dict__ for c in type(instance).__bases__):
return 'super class'
return "doesn't exist"
....:
In [24]:
In [24]: find_origin(k, 'a')
Out[24]: 'super class'
In [25]:
In [25]: find_origin(k, 'b')
Out[25]: 'class'
In [26]: find_origin(k, 'd')
Out[26]: "doesn't exist"
As @Martijn Pieters mentioned in order to search in deeper levels of super classes you need to use mro
attribute of the class obejct instead of __bases__
. As a more pythonic way you can use a generator expression within next()
function:
In [38]: def find_origin(instance, arg):
return next((c for c in type(instance).mro() if arg in c.__dict__), "doesn't exist")
....:
In [39]: find_origin(k, 'a')
Out[39]: __main__.Klass_1
In [40]: find_origin(k, 'b')
Out[40]: __main__.Klass_2
In [41]: find_origin(k, 'c')
Out[41]: "doesn't exist"
Upvotes: 1
Reputation: 1121824
You'll have to check the __dict__
objects of each class in the MRO (you can use the vars()
function here to make the code cleaner):
def class_for_attribute(instance, attribute_name):
for cls in type(instance).mro():
if attribute_name in vars(cls):
return cls
Demo:
>>> class_for_attribute(k, 'a')
<class '__main__.Klass_1'>
>>> class_for_attribute(k, 'b')
<class '__main__.Klass_2'>
Upvotes: 6