EquipDev
EquipDev

Reputation: 5931

How to determine if an attribute is located in origin class or inherited class?

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

Answers (2)

Kasravnd
Kasravnd

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

Martijn Pieters
Martijn Pieters

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

Related Questions