关于python:类的实例作为同一类的类成员

Instance of class as class member of the same class

创建类成员(该类的实例)的最"Python式"方法是什么?也就是这样:

1
2
3
4
5
6
7
class MyClass:

    # error! (and this is to be expected as MyClass is not yet fully defined)
    instance_as_member = MyClass()

    def __init__(self):
        print("MyClass instance created!")

可以通过在类定义之后添加成员来解决此问题:

1
2
3
class MyClass:
    ...
MyClass.instance_as_member = MyClass()

但这似乎有点错误;当然,应该在该类中定义类成员。

有更好的方法吗?


这不是"有点错"——这是一种简单而明显的方法。一行代码,每个人都可以阅读,并且很明显"它是什么"。

但它仍然不"感觉"优雅。因此,如果您不想仅仅为了外观而这样做,并且可能,如果有许多这样的类,不重复代码,那么可以使用类修饰器来完成:

1
2
3
4
5
6
7
8
9
10
def instance_as_member(attr_name='instance_as_member', *init_args, **init_kwargs):
    def decorator(cls):
        instance = cls(*init_args, **init_kwargs)
        setattr(cls, attr_name, instance)
        return cls
    return decorator

@instance_as_member()
class MyClass:
    ...

装饰师可以这样做,因为他们让CLS在完全创建后被修改为参数。对于元类来说,这是很困难的,因为类实例化的某些步骤,如调用__init_subclass__是在可以重写的任何显式元类方法之后执行的,因此,在试图在元类初始化方法(__init____new__或元类'__call__内创建实例时可能会遇到问题。


虽然类没有在主体中定义,但它是在__init__中定义的。

但是,存在一个问题,即在__init__中实例化类是递归的,因此您需要跟踪instance_as_member是否已经生成。

类似于以下作品:

1
2
3
4
5
6
7
8
9
10
11
12
class Foo:
    member_added = False
    instance_as_member = None

    def __init__(self):
        if not Foo.member_added:
            Foo.member_added = True
            Foo.instance_as_member = Foo()

print(Foo.instance_as_member)
Foo()
print(Foo.instance_as_member)

这将导致:

None
<__main__.Foo object at 0x7fcd9f68f3c8>

(至于这是不是一个好主意是一个完全不同的问题!)


您似乎需要setUp类型的功能。下面是一种从类内执行此操作的"稍微脏一点"方法:设置一个类变量标志来表示类的第一次使用。在实例化时检查该标志;如果这是第一次访问,则清除该标志并运行设置方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyClass:

    first_touch = True

    def __init__(self, id=0):

        if MyClass.first_touch:
            MyClass.setUp(id)
        print("MyClass instance created!", id)

    @classmethod
    def setUp(self, id):
        MyClass.first_touch = False
        MyClass.instance_as_member = MyClass(-999)


print("Start")
local_obj = MyClass(1)
local_obj = MyClass(2)
local_obj = MyClass(3)