Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

class

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:

  • I want the name of the class being defined, not the class of the object passed in.
  • I don't want to hard code the name of the class.
  • I want to make it easy to copy/paste an example from one script to
    another, and the fewer mentions of the unique class name, the better. (Inheritance isn't really efficient, as there are enough custom differences to make it awkward. But accidentally leaving in the name of the wrong class is a hard bug to find.)
like image 764
Quantum Mechanic Avatar asked Jun 22 '17 17:06

Quantum Mechanic


People also ask

How do I find the name of a class in Python?

To get the class name of an instance in Python: Use the type() function and __name__ to get the type or class of the Object/Instance. Using the combination of the __class__ and __name__ to get the type or class of the Object/Instance.

Is __ init __ called automatically?

Use the __init__() method to initialize the object's attributes. The __init__() doesn't create an object but is automatically called after the object is created.

What is __ init __ () in Python?

The __init__ method is the Python equivalent of the C++ constructor in an object-oriented approach. The __init__ function is called every time an object is created from a class. The __init__ method lets the class initialize the object's attributes and serves no other purpose. It is only used within classes.

What does super () __ Init__ do?

Understanding Python super() with __init__() methods It is known as a constructor in Object-Oriented terminology. This method when called, allows the class to initialize the attributes of the class. The super() function allows us to avoid using the base class name explicitly.


2 Answers

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
like image 186
Robᵩ Avatar answered Oct 18 '22 02:10

Robᵩ


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
like image 41
Ashwini Chaudhary Avatar answered Oct 18 '22 03:10

Ashwini Chaudhary