[英]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.