在class属性中引用类名的最佳方法是什么?

Pai*_*Air 9 python

不幸的是,在Python中,类属性不能引用其类名.以下引发了一个名称错误:

class Foo(object):
    bar = Foo
Run Code Online (Sandbox Code Playgroud)

在class属性必须引用其类名的情况下(在我的情况下,它是库的验证方案的一部分),最清楚的方法是什么?

以下是我目前的尝试:

class Foo(object):
    bar = None
Foo.bar = Foo
Run Code Online (Sandbox Code Playgroud)

这是有效的,但即使使用注释也可能会引起混淆,因为您希望在声明时初始化类属性,而不是在声明类之后.

有没有人有更好的解决方法?

Bre*_*bel 5

使用元类来自动设置它。

def my_meta(name, bases, attrs):
    cls = type(name, bases, attrs)
    cls.bar = cls
    return cls


class Foo(object):
    __metaclass__ = my_meta


>>> print Foo.bar
<class '__main__.Foo'>
Run Code Online (Sandbox Code Playgroud)


mar*_*eau 2

您可以定义一个类装饰器,用正在定义的类替换占位符字符串:

def fixup(cls):
    placeholder = '@' + cls.__name__
    for k,v in vars(cls).items():
        if v == placeholder:
            setattr(cls, k, cls)
    return cls

@fixup
class Foo(object):
    bar = '@Foo'

print('Foo.bar: {!r}'.format(Foo.bar))  # -> Foo.bar: <class '__main__.Foo'>
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用__init_subclass__()Python 3.6 中引入的特殊方法来创建基类,然后从中派生您的类,而不是通用的object

class Base(object):
    def __init_subclass__(cls, /, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.bar = cls

class Foo(Base):
    pass

print('Foo.bar: {!r}'.format(Foo.bar))  # -> Foo.bar: <class '__main__.Foo'>
Run Code Online (Sandbox Code Playgroud)