Raf*_*l T 2 python containers iterator
什么是最简单(也是最优雅)的方法,以确定in运算符是否可以在python中使用?如果我打开一个python shell并输入:
"" in 2
Run Code Online (Sandbox Code Playgroud)
它打印:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'int' is not iterable
Run Code Online (Sandbox Code Playgroud)
根据python-docs,一个iterable是:
容器.iter()
返回一个迭代器对象.该对象需要支持下面描述的迭代器协议.如果容器支持不同类型的迭代,则可以提供其他方法来专门请求这些迭代类型的迭代器.(支持多种迭代形式的对象的示例是支持广度优先和深度优先遍历的树结构.)此方法对应于Python/C API中Python对象的类型结构的tp_iter槽.
所以
hasattr([], "__iter__") and hasattr({}, "__iter__")
Run Code Online (Sandbox Code Playgroud)
true按预期返回,但是
hasattr("test_string", "__iter__")
Run Code Online (Sandbox Code Playgroud)
返回false.但我可以使用
"test" in "test_string"
Run Code Online (Sandbox Code Playgroud)
没有任何问题.
优雅我指的是不使用try-except解决方案
迭代器协议实际上不需要支持类型__iter__.它需要一个类型,无论是支持__iter__,或__getitem__从0开始连续的整数参数请参阅iter对于这个在文档的最好解释功能.
因此,hasattr(x, "__iter__")如果测试某些东西是否可迭代,会给你漏报.
那么,你怎么能这样做?好吧,即使你不喜欢它,正确的方法是:
try:
i = iter(x)
except TypeError:
# not iterable
Run Code Online (Sandbox Code Playgroud)
另外,请注意,作为hasattr解释的文档:
这是通过调用
getattr(object, name)和查看它是否引发异常来实现的.
所以,实际上,你根本就没有避免异常; 你只是想出一个更复杂的方法来提出异常并将这个事实隐藏起来.
但与此同时,迭代首先是一个红色的鲱鱼.的in操作者与所述实施__contains__方法.没有定义__contains__方法的容器类型将回退到迭代和比较,但不需要类型来实现它.你可以拥有__contains__比迭代更快的速度(与dict和一样set); 你甚至可以成为一个不可迭代的容器.(注意,collections模块ABC具有单独的Container和Iterable基础;两者都不依赖于另一个.)
所以,如果你真的想在没有任何异常处理的情况下这样做,你怎么能这样做?
那么,你必须检查至少下列之一是真的:
x有一个__contains__方法.x有一个__iter__方法.x有一个__getitem__方法,当用数字调用时0,要么成功返回,要么加注IndexError.即使您接受最后一个不可能在没有实际尝试用数字调用它的情况下进行测试而0只是假设具有__getitem__"足够接近",那么如何在不依赖异常的情况下对此进行测试?
你真的不能.您可以,例如,迭代dir(x),但这不适用于__contains__动态定义的类,例如,在__getattr__委托给的方法中self.real_sequence.
并且,即使你可以,如果你有一个定义__contains__为不参数的类,会发生什么?属性在那里,但in仍然会提出一个TypeError.
所有这些都忽略了(依赖于实现的)规则,在这些规则上,在对象上查找特殊方法以及在类型本身上查找哪些特殊方法.例如,在CPython 2.7中:
>>> class C(object): pass
>>> c = C()
>>> c.__contains__ = lambda self, x: return True
>>> hasattr(c, '__contains__')
True
>>> c.__contains__(2)
True
>>> 2 in c
TypeError: argument of type 'C' is not iterable
Run Code Online (Sandbox Code Playgroud)
尝试除了是正确和优雅的方式.
首先,是否a in b会引发异常与否取决于两个 A和B,而不仅仅是单独的B.
另一个问题是有多种方法in可行.以下是支持in但不支持迭代的对象示例:
>>> class EvenNumbers(object):
... def __contains__(self, n):
... return n % 2 == 0
...
>>> even_numbers = EvenNumbers()
>>> 4 in even_numbers
True
>>> 5 in even_numbers
False
>>> for even_number in even_numbers:
... pass
...
TypeError: 'EvenNumbers' object is not iterable
Run Code Online (Sandbox Code Playgroud)
这是一个支持迭代的对象示例,但没有定义__contains__:
>>> import itertools as it
>>> even_numbers = (2*n for n in it.count())
>>> 4 in even_numbers
True
>>> even_numbers.__contains__
AttributeError: 'generator' object has no attribute '__contains__'
Run Code Online (Sandbox Code Playgroud)
因此,为了实现有效的LBYL实现,您必须考虑a in b可以工作(或不工作)的每种可能的方法.我这里只列出了一对,还有其他几个.你会发现你的代码变得非常冗长和丑陋,并且最终会意识到try/except是一直阻力最小的路径!
| 归档时间: |
|
| 查看次数: |
181 次 |
| 最近记录: |