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
上述陈述成功。但根据我的理解,它不应该成功。因为同一个类的两个实例应共享不同的内存位置。
任何建议都表示赞赏。
答案 0 :(得分:0)
在某些地方,它似乎是一个破碎的,试图为给定的类实现5个实例的限制,就像单例是给定类的一个实例的限制一样。
此外,测试也被打破了。
问题在于,每当你尝试在Python中实例化一个类时,它与调用任何其他对象没什么不同:__call__
方法在类的类中运行 - 也就是说,它的元类。通常,元类的__call__
负责打电话给班级&#39; __new__
和__init__
方法 - 在上面的示例中调用super().__call__
时完成。然后,__call__
返回的值将用作新实例 - 并且上面可能存在最严重的错误:在前5次A.__call__
次运行中,它返回None
。
所以None分配给a
和a1
,而None是单身 - 这就是你的断言有效的原因。
现在,如果要修复它并在len(l) < 5
时返回新创建的实例,那将限制具有A
作为元类的所有类的实例总数,而不仅仅是五个实例B
。那是因为要按类进行工作,_l
属性应该位于类本身 - 上面设置代码的方式,它将从类的类中检索 - 这是A._l
- (除非在B和其他类中明确创建_l
属性)。
最后,没有机制可以在上面的代码中提供有限创建实例的循环,这是人们可以想象的可以有一些实用的东西 - 而不是选择一个实例,列表与所有创建的实例都以raw格式返回。
所以,在发布这个版本的工作版之前,让我们直截了当地说:你问
同一个类的两个实例应该共享不同的内存 位置。
是的 - 但是实际创造两种不同的意义的是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]