3

I have a subclass that may have a method 'method_x' defined. I want to know if 'method_x' was defined somewhere else up the class hierarchy.

If I do:

hasattr(self, 'method_x')

I'll get a truth value that also looks at any methods defined for the subclass. How do I limit this to just inquire as to whether or not that method was defined higher up the class chain?

Morgan Thrapp
  • 9,748
  • 3
  • 46
  • 67
piRSquared
  • 285,575
  • 57
  • 475
  • 624

2 Answers2

7

If you're using Python 3, you can supply super() to the object parameter of hasattr.

For example:

class TestBase:
    def __init__(self):
        self.foo = 1

    def foo_printer(self):
        print(self.foo)


class TestChild(TestBase):
    def __init__(self):
        super().__init__()
        print(hasattr(super(), 'foo_printer'))

test = TestChild()

With Python 2, it's similar, you just have to be more explicit in your super() call.

class TestBase(object):
    def __init__(self):
        self.foo = 1

    def foo_printer(self):
        print(self.foo)


class TestChild(TestBase):
    def __init__(self):
        super(TestChild, self).__init__()
        print(hasattr(super(TestChild, self), 'foo_printer'))


test = TestChild()

Both 2 and 3 will work with multiple levels of inheritance and mixins.

Morgan Thrapp
  • 9,748
  • 3
  • 46
  • 67
  • This does not appear to work as I expected. If you define it in the child class, it works OK, but if I have a method defined in a mixin that wants to check if it should call `super().method`, it always returns `False`. – Mad Physicist Jun 11 '22 at 03:41
0

Python 3.3 introduced a __qualname__ attribute on functions, which you can check on your subclass' methods to learn which class they were defined on:

class Foo:
    def a(self): ...
    def b(self): ...

class Bar(Foo):
    def a(self): ...
    
>>> Bar.a.__qualname__
'Bar.a'
>>> Bar.b.__qualname__
'Foo.b'

With that in mind, you can write something like this:

def inherited(instance, method_name) -> bool:
    cls_name = instance.__class__.__name__
    qual_name = getattr(instance, method_name).__qualname__
    return not qual_name.startswith(f"{cls_name}.")

>>> inherited(Foo(), "a")
False
>>> inherited(Foo(), "b")
True

This will only work for methods, and not attributes in general.

Arne
  • 17,706
  • 5
  • 83
  • 99