找出in
运算符是否可以在python中使用,最简单(也是最优雅)的方法是什么?
如果我打开一个python shell并输入:
"" in 2
打印:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'int' is not iterable
根据python-docs,可迭代的是:
容器。的 ITER ()
返回一个迭代器对象。该对象需要支持下面描述的迭代器协议。如果容器支持不同 在迭代类型中,可以具体提供其他方法 为这些迭代类型请求迭代器。 (对象的一个例子 支持多种迭代形式的是树结构 支持广度优先和深度优先遍历。)此方法 对应于Python类型结构的tp_iter槽 Python / C API中的对象。
所以
hasattr([], "__iter__") and hasattr({}, "__iter__")
按预期返回true
,但
hasattr("test_string", "__iter__")
返回false。但我可以使用
"test" in "test_string"
没有任何问题。
优雅我指的是不使用try-except解决方案
答案 0 :(得分:9)
迭代器协议实际上并不需要支持__iter__
的类型。它需要一个 支持__iter__
或__getitem__
的类型,其序列整数参数从0开始。有关此问题的最佳解释,请参阅iter
函数。文档。
因此,如果测试某些东西是否可迭代,hasattr(x, "__iter__")
会给你假阴性。
那么 你怎么能这样做呢?好吧,即使你不喜欢它,正确的方法是:
try:
i = iter(x)
except TypeError:
# not iterable
另请注意,正如hasattr
的文档解释:
这是通过调用
getattr(object, name)
并查看是否引发异常来实现的。
所以,实际上,你根本就没有避免例外;你只是想出一个更复杂的方式来提出异常并将这个事实隐藏起来。
但与此同时,迭代首先是一个红色的鲱鱼。 in
运算符使用__contains__
方法实现。没有定义__contains__
方法的容器类型将回退到迭代和比较,但类型不是必需来实现它办法。您可以__contains__
比迭代更快(dict
和set
);你甚至可以成为一个不可迭代的容器。 (请注意,collections
module ABCs具有单独的Container
和Iterable
基础;两者都不依赖于另一个。)
所以,如果你真的想在没有任何异常处理的情况下这样做,你怎么能这样做?
嗯,你必须检查至少有以下一种情况:
x
有__contains__
方法。x
有__iter__
方法。x
有一个__getitem__
方法,当使用数字0
调用时,可以成功返回或提升IndexError
。即使您接受最后一个可能无法在没有实际尝试使用数字0
进行测试的情况下进行测试,并且只是假设__getitem__
是&#34;足够接近& #34;,如何在不依赖异常的情况下对此进行测试?
你真的不能。例如,您可以迭代dir(x)
,但这对于动态定义__contains__
的类不起作用,例如,在委托给__getattr__
的{{1}}方法中
而且,即使你可以,如果你有一个将self.real_sequence
定义为不参数的类,会发生什么?该属性存在,但__contains__
仍然会引发in
。
所有这些都忽略了which special methods are looked up on the object and which on the type itself上的(依赖于实现的)规则。例如,在CPython 2.7中:
TypeError
答案 1 :(得分:7)
尝试除了是正确而优雅的方式。
首先,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
以下是支持迭代的对象示例,但未定义__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__'
因此,为了实现有效的LBYL实现,您必须考虑a in b
可以工作(或不工作)的每种可能方法。我这里只列出了一对,还有其他几个。你会发现你的代码变得非常冗长和丑陋,最终会意识到try / except一直是阻力最小的路径!
答案 2 :(得分:2)
in
(和not in
)运算符使用__contains__()来检查成员资格(通常为)。检查此问题的最简单和最优雅的方法是直接存在__contains__
属性,或者用于Container抽象基类:
hasattr(obj, '__contains__')
# Python 3: Container
import collections.abc
isinstance(obj, collections.abc.Container)
# Python 2: Container
import collections
isinstance(obj, collections.Container)
但是,文档确实提到了:
对于未定义
__contains__()
的对象,成员资格测试首先通过__iter__()
尝试迭代,然后通过__getitem__()
尝试旧的序列迭代协议,请参阅this section in the language reference。< / p>
因此,如果您希望绝对确定in
可以在不依赖try-except块的情况下使用,则应使用:
hasattr(obj, '__contains__') or hasattr(obj, '__iter__') or hasattr(obj, '__getitem__')
如果您只期望container个类似的对象,我会忽略__iter__()
甚至__getitem__()
,只需坚持__contains__()
。
答案 3 :(得分:1)
您可以查看是否'__contains__' in dir(theobject)
,但我并不认为try
不优雅。