【问题标题】:How to get the current Python class name in __init__ regardless of the class of "self"?无论“self”类如何,如何在 __init__ 中获取当前 Python 类名?
【发布时间】:2017-11-26 02:36:39
【问题描述】:

我有很多类都有这样的代码。每个__init__ 启动一个线程和一个带有类名的记录器。如何在__init__ 中以字符串形式获取当前类的名称?注意self可能不是当前类的一个实例,所以下面的不是很万无一失。

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)

更新:

澄清一下:

  • 我想要定义的类的名称,而不是 传入的对象。
  • 我不想硬编码类的名称。
  • 我想让从一个脚本中复制/粘贴示例变得容易
    另一个,唯一类名的提及越少越好。 (继承并不是很有效,因为有足够的自定义 差异使其尴尬。却不小心以名离开 错误的类是一个很难找到的错误。)

【问题讨论】:

  • 在什么情况下__init__方法的self不是是当前类的实例?
  • @CoryKramer 例如,您定义了def __init__(potato, self='haha!'): ...
  • @wim 我认为假设self 是“方法的第一个参数”的简写是公平的
  • 那么,鉴于您在 Rob 的回答中的评论,仅 硬编码 __init__ 中的名称有什么问题?

标签: python class


【解决方案1】:

您可以这样检索一个对象的类的名称:

obj.__class__.__name__

例子:

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

class Derived(SomeClass):
    pass

x = SomeClass()
y = Derived()

结果:

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

【讨论】:

  • 您的意思是self 将始终引用当前正在执行的类的实例吗?
  • 没有。事实上,我演示了self 不是当前类的实例,而是派生类的实例。
  • 嗯,当我说“当前班级”时,这就是我的意思。是的,我确实这么说有些奇怪。
  • 是的。但这失败了,因为在这两种情况下我都想要 SomeClass 名称。也就是我要的是定义代码的类的名字,而不是传入的实例的类。
  • @QuantumMechanic 你需要在你的实际问题中消除“当前正在执行的类”的歧义,否则人们会以各种不同的方式解释它。
【解决方案2】:

在 Python 3 中这很简单,我们可以使用 __class__ 单元格变量来获取当前类。

在 Python 2 中,我们可以通过使用元类在函数全局作用域中注入类的名称并稍后清理它来实现类似的效果。

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()

输出:

B
Inside B
A
Inside A

要将__class__ 变量作为类对象本身,我们可以做一些更改:

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

现在的输出是:

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

【讨论】:

  • 不错。你能解释一下将注入逻辑放在wrapper中的原因吗?
  • 不错。代码比我预期的要多,但在我看来可以彻底干净地解决问题。
  • 后续:这个可以泛化成任何函数的名字吗?这里是func,但目标函数可能会有所不同(想到__init__)。
  • @QuantumMechanic func 只是我在装饰器中使用的一个变量名,它适用于任何函数。
  • @juanpa.arrivillaga 正在考虑尽可能地保持与 Python 3 类似的东西,因此使用了 __class__ 变量。添加了另一个版本,其中 __class__ 现在指向类本身。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-25
相关资源
最近更新 更多