同一类的类成员的类实例

19 浏览
0 Comments

同一类的类成员的类实例

如何用最“Pythonic”的方式创建一个类成员,该成员是该类的实例?例如:\n

class MyClass:
    # 错误!(这是可以预料的,因为MyClass还没有完全定义)
    instance_as_member = MyClass()
    def __init__(self):
        print("MyClass实例已创建!")

\n可以通过在类定义之后添加一个成员来解决这个问题:\n

class MyClass:
    ...
MyClass.instance_as_member = MyClass()

\n但这似乎有点不对;自然而然,类成员应该在该类中定义。\n有更好的方法吗?

0
0 Comments

在上述内容中,提到了一个问题,即在类的方法__init__中实例化同一个类的对象会导致递归调用。为了解决这个问题,需要跟踪instance_as_member是否已经创建过。

解决方法如下:

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>

以上解决方法确实可以实现在类的成员中存储一个该类的实例对象,但是需要注意的是,这是否是一个好的设计思路是另一个完全不同的问题。

0
0 Comments

在上述代码中,存在一个问题:在同一个类的类成员中实例化该类的实例。这可能导致无限递归调用,最终导致堆栈溢出错误。这个问题的根本原因是在类成员中实例化类的实例会导致无限循环调用。

解决这个问题的方法是使用静态方法或类方法来代替在类成员中实例化类的实例。这样做可以避免无限递归调用,从而解决了问题。

以下是修改后的代码示例:

class MyClass:
    first_touch = True
    
    def __init__(self, id=0):
        if MyClass.first_touch:
            MyClass.setUp(id)
        print("MyClass instance created!", id)
    
    @staticmethod
    def setUp(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)

通过将 `setUp` 方法定义为静态方法(使用 `@staticmethod` 装饰器),我们可以在不实例化类的情况下调用它。这样就避免了无限递归调用,并解决了问题。

总结起来,这个问题的原因是在同一个类的类成员中实例化该类的实例,解决方法是使用静态方法或类方法来代替在类成员中实例化类的实例。这样可以避免无限递归调用,从而解决了问题。

0
0 Comments

在上述内容中,提到了一个问题:将一个类的实例作为该类的成员。虽然这种做法是合理且简单明了的,但并不优雅。为了解决这个问题,可以使用类装饰器。代码如下:

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:
    ...

使用装饰器可以很好地解决这个问题,因为装饰器可以在类完全创建后作为参数对其进行修改。而使用元类可能会有些麻烦,因为类实例化的某些步骤(如调用`__init_subclass__`)在任何显式的元类方法(可以被重写的方法)之后执行,因此,在元类初始化方法(`__init__`、`__new__`或元元类的`__call__`)中创建实例可能会出现问题。

虽然使用装饰器是个不错的主意,但我认为你的第一条评论是正确的:为了可读性起见,最好在类定义之后进行赋值。

0