查看是否可以使用 `in` 运算符
find out if the `in` operator can be used
找出 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 可迭代是:
container.iter()
Return an iterator object. The object is required to support the iterator protocol described below. If a container supports different
types of iteration, additional methods can be provided to specifically
request iterators for those iteration types. (An example of an object
supporting multiple forms of iteration would be a tree structure which
supports both breadth-first and depth-first traversal.) This method
corresponds to the tp_iter slot of the type structure for Python
objects in the Python/C API.
所以
hasattr([], "__iter__") and hasattr({}, "__iter__")
return true
符合预期,但是
hasattr("test_string", "__iter__")
return 错误。但我可以使用
"test" in "test_string"
没有任何问题。
优雅是指不使用 try-except 解决方案
你可以看看是否'__contains__' in dir(theobject)
,但我并不认为try
不雅
in
(和not in
)运算符使用__contains__() to check for membership (typically). The simplest and most elegant way to check for this is either the direct existence of the __contains__
attribute, or for the Container抽象基class:
hasattr(obj, '__contains__')
# Python 3: Container
import collections.abc
isinstance(obj, collections.abc.Container)
# Python 2: Container
import collections
isinstance(obj, collections.Container)
但是,文档确实提到:
For objects that don’t define __contains__()
, the membership test first tries iteration via __iter__()
, then the old sequence iteration protocol via __getitem__()
, see this section in the language reference.
所以,如果你想绝对确定 in
可以在不依赖 try-except 块的情况下使用,你应该使用:
hasattr(obj, '__contains__') or hasattr(obj, '__iter__') or hasattr(obj, '__getitem__')
如果你只期待 container 样的对象,我会忽略 __iter__()
甚至 __getitem__()
,并坚持使用 __contains__()
.
除了是正确而优雅的方式外,请尝试。
首先,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 一直以来都是阻力最小的路径!
迭代器协议实际上不需要类型来支持__iter__
。它需要一个 支持 __iter__
或 __getitem__
的类型,并具有从 0 开始的连续整数参数。请参阅 iter
函数以获得最佳解释这在文档中。
因此,hasattr(x, "__iter__")
如果测试某物是否可迭代,将会给你漏报。
那么,如何做到这一点?好吧,即使你不喜欢,正确的方法是:
try:
i = iter(x)
except TypeError:
# not iterable
另外,请注意,正如 hasattr
的文档所解释的那样:
This is implemented by calling getattr(object, name)
and seeing whether it raises an exception or not.
所以,实际上,您根本就没有在避免异常;你只是想出了一个更复杂的方法来引发异常并对你自己隐藏这个事实。
但与此同时,迭代首先是一个转移注意力的问题。 in
运算符是通过 __contains__
method. Container types that don't define a __contains__
method will fall back to iterating and comparing, but types aren't required to implement it that way. You can have a __contains__
that's much faster than iterating could be (as with dict
and set
); you can even be a container without being an iterable. (Note that the collections
module ABCs 实现的,具有单独的 Container
和 Iterable
基础;两者都不依赖于另一个。)
所以,如果你真的想在没有任何异常处理的情况下做到这一点,你怎么可能?
好吧,您必须检查以下至少一项为真:
x
有一个 __contains__
方法。
x
有一个 __iter__
方法。
x
有一个 __getitem__
方法,当用数字 0
调用时,要么 returns 成功,要么引发 IndexError
.
即使您接受如果不实际尝试用数字 0
调用它就不可能测试最后一个,并且假设 __getitem__
是 "close enough",如何在不依赖异常的情况下对此进行测试?
你真的做不到。例如,您可以迭代 dir(x)
,但这不适用于动态定义 __contains__
的 classes,例如,在委托给 [=] 的 __getattr__
方法中41=].
而且,即使可以,如果您有一个 class 定义 __contains__
为不带参数,会发生什么?属性在那里,但 in
仍然会引发 TypeError
.
所有这些都忽略了 which special methods are looked up on the object and which on the type itself 上的(依赖于实现的)规则。例如,在 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
找出 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 可迭代是:
container.iter()
Return an iterator object. The object is required to support the iterator protocol described below. If a container supports different types of iteration, additional methods can be provided to specifically request iterators for those iteration types. (An example of an object supporting multiple forms of iteration would be a tree structure which supports both breadth-first and depth-first traversal.) This method corresponds to the tp_iter slot of the type structure for Python objects in the Python/C API.
所以
hasattr([], "__iter__") and hasattr({}, "__iter__")
return true
符合预期,但是
hasattr("test_string", "__iter__")
return 错误。但我可以使用
"test" in "test_string"
没有任何问题。
优雅是指不使用 try-except 解决方案
你可以看看是否'__contains__' in dir(theobject)
,但我并不认为try
不雅
in
(和not in
)运算符使用__contains__() to check for membership (typically). The simplest and most elegant way to check for this is either the direct existence of the __contains__
attribute, or for the Container抽象基class:
hasattr(obj, '__contains__')
# Python 3: Container
import collections.abc
isinstance(obj, collections.abc.Container)
# Python 2: Container
import collections
isinstance(obj, collections.Container)
但是,文档确实提到:
For objects that don’t define
__contains__()
, the membership test first tries iteration via__iter__()
, then the old sequence iteration protocol via__getitem__()
, see this section in the language reference.
所以,如果你想绝对确定 in
可以在不依赖 try-except 块的情况下使用,你应该使用:
hasattr(obj, '__contains__') or hasattr(obj, '__iter__') or hasattr(obj, '__getitem__')
如果你只期待 container 样的对象,我会忽略 __iter__()
甚至 __getitem__()
,并坚持使用 __contains__()
.
除了是正确而优雅的方式外,请尝试。
首先,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 一直以来都是阻力最小的路径!
迭代器协议实际上不需要类型来支持__iter__
。它需要一个 支持 __iter__
或 __getitem__
的类型,并具有从 0 开始的连续整数参数。请参阅 iter
函数以获得最佳解释这在文档中。
因此,hasattr(x, "__iter__")
如果测试某物是否可迭代,将会给你漏报。
那么,如何做到这一点?好吧,即使你不喜欢,正确的方法是:
try:
i = iter(x)
except TypeError:
# not iterable
另外,请注意,正如 hasattr
的文档所解释的那样:
This is implemented by calling
getattr(object, name)
and seeing whether it raises an exception or not.
所以,实际上,您根本就没有在避免异常;你只是想出了一个更复杂的方法来引发异常并对你自己隐藏这个事实。
但与此同时,迭代首先是一个转移注意力的问题。 in
运算符是通过 __contains__
method. Container types that don't define a __contains__
method will fall back to iterating and comparing, but types aren't required to implement it that way. You can have a __contains__
that's much faster than iterating could be (as with dict
and set
); you can even be a container without being an iterable. (Note that the collections
module ABCs 实现的,具有单独的 Container
和 Iterable
基础;两者都不依赖于另一个。)
所以,如果你真的想在没有任何异常处理的情况下做到这一点,你怎么可能?
好吧,您必须检查以下至少一项为真:
x
有一个__contains__
方法。x
有一个__iter__
方法。x
有一个__getitem__
方法,当用数字0
调用时,要么 returns 成功,要么引发IndexError
.
即使您接受如果不实际尝试用数字 0
调用它就不可能测试最后一个,并且假设 __getitem__
是 "close enough",如何在不依赖异常的情况下对此进行测试?
你真的做不到。例如,您可以迭代 dir(x)
,但这不适用于动态定义 __contains__
的 classes,例如,在委托给 [=] 的 __getattr__
方法中41=].
而且,即使可以,如果您有一个 class 定义 __contains__
为不带参数,会发生什么?属性在那里,但 in
仍然会引发 TypeError
.
所有这些都忽略了 which special methods are looked up on the object and which on the type itself 上的(依赖于实现的)规则。例如,在 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