简体   繁体   中英

Class instance as static attribute

Python 3 doesn't allow you to reference a class inside its body (except in methods):

class A:
    static_attribute = A()

    def __init__(self):
        ...

This raises a NameError in the second line because 'A' is not defined .

Alternatives

I have quickly found one workaround:

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

    def __init__(self):
        ...

Although this isn't exactly the same since it returns a different instance every time (you could prevent this by saving the instance to a static variable the first time).

Are there simpler and/or more elegant alternatives?

EDIT: I have moved the question about the reasons for this restriction to a separate question

The expression A() can't be run until the class A has been defined. In your first block of code, the definition of A is not complete at the point you are trying to execute A() .

Here is a simpler alternative:

class A:
    def __init__(self):
        ...

A.static_attribute = A()

When you define a class, Python immediately executes the code within the definition. Note that's different than defining a function where Python compiles the code, but doesn't execute it.

That's why this will create an error:

class MyClass(object):
    a = 1 / 0

But this won't:

def my_func():
    a = 1 / 0

In the body of A 's class definition, A is not yet defined, so you can't reference it until after it's been defined.

There are several ways you can accomplish what you're asking, but it's not clear to me why this would be useful in the first place, so if you can provide more details about your use case, it'll be easier to recommend which path to go down.

The simplest would be what khelwood posted:

class A(object):
    pass
A.static_attribute = A()

Because this is modifying class creation, using a metaclass could be appropriate:

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

Or you could use descriptors to have the instance lazily created or if you wanted to customize access to it further:

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


class A(object):
    static_attribute = MyDescriptor()

Using the property decorator is a viable approach, but it would need to be done something like this:

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>

You can use a class decorator:

def set_static_attribute(cls):
    cls.static_attribute = cls()
    return cls

@set_static_attribute    
class A:
    pass

Now:

>>>> A.static_attribute
<__main__.A at 0x10713a0f0>

Applying the decorator on top of the class makes it more explicit than setting static_attribute after a potentially long class definition. The applied decorator "belongs" to the class definition. So if you move the class around in your source code you will more likely move it along than an extra setting of the attribute outside the class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM