如何在没有__len__数据类型定义的对象上添加__len __?

时间:2019-01-18 23:28:23

标签: python python-3.x generator

根据文档,由于以下原因,此方法不起作用:

  

对于自定义类,只有在对对象的类型(而不是在对象的实例字典中)进行定义的情况下,才能保证对特殊方法的隐式调用可以正常工作。该行为是以下代码引发异常的原因:

     
>>> class C:
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
     

https://docs.python.org/3/reference/datamodel.html#special-method-lookup

我曾在没有__len__的函数生成器上尝试过此操作,但我事先知道了它的长度,然后,我尝试用c.__len__ = lambda: 5之类的东西对猴子进行补丁,但它一直说生成器对象没有长度。

这是生成器:

def get_sections(loaded_config_file):
    for module_file, config_parser in loaded_config_file.items():
        for section in config_parser.sections():
            yield section, module_file, config_parser

我正在将生成器(没有长度)传递给另一个函数(但另一个生成器),该函数需要通过调用len()来迭代长度:

  
def sequence_timer(sequence, info_frequency=0):
    i = 0
    start = time.time()
    if_counter = start
    length = len(sequence)
    for elem in sequence:
        now = time.time()
        if now - if_counter < info_frequency:
            yield elem, None
        else:
            pi = ProgressInfo(now - start, float(i)/length)
            if_counter += info_frequency
            yield elem, pi
        i += 1
     

https://github.com/arp2600/Etc/blob/60c5af803faecb2d14b5dd3041254ef00a5a79a9/etc.py

然后,在尝试将__len__属性添加到get_sections时出现错误:

get_sections.__len__ = lambda: calculated_length
for stuff, progress in sequence_timer( get_sections ):
    section, module_file, config_parser = stuff

TypeError: object of type 'function' has no len()

1 个答案:

答案 0 :(得分:4)

您不能将其添加到现有对象中,因此请创建自己的包装类,该包装类具有您控制的类级别的定义:

class KnownLengthIterator:
    def __init__(self, it, length):
        self.it = it
        self.length = int(length)

    def __len__(self):
        return self.length

    def __iter__(self):
        yield from self.it

现在,您只需更改无效的尝试即可将长度设置为:

get_sections.__len__ = lambda: calculated_length

有效地重新包装,使get_sections继续是有效的生成器(yield from将所有迭代行为委派给包装的生成器),同时也公开了长度:

get_sections = KnownLengthIterator(get_sections, calculated_length)

无需更改其他代码。