类实例作为静态属性

3

Python 3不允许你在类的主体内引用该类本身(除非在方法中):

class A:
    static_attribute = A()

    def __init__(self):
        ...

在第二行代码中,因为‘A’未定义,所以引发了一个NameError错误。

替代方案

我很快找到了一种解决方法:

class A:
    @property
    @classmethod
    def static_property(cls):
        return A()

    def __init__(self):
        ...

尽管这并不完全相同,因为它每次返回一个不同的实例(您可以通过将该实例保存到静态变量中来防止此问题出现)。
是否有更简单和/或更优雅的替代方案?
编辑:我已将关于此限制原因的问题移至单独的问题

如果您提供一个实例化类作为同一类属性的情况,这将是一个更有趣的问题。与Java不同,您可以拥有存在于类定义之外的对象。 - chepner
4个回答

4
表达式 A() 必须在类 A 被定义后才能运行。在您的第一个代码块中,当您尝试执行 A() 时,A 的定义还不完整。
以下是一种更简单的替代方法:
class A:
    def __init__(self):
        ...

A.static_attribute = A()

3
当您定义一个类时,Python会立即执行类定义中的代码。请注意,这与定义函数不同,Python编译了代码但不执行它。
因此,以下代码将产生错误:
class MyClass(object):
    a = 1 / 0

但是这个不会影响:
def my_func():
    a = 1 / 0

A类的定义体中,A尚未被定义,因此只有在其定义完成后才能引用它。
有几种方法可以实现您要求的功能,但我不清楚为什么这首先会有用,如果您可以提供有关您的用例的更多详细信息,那么推荐哪个方案将更容易。
最简单的方法是khelwood发布的内容:
class A(object):
    pass
A.static_attribute = A()

由于这是修改类的创建方式,使用元类可能是合适的:
class MetaA(type):
    def __new__(mcs, name, bases, attrs):
        cls = super(MetaA, mcs).__new__(mcs, name, bases, attrs)
        cls.static_attribute = cls()
        return cls


class A(object):
    __metaclass__ = MetaA

或者您可以使用描述符来实现延迟创建实例,或者如果您想进一步自定义访问,则可以使用它:

class MyDescriptor(object):
    def __get__(self, instance, owner):
        owner.static_attribute = owner()
        return owner.static_attribute


class A(object):
    static_attribute = MyDescriptor()

1
使用property装饰器是可行的方法,但需要像这样进行操作:
class A:
    _static_attribute = None

    @property
    def static_attribute(self):
        if A._static_attribute is None:
            A._static_attribute = A()
        return A._static_attribute

    def __init__(self):
        pass

a = A()
print(a.static_attribute)  # -> <__main__.A object at 0x004859D0>
b = A()
print(b.static_attribute)  # -> <__main__.A object at 0x004859D0>

0
你可以使用类装饰器:
def set_static_attribute(cls):
    cls.static_attribute = cls()
    return cls

@set_static_attribute    
class A:
    pass

现在:
>>>> A.static_attribute
<__main__.A at 0x10713a0f0>

将装饰器应用于类之上比在一个可能很长的类定义后设置static_attribute更加明确。所应用的装饰器“属于”类定义。因此,如果您在源代码中移动类,您更可能会沿着它一起移动,而不是在类外部进行额外的属性设置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接