我不明白下面这段代码的行为。有人可以帮我吗
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 被分配给 a
和 a1
,并且 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]
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 被分配给 a
和 a1
,并且 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]