你所需要做的就是实现__length_hint__方法,这个方法是迭代器上的内置方法(不是生成器),正如你上面看到的那样,并且还支持动态长度更改。但是,正如他的名字那样,这只是一个提示(hint),并不能保证完全准确:对于列表迭代器,可以得到准确的结果,但是对于其他迭代器则不确定。但是即使它不准确,它也可以帮我们获得需要的信息,正如PEP 424中解释的那样。
length_hint must return an integer (else a TypeError is raised) or NotImplemented, and is not required to be accurate. It may return a value that is either larger or smaller than the actual size of the container. A return value of NotImplemented indicates that there is no finite length estimate. It may not return a negative value (else a ValueError is raised).
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
else:
return cls.__instance
class Logger(metaclass=Singleton):
def __init__(self):
print("Creating global Logger instance")
Singleton类拥有一个私有__instance——如果没有,它会被创建并赋值,如果它已经存在,它只会被返回。
假设有一个类,你想创建它的一个实例而不调用__init__。__new__ 方法可以帮助解决这个问题:
class Document:
def __init__(self, text):
self.text = text
bare_document = Document.__new__(Document)
print(bare_document.text)
# AttributeError: 'Document' object has no attribute 'text'
setattr(bare_document, "text", "Text of the document")
在某些情况下,我们可能需要绕过创建实例的通常过程,上面的代码演示了如何做到这一点。我们不调用Document(…),而是调用Document.__new__(Document),它创建一个裸实例,而不调用__init__。因此,实例的属性(在本例中为text)没有初始化,所欲我们需要额外使用setattr函数赋值(它也是一个魔法的方法__setattr__)。
为什么要这么做呢。因为我们可能会想要替代构造函数,比如:
class Document:
def __init__(self, text):
self.text = text
@classmethod
def from_file(cls, file): # Alternative constructor
d = cls.__new__(cls)
# Do stuff...
return d
这里定义from_file方法,它作为构造函数,首先使用__new__创建实例,然后在不调用__init__的情况下配置它。
下一个与元编程相关的神奇方法是__getattr__。当普通属性访问失败时调用此方法。这可以用来将对缺失方法的访问/调用委托给另一个类:
class String:
def __init__(self, value):
self._value = str(value)
from pathlib import Path, PurePath
modules = list(Path(__file__).parent.glob("*.py"))
print([PurePath(f).stem for f in modules if f.is_file() and not f.name == "__init__.py"])
# ['module_one', 'module_two', 'module_three']
最后一个我重要的属性是的是__debug__。它可以用于调试,但更具体地说,它可以用于更好地控制断言:
# example.py
def func():
if __debug__:
print("debugging logs")