![](/img/trans.png)
[英]Why is the code below return an empty list.I could not understand the reason of it so if someone can help me about that , that will make me happy
[英]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
以上陈述成功了。但据我所知,它不应成功。 因为同一类的两个实例应该共享不同的内存位置。
任何建议表示赞赏。
尝试在给定类中实现5个实例的限制似乎是一个失败的尝试,就像单例是给定类中一个实例的限制一样。
而且,测试也被破坏了。
事实是,每当您尝试在Python中实例化一个类时,它都与调用任何其他对象没有什么不同: __call__
方法在该类的类(即其元类)中运行。 通常,元类的__call__
负责调用类的__new__
和__init__
方法-在上例中调用super().__call__
时,便完成了此操作。 然后,将__call__
返回的值用作新实例-上面可能是最严重的错误:在A.__call__
运行的前5次中,它返回None
。
因此,没有为a
和a1
都分配None,并且None为单例-这就是您的断言起作用的原因。
现在,如果要解决该问题并在len(l) < 5
时返回新创建的实例,这将限制所有将A
作为元类的类的实例总数,而不仅是B
五个实例。 Thatis因为每类工作, _l
属性应位于类本身-代码上面设定的方式,它将从class'class检索-这是A._l
- (除非我们明确地创建一个_l
属性在B和其他班级)。
最后,在上面的代码中没有机制可以对有限的已创建实例进行循环查询,这可以想象为可以具有某种实用性的事物-而不是选择实例,而是列出所有已创建实例的列表原始返回。
因此,在发布此版本的工作版本之前,我们先弄清楚一件事:您问
同一类的两个实例应该共享不同的内存位置。
是的-但是实际上创建两个不同的对象的是type.__call__
。 A.__call__
不会每次都调用此方法,它返回的任何对象都将用于代替新实例。 如果它返回一个预先存在的对象,那就是调用者代码得到的。
重新命名它,因此毫无疑问:实例化类与在Python中调用任何其他对象没有什么不同:关联对象的__call__
运行并返回一个值。
现在,如果要限制类的实例并平衡要检索的实例,则元类代码可以是:
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]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.