У меня есть функция, которая принимает аргумент, который может быть либо одиночным, либо двойным элементом:
def iterable(arg)
if #arg is an iterable:
print "yes"
else:
print "no"
так что:
>>> iterable (("е", "е")) да >>> iterable (["f", "f"]) да >>> iterable ("ff") нет
Проблема в том, что строка технически итеративна, поэтому я не могу просто поймать ValueError при попытке arg[1]
. Я не хочу использовать isinstance (), потому что это не очень хорошая практика (по крайней мере, мне так сказали).
isinstance
это способ сделать это на языках с динамической типизацией. Вещь не для повседневного использования, но годится в обоснованных случаях.Ответы:
Используйте isinstance (я не понимаю, почему это плохая практика)
import types if not isinstance(arg, types.StringTypes):
Обратите внимание на использование StringTypes. Это гарантирует, что мы не забудем о каком-то непонятном типе строки.
С другой стороны, это также работает для производных строковых классов.
class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true
Кроме того, вы можете захотеть взглянуть на этот предыдущий вопрос .
Ура.
NB: поведение изменилось в Python 3, поскольку
StringTypes
иbasestring
больше не определены. В зависимости от ваших потребностей, вы можете заменить ихisinstance
наstr
или часть кортежа(str, bytes, unicode)
, например, для пользователей Cython. Как упомянул @Theron Luhn , вы также можете использоватьsix
.источник
isinstance
может быть единственный способ.do isinstance(arg, str)
. Для версии с обратной совместимостью рассмотрите возможность использования pythonhosted.org/six/#six.string_typestypes.StringTypes
он недоступен в Python3. Какое значение имеет Python2?По состоянию на 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.6, с введением абстрактных базовых классов
isinstance
(используемых в ABC, а не в конкретных классах) теперь считается вполне приемлемым. В частности: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
это.источник
isinstance('spam', NonStringIterable)
возвращаетсяTrue
.__iter__
в настоящее время осуществляется в строках в Python 3. Так что мой «легко дополнить» пункт становится применимым и , например ,if issublass(cls, str): return False
потребности , которые будут добавлены в начале__subclasshook__
(а также любые другие классы , которые определяют ,__iter__
но в вашем мышление не должно восприниматься как «нестроковые итерации»).if issublass(C, str): return False
нужно добавить?Я понимаю, что это старый пост, но подумал, что стоит добавить свой подход для потомков Интернета. Приведенная ниже функция, похоже, работает для меня в большинстве случаев как с Python 2, так и с Python 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
Это проверяет наличие нестроковой итерации (неверно) с использованием встроенной функции,
hasattr
котораяTypeError
вызывает a, когда ее второй аргумент не является строкой или строкой Unicode.источник
Объединив предыдущие ответы, я использую:
import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...]
Не 100% доказательство дураков, но если объект не является повторяемым, вы все равно можете пропустить его и вернуться к утиной печати.
Изменить: Python3
types.StringTypes == (str, unicode)
. Эквивалент Phython3:if isinstance(var, str ) \ or not isinstance(var, collections.Iterable):
источник
2.x
Я бы предложил:
hasattr(x, '__iter__')
или с учетом комментария Дэвида Чарльза о настройке этого для Python3, как насчет:
hasattr(x, '__iter__') and not isinstance(x, (str, bytes))
3.x
встроенный
basestring
абстрактный тип был удален .str
Вместо этого используйте . Вstr
иbytes
типах не имеют функциональность достаточно общий , чтобы гарантировать общий базовый класс.источник
__iter__
, что в Python 3 есть строки?Как вы правильно заметили, одна строка - это последовательность символов.
Поэтому вам действительно нужно выяснить, что это за последовательность
arg
, используя isinstance или type (a) == str.Если вы хотите реализовать функцию, которая принимает переменное количество параметров, вы должны сделать это следующим образом:
def function(*args): # args is a tuple for arg in args: do_something(arg)
функция («ff») и функция («ff», «ff») будут работать.
Я не вижу сценария, в котором нужна функция isiterable (), подобная вашей. Это не isinstance () плохой стиль, а ситуации, когда вам нужно использовать isinstance ().
источник
type(a) == str
следует избегать. Это плохая практика, потому что она не принимает во внимание похожие типы или типы, производные отstr
.type
по иерархии типов не лезет, пока не лезетisinstance
, поэтому лучше использоватьisinstance
.Чтобы подробно
collections.py
рассказать об отличном приеме Алекса Мартелли и ответить на некоторые вопросы, связанные с ним: Текущее рабочее решение в python 3.6+:import collections import _collections_abc as cabc import abc class NonStringIterable(metaclass=abc.ABCMeta): __slots__ = () @abc.abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, c): if cls is NonStringIterable: if issubclass(c, str): return False return cabc._check_methods(c, "__iter__") return NotImplemented
и продемонстрировал
>>> typs = ['string', iter(''), list(), dict(), tuple(), set()] >>> [isinstance(o, NonStringIterable) for o in typs] [False, True, True, True, True, True]
Если вы хотите добавить
iter('')
в исключения, например, измените строкуif issubclass(c, str): return False
быть
# `str_iterator` is just a shortcut for `type(iter(''))`* if issubclass(c, (str, cabc.str_iterator)): return False
получить
[False, False, True, True, True, True]
источник