Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

name of the class that contains the method code

I'm trying to find the name of the class that contains method code.

In the example underneath I use self.__class__.__name__, but of course this returns the name of the class of which self is an instance and not class that contains the test() method code. b.test() will print 'B' while I would like to get 'A'.

I looked into the inspect module documentation but did not find anything directly useful.

class A:
  def __init__(self):
    pass
  def test(self):
    print self.__class__.__name__

class B(A):
  def __init__(self):
    A.__init__(self)



a = A()
b = B()

a.test()
b.test()
like image 720
user266940 Avatar asked May 06 '10 14:05

user266940


People also ask

Which method is called by the class name?

static methods can be called using class name and do not require an object of that class.

What is a method class?

A class method is a method that can be invoked without reference to any object instance; these are called static methods in other languages. The term method usually refers to an instance method. The more specific phrase class method is used to refer to class methods.

What is a method in a code?

A method is a block of code which only runs when it is called. You can pass data, known as parameters, into a method. Methods are used to perform certain actions, and they are also known as functions.

Can a method have class name?

Yes, It is allowed to define a method with the same name as that of a class. There is no compile-time or runtime error will occur.


4 Answers

In Python 3.x, you can simply use __class__.__name__. The __class__ name is mildly magic, and not the same thing as the __class__ attribute of self.

In Python 2.x, there is no good way to get at that information. You can use stack inspection to get the code object, then walk the class hierarchy looking for the right method, but it's slow and tedious and will probably break when you don't want it to. You can also use a metaclass or a class decorator to post-process the class in some way, but both of those are rather intrusive approaches. And you can do something really ugly, like accessing self.__nonexistant_attribute, catching the AttributeError and extracting the class name from the mangled name. None of those approaches are really worth it if you just want to avoid typing the name twice; at least forgetting to update the name can be made a little more obvious by doing something like:

class C:
    ...
    def report_name(self):
        print C.__name__
like image 169
Thomas Wouters Avatar answered Nov 15 '22 13:11

Thomas Wouters


inspect.getmro gives you a tuple of the classes where the method might come from, in order. As soon as you find one of them that has the method's name in its dict, you're done:

for c in inspect.getmro(self.__class__):
    if 'test' in vars(c): break
return c.__name__
like image 26
Alex Martelli Avatar answered Nov 15 '22 11:11

Alex Martelli


Use __dict__ of class object itself:

class A(object):
    def foo(self):
        pass

class B(A):
    pass

def find_decl_class(cls, method):
    if method in cls.__dict__:
        return cls
    for b in cls.__bases__:
        decl = find_decl_class(b, method)
        if decl:
            return decl

print 'foo' in A.__dict__
print 'foo' in B.__dict__
print find_decl_class(B, 'foo').__name__

Will print True, False, A

like image 37
nkrkv Avatar answered Nov 15 '22 13:11

nkrkv


You can use (abuse?) private name mangling to accomplish this effect. If you look up an attribute on self that starts with __ from inside a method, python changes the name from __attribute to _classThisMethodWasDefinedIn__attribute.

Just somehow stash the classname you want in mangled-form where the method can see it. As an example, we can define a __new__ method on the base class that does it:

def mangle(cls, attrname):
    if not attrname.startswith('__'):
        raise ValueError('attrname must start with __')
    return '_%s%s' % (cls.__name__, attrname)

class A(object):

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for c in cls.mro():
            setattr(obj, mangle(c, '__defn_classname'), c.__name__)
        return obj

    def __init__(self):
        pass

    def test(self):
        print self.__defn_classname

class B(A):

    def __init__(self):
        A.__init__(self)



a = A()
b = B()

a.test()
b.test()

which prints:

A
A
like image 32
Matt Anderson Avatar answered Nov 15 '22 11:11

Matt Anderson