как сказать, что переменная является итерационной, но не строкой
у меня есть функция, которая принимает аргумент, который может быть один элемент или двойной элемента:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
Так что:
>>> iterable( ("f","f") ) yes >>> iterable( ["f","f"] ) yes >>> iterable("ff") no
проблема в том, что строка технически повторяется, поэтому я не могу просто поймать ValueError при попытке arg[1]
. Я не хочу использовать isinstance(), потому что это не хорошая практика (так мне сказали).
7 ответов:
использовать isinstance (я не понимаю, почему это плохая практика)
import types if not isinstance(arg, types.StringTypes):
обратите внимание на использование строковых типов. Это гарантирует, что мы не забудем о каком-то неясном типе строки.
С другой стороны, это также работает для производных строковых классов.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
кроме того, вы можете взглянуть на это предыдущий вопрос.
Ура.
Примечание: поведение изменилось в Python 3, как
StringTypes
иbasestring
больше не определены. В зависимости от ваших потребностей, вы можете заменить их вisinstance
bystr
, или кортеж подмножества(str, bytes, unicode)
, например, для пользователей Cython. Как @Theron Luhn упоминается, вы также можете использоватьsix
.
начиная с Python 2.6, с введением абстрактных базовых классов,
isinstance
(используется для Азбуки, а не для конкретных классов) теперь считается вполне приемлемым. В частности:from abc import ABCMeta, abstractmethod class NonStringIterable: __metaclass__ = ABCMeta @abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, C): if cls is NonStringIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented
это точная копия (изменение только имени класса)
Iterable
Как определено в_abcoll.py
(деталь реализацииcollections.py
)... причина этого работает, как вы хотите, в то время какcollections.Iterable
не делает, это то, что последний идет лишнюю милю, чтобы гарантировать, что строки считаются итерационными, вызываяIterable.register(str)
явно сразу после этогоclass
заявление.конечно, это легко увеличить
__subclasshook__
вернутьсяFalse
доany
вызовите другие классы, которые вы хотите специально исключить из вашего определения.в любом случае, после того, как вы импортировали этот новый модуль как
myiter
,isinstance('ciao', myiter.NonStringIterable)
будетFalse
иisinstance([1,2,3], myiter.NonStringIterable)
будетTrue
, так же, как вы просите -- и в Python 2.6 и позже это считается правильным способом воплощения таких проверок... определить абстрактный базовый класс и проверкаisinstance
на нем.
по состоянию на 2017 год, вот портативное решение, которое работает со всеми версиями Python:
#!/usr/bin/env python import collections import six def iterable(arg): return ( isinstance(arg, collections.Iterable) and not isinstance(arg, six.string_types) ) # non-string iterables assert iterable(("f", "f")) # tuple assert iterable(["f", "f"]) # list assert iterable(iter("ff")) # iterator assert iterable(range(44)) # generator assert iterable(b"ff") # bytes (Python 2 calls this a string) # strings or non-iterables assert not iterable(u"ff") # string assert not iterable(44) # integer assert not iterable(iterable) # function
Я понимаю, что это старый пост, но подумал, что стоит добавить мой подход для Интернет-потомства. Функция, приведенная ниже, кажется, работает для меня в большинстве случаев с Python 2 и 3:
def is_collection(obj): """ Returns true for any iterable which is not a string or byte sequence. """ try: if isinstance(obj, unicode): return False except NameError: pass if isinstance(obj, bytes): return False try: iter(obj) except TypeError: return False try: hasattr(None, obj) except TypeError: return True return False
это проверяет наличие нестроковой итерации (mis) с помощью встроенного
hasattr
который поднимет aTypeError
когда его второй аргумент не является строкой или строкой Юникода.
объединив предыдущие ответы, я использую:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
не 100% доказательство дураков, но если объект не является итерационным, вы все равно можете пропустить его и вернуться к набору утки.
Edit: Python3
types.StringTypes == (str, unicode)
. В Phython3 эквиваленте составляет:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
хах, не понимаю... что случилось с
hasattr( x, '__iter__' )
?
... NB elgehelge помещает это в комментарий здесь, говоря: "посмотрите на мой более подробный ответ", но я не смог найти его/ее подробный ответ
позже
ввиду комментария Дэвида Чарльза о Python3, как насчет:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
? По-видимому, "basestring" больше не является типом Python3:
https://docs.python.org/3.0/whatsnew/3.0.html
The builtin basestring abstract type was removed. Use str instead. The str and bytes types don’t have functionality enough in common to warrant a shared base class.
Как вы правильно указываете, одна строка является последовательностью символов.
Итак, то, что вы действительно хотите сделать, это выяснить, какая последовательность
arg
is с помощью isinstance или type (a)==str.Если вы хотите реализовать функцию, которая принимает переменное количество параметров, вы должны сделать это так:
def function(*args): # args is a tuple for arg in args: do_something(arg)
функция ("ff") и функция ("ff"," ff") будут работать.
Я не вижу сценарий, где isiterable () функция, как твоя нужна. Это не isinstance (), что плохой стиль, но ситуации, когда вам нужно использовать isinstance().