Quantum Mechanic
Quantum Mechanic

Reputation: 662

How to get the current Python class name in __init__ regardless of the class of "self"?

I have a number of classes with code like this. Each __init__ starts a thread and a logger with the name of the class. How do I get the name of the current class in its own definition, as a string, inside __init__? Note that self may not be an instance of the current class, so the following is not quite foolproof.

from threading import Thread
import logging

def myClassName(myclass):
    myclass._class_name = myclass.__name__
    return myclass

@myClassName
class SomeClass(object):
    def __init__(self):
        class_name = type(self)._class_name
        print "My class name in __init__ is", class_name
        self.thread = Thread(name=class_name)
        self.logger = logging.getLogger(class_name)

Update:

To clarify:

Upvotes: 12

Views: 16926

Answers (2)

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 251116

In Python 3 this is pretty straight forward, we can use the __class__ cell variable to get the current class.

In Python 2 we can achieve something similar by injecting class's name in functions globals scope using a metaclass and later cleaning it up.

from functools import wraps
from types import FunctionType


def decorate(func, class_name):
    @wraps(func)
    def wrapper(*args, **kwargs):
        sentinel = object()
        actual_value = func.__globals__.get('__class__', sentinel)
        func.__globals__['__class__'] = class_name
        try:
            result = func(*args, **kwargs)
        finally:
            if actual_value is sentinel:
                del func.__globals__['__class__']
            else:
                func.__globals__['__class__'] = actual_value
        return result
    return wrapper


class Meta(type):
    def __new__(cls, name, bases, attrs):
        for k, v in attrs.items():
            if isinstance(v, FunctionType):
                attrs[k] = decorate(v, name)
        return type.__new__(cls, name, bases, attrs)


class A:
    __metaclass__ = Meta
    def func(self):
        print(__class__)
        print('Inside A')


class B(A):
    def func(self):
        print(__class__)
        print('Inside B')
        super(B, self).func()


B().func()

Output:

B
Inside B
A
Inside A

To get the __class__ variable as the class object itself we can make few changes:

def decorate(func, cls):
    @wraps(func)
    def wrapper(*args, **kwargs):
        sentinel = object()
        actual_value = func.__globals__.get('__class__', sentinel)
        func.__globals__['__class__'] = cls
        try:
            result = func(*args, **kwargs)
        finally:
            if actual_value is sentinel:
                del func.__globals__['__class__']
            else:
                func.__globals__['__class__'] = actual_value
        return result
    return wrapper


class Meta(type):
    def __new__(cls, name, bases, attrs):
        cls = type.__new__(cls, name, bases, attrs)
        for k, v in attrs.items():
            if isinstance(v, FunctionType):
                setattr(cls, k, decorate(v, cls))
        return cls

Now output would be:

<class '__main__.B'>
Inside B
<class '__main__.A'>
Inside A

Upvotes: 9

Robᵩ
Robᵩ

Reputation: 168766

You can retrieve the name of the class of an an object thus:

obj.__class__.__name__

Example:

class SomeClass(object):
    def __init__(self):
        print("I am a %s"%self.__class__.__name__)

class Derived(SomeClass):
    pass

x = SomeClass()
y = Derived()

Result:

$ python x.py
I am a SomeClass
I am a Derived

Upvotes: 12

Related Questions