[英]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
the above statement succeeds.But as per my understanding it should not succeed. 以上陈述成功了。但据我所知,它不应成功。 as two instances of the same class should share different memory locations.
因为同一类的两个实例应该共享不同的内存位置。
Any suggestions are appreciated. 任何建议表示赞赏。
It appears to be a broken, in several places, attempt to implementa limit of 5 instances for a given class, just like a singleton is a limit of one instance for a given class. 尝试在给定类中实现5个实例的限制似乎是一个失败的尝试,就像单例是给定类中一个实例的限制一样。
Moreover, the testing is broken as well. 而且,测试也被破坏了。
The thing is that whenever you try to instantiate a class in Python, it is no different than calling any other object: the __call__
method is run in the class's class - that is, its metaclass. 事实是,每当您尝试在Python中实例化一个类时,它都与调用任何其他对象没有什么不同:
__call__
方法在该类的类(即其元类)中运行。 Ordinarily, the metaclass's __call__
is responsible for calling the class' __new__
and __init__
method - and that is done when super().__call__
is invoked in the example above. 通常,元类的
__call__
负责调用类的__new__
和__init__
方法-在上例中调用super().__call__
时,便完成了此操作。 The value returned by __call__
is then used as the new instance - and there is what is perhaps the most gross error above: in the first 5 times A.__call__
runs, it returns None
. 然后,将
__call__
返回的值用作新实例-上面可能是最严重的错误:在A.__call__
运行的前5次中,它返回None
。
So None is assigned both to a
and to a1
, and None is a singleton - that is why your assertion works. 因此,没有为
a
和a1
都分配None,并且None为单例-这就是您的断言起作用的原因。
Now, if one is to fix that and return the newly created instance whenever len(l) < 5
, that would restrict the total number of instances for all classes that have A
as a metaclass, not just five instances of B
. 现在,如果要解决该问题并在
len(l) < 5
时返回新创建的实例,这将限制所有将A
作为元类的类的实例总数,而不仅是B
五个实例。 Thatis because to work per class, the _l
attribute should be located in the class themselves - the way the code is set above, it will be retrieved from the class'class - which is A._l
- (unless one explicitly creates a _l
attribute in B and other classes) . Thatis因为每类工作,
_l
属性应位于类本身-代码上面设定的方式,它将从class'class检索-这是A._l
- (除非我们明确地创建一个_l
属性在B和其他班级)。
And finally, there is no mechanism to provide a round-robin of the limited created instances on the code above, which is what one can imagine as a thing that could have some utility - instead of selecting an instance, the list with all created instances is returned in raw. 最后,在上面的代码中没有机制可以对有限的已创建实例进行循环查询,这可以想象为可以具有某种实用性的事物-而不是选择实例,而是列出所有已创建实例的列表原始返回。
So, before posting a working version of this, let's get one thing straight: you ask 因此,在发布此版本的工作版本之前,我们先弄清楚一件事:您问
two instances of the same class should share different memory locations.
同一类的两个实例应该共享不同的内存位置。
Yes - but what actually creates the two different isntances is type.__call__
. 是的-但是实际上创建两个不同的对象的是
type.__call__
。 The A.__call__
there won't be calling this everytime, and whatever object it returns will be used in place of the new instance. A.__call__
不会每次都调用此方法,它返回的任何对象都将用于代替新实例。 If it returns a pre-existing object, that is what the caller code gets. 如果它返回一个预先存在的对象,那就是调用者代码得到的。
Rewording it so there are no doubts: instantiating classes is no different than calling any other object in Python: the associated object's __call__
is run and returns a value. 重新命名它,因此毫无疑问:实例化类与在Python中调用任何其他对象没有什么不同:关联对象的
__call__
运行并返回一个值。
Now, if the idea is to have a limit of instances of classes, and balance the instances being retrieved, the metaclass code could be so: 现在,如果要限制类的实例并平衡要检索的实例,则元类代码可以是:
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.