有没有办法在不对键施加约束的情况下禁用定义 __getitem__ 方法的 类 的迭代?

Is there a way to disable iteration for classes that define a __getitem__ method without putting constraints on the key?

作为我起点的class如下:

class Test:
    def __getitem__(self, key):
        global frame
        frame = inspect.currentframe()
        if key > 9:
            raise KeyError
        return key

我的想法是使用 frame.f_back 发现迭代器是为实例自动创建的,如下例所示:

for x in Test():
    x

在 运行 这两个之后并查看 frame.f_back__getitem__ 是否可以获得足够的信息来检测它是否被从任何地方调用并不明显 "iterator" 正在与之交互。最简单的解决方案是使容器从 1 而不是 0 开始以访问其内容,或者甚至可能在将密钥传递给函数之前强制将其包装在列表中,如下所示:

>>> class Test:
    def __getitem__(self, key):
        if not isinstance(key, list):
            raise TypeError
        if len(key) != 1:
            raise ValueError
        key = key.pop()
        if not isinstance(key, int):
            raise TypeError
        if not 0 <= key < 10:
            raise KeyError
        return key


>>> for x in Test():
    x


Traceback (most recent call last):
  File "<pyshell#42>", line 1, in <module>
    for x in Test():
  File "<pyshell#39>", line 4, in __getitem__
    raise TypeError
TypeError
>>> Test()[[5]]
5
>>> 

有没有一种方法 __getitem__ 可以知道它被自动用作迭代器并引发异常以防止这种使用?

相关: Why does defining __getitem__ on a class make it iterable in python?

如果目标是阻止对象成为可迭代对象,您可以在 __iter__ 方法上强制出错:

class Test:
    def __getitem__(self, key):
        if key > 9:
            raise KeyError
        return key
    def __iter__(self):
        raise TypeError('Object is not iterable')

测试运行:

>>> t = Test()
>>> for x in t:
    print(x)

Traceback (most recent call last):
  File "<pyshell#126>", line 1, in <module>
    for x in t:
  File "<pyshell#122>", line 7, in __iter__
    raise TypeError('Object is not iterable')
TypeError: Object is not iterable

但是 __getitem__ 仍然有效:

>>> t[0]
0
>>> t[1]
1
>>> t[10]
Traceback (most recent call last):
  File "<pyshell#129>", line 1, in <module>
    t[10]
  File "<pyshell#122>", line 4, in __getitem__
    raise KeyError
KeyError

执行此操作的方法是同时实施 __getitem____iter__ 方法。 当尝试访问单个元素时 __getitem__ 将被触发,当尝试迭代时 __iter__ 将被调用并引发异常。

class Test():
    def __getitem__(self, k):
        return k

    def __iter__(self):
        raise TypeError