简体   繁体   English

我不明白以下代码的行为。 有人可以帮我吗

[英]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. 因此,没有为aa1都分配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.

相关问题 为什么下面的代码返回一个空列表。我不明白它的原因,所以如果有人可以帮助我,那会让我高兴 - 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 我想设置一个密码来关闭discord.py上的机器人,我不知道怎么写代码。 有人帮我写代码 - I want to set a password to shut down a bot on discord.py, and I don't know how to write the code. Someone help me witht the code 有人可以帮助我了解此Python代码如何工作 - Can someone help me understand how this Python code works 有人可以帮我理解什么.index 在这段代码中做了什么吗? - Can someone help me understand what .index is doing in this code? 有人可以帮我优化下面的 python 代码吗,我在某些测试用例中遇到超时异常 - Can someone help me in optimising the below python code, I am getting timeout exception for some of the test cases 有人可以帮我理解python中的循环吗 - Can someone help me understand this for loop in python 有人可以帮我理解这个逻辑吗? - Can someone help me understand the logic of this? 有人可以帮我理解这个错误: - Can someone help me understand this error: 有人可以帮我理解 class 变量吗 - Can someone help me understand class variables 我不懂Django的测试,你能帮我吗? - I don't understand tests in Django, Can you help me please?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM