как сказать, что переменная является итерационной, но не строкой
у меня есть функция, которая принимает аргумент, который может быть один элемент или двойной элемента:
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больше не определены. В зависимости от ваших потребностей, вы можете заменить их вisinstancebystr, или кортеж подмножества(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.
Как вы правильно указываете, одна строка является последовательностью символов.
Итак, то, что вы действительно хотите сделать, это выяснить, какая последовательность
argis с помощью isinstance или type (a)==str.Если вы хотите реализовать функцию, которая принимает переменное количество параметров, вы должны сделать это так:
def function(*args): # args is a tuple for arg in args: do_something(arg)функция ("ff") и функция ("ff"," ff") будут работать.
Я не вижу сценарий, где isiterable () функция, как твоя нужна. Это не isinstance (), что плохой стиль, но ситуации, когда вам нужно использовать isinstance().