我不明白下面这段代码的行为。有人可以帮我吗

I don't understand the behavior of this below code. Can someone help me here

class A(type):
    _l = []
    def __call__(cls,*args,**kwargs):
        if len(cls._l)<=5:
            cls._l.append(super().__call__(*args,**kwargs))
        else:
            return cls._l

class B(metaclass=A):
    pass


#testing starts

a=B()
a1=B()

assert a is a1

上面的语句succeeds.But按我的理解应该不会成功。因为相同 class 的两个实例应该共享不同的内存位置。

如有任何建议,我们将不胜感激。

它似乎在几个地方被破坏了,试图为给定 class 实现 5 个实例的限制,就像单例是给定 class 的一个实例限制一样.

此外,测试也被破坏了。

问题在于,每当您尝试在 Python 中实例化一个 class 时,它与调用任何其他对象没有什么不同:__call__ 方法是 运行在 class 的 class 中 - 即它的元 class。通常,metaclass 的 __call__ 负责调用 class' __new____init__ 方法——当 super().__call__在上面的例子中被调用。然后 __call__ 编辑的值 return 被用作新实例 - 上面可能是最严重的错误:在前 5 次 A.__call__ 运行s , 它 returns None

所以 None 被分配给 aa1,并且 None 是一个单例 - 这就是你的断言有效的原因。

现在,如果要解决这个问题并且 return 每当 len(l) < 5 时新创建的实例就会限制所有 class 具有 A 作为元class,而不仅仅是 B 的五个实例。这是因为要按照 class 工作,_l 属性应该位于 class 本身 - 按照上面设置代码的方式,它将从 class' class - 即 A._l -(除非在 B 和其他 classes 中明确创建 _l 属性)。

最后,在上面的代码中,没有提供有限创建实例循环的机制,这是人们可以想象的一种可能具有某种实用性的东西——而不是选择一个实例,包含所有已创建实例的列表是 return 原始格式。

所以,在发布这个的工作版本之前,让我们弄清楚一件事:你问

two instances of the same class should share different memory locations.

是的 - 但实际上创建两个不同实例的是 type.__call__A.__call__ 不会每次都调用它,它 return 的任何对象都将用于代替新实例。如果它 return 是一个预先存在的对象,那就是调用者代码得到的。

改写它以便毫无疑问:实例化 classes 与调用 Python 中的任何其他对象没有什么不同:关联对象的 __call__ 是 运行 并且return一个值。

现在,如果想限制 classes 的实例数,并平衡检索到的实例,metaclass 代码可以是这样的:

MAX_INSTANCES = 5

class A(type):

    def __call__(cls, *args, **kw):
        cls._instances = instances = getattr(cls, "_instances", [])
        cls._index = getattr(cls, "_index", -1)
        if len(instances) < MAX_INSTANCES:
            # Actually creates a new instance:
            instance = super().__call__(cls, *args, **kw)
            instances.append(instance)
        cls._index = (cls._index + 1) % MAX_INSTANCES
        return cls._instances[cls._index]


class B(metaclass=A):
    pass

b_list = [B() for B in range(MAX_INSTANCES * 2)]

for i in range(MAX_INSTANCES):
    assert b_list[i] is b_list[i + MAX_INSTANCES]
    for j in range(1, MAX_INSTANCES):
        assert b_list[i] is not b_list[i + j]