0
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.

princelySid
  • 6,687
  • 2
  • 16
  • 17

2 Answers2

1

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
Zecong Hu
  • 2,584
  • 18
  • 33
0

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__)
Andrei Berenda
  • 1,946
  • 2
  • 13
  • 27
  • Essentially, re-implement all methods of the parent class in all descendent classes…? Yeah, not very scalable at all… – deceze Jan 20 '20 at 13:47
  • This works, though I would love a solution that didn't have me rewriting the func in every dependent class because it starts to get tedious when you have several inherited functions – princelySid Jan 20 '20 at 14:01