как сказать переменная итеративна, но не строка

У меня есть функция, которая принимает аргумент, который может быть как отдельным, так и двойным:

<code>def iterable(arg)
    if #arg is an iterable:
        print "yes"
    else:
        print "no"
</code>

и что

>>> iterable( ("f","f") )
yes

>>> iterable( ["f","f"] )
yes

>>> iterable("ff")
no

Проблема в том, что строка технически повторяема, поэтому я не могу просто перехватить ValueError при попыткеarg[1]. Я не хочу использовать isinstance (), потому что это не очень хорошая практика (или мне так сказали).

 Kathy Van Stone28 июн. 2009 г., 19:47
Какая версия Python? Я считаю, что ответ отличается от 2. * и 3
 priestc30 июн. 2009 г., 05:47
@ Lennart: Canonical.org / ~ Kragen / isinstance это может быть устаревшим, хотя
 Lennart Regebro28 июн. 2009 г., 22:37
Тебе сказали неправильно, инстанс - не плохая практика.
 Lennart Regebro28 июн. 2009 г., 22:45
Ой, подождите, может быть, он ссылается на принцип, что проверять тип объектов плохо, и что это свидетельствует о том, что программа не работает? Это верно в принципе (но не всегда на практике). Это может или не может быть такой случай. Но проблема не в функции экземпляра, а в привычке проверять типы.
 priestc28 июн. 2009 г., 19:48
Я использую версию 2.5

Ответы на вопрос(7)

которое работает со всеми версиями 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
 Nick T15 авг. 2018 г., 18:28
Между 2/3 и байтовыми строками есть небольшие несоответствия, но если вы используете нативную «строку», то они оба ложные

хм, не понимаю ... что плохого в том, чтобы идти

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.htm

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.
 mike rodent23 янв. 2016 г., 12:36
@ DavidCharles О, правда? Виноват. Я пользователь Jython, и у Jython в настоящее время нет версии 3.
 davidrmcharles22 янв. 2016 г., 00:43
Возможно, потому что__iter__ есть ли в строках в Python 3?
Решение Вопроса

Используйте 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. Как@ Терон Лун упоминается, вы также можете использоватьsix.

 estani05 дек. 2012 г., 16:19
Я думаю, что идея плохой практики из-за утка набирает принцип. Быть членом определенного класса также не означает, что этотольк объект, который можно использовать, или что ожидаемые методы доступны. Но я думаю, что иногда вы просто не можете понять, что делает метод, даже если он присутствует, поэтомуisinstance может быть единственный путь.
 Tom28 июн. 2009 г., 20:19
Прекрасно, scvalex. Я удаляю свой -1 сейчас и делаю его +1: -).
 sorin21 июн. 2017 г., 13:47
2017: Этот ответ больше не действителен, см. Stackoverflow.com / а / 44328500/99834 для той, которая работает со всеми версиями Python.
 Theron Luhn17 янв. 2015 г., 01:54
Примечание: types.StringTypes недоступно в Python 3. Поскольку в py3k есть только один строковый тип, я думаю, что это безопасно дляdo isinstance(arg, str). Для обратно-совместимой версии рассмотрите возможность использования Pythonhosted.org / шесть / # six.string_types
 Nick T16 янв. 2014 г., 08:19
-1, условно будетTrue даже если inpu, t это число, функция, класс ...

Объединив предыдущие ответы, я использую:

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):
 xvan19 июл. 2018 г., 16:34
@ normanius Добавлена версия Python3
 PaulR13 февр. 2015 г., 15:56
Ваш оператор импорта должен быть «тип», а не «тип»

что это старый пост, но подумал, что стоит добавить мой подход к потомству в Интернете. Представленная ниже функция, кажется, работает для меня в большинстве случаев с 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

Это проверяет наличие нестроковых итераций (неправильно) с использованием встроенногоhasattr который подниметTypeError когда его второй аргумент не является строкой или строкой Юникода.

с введением абстрактных базовых классов,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 в теме

 Rob Smallshire14 авг. 2018 г., 11:27
@ AlexMartelli Для Python 3, вы не имеете в виду, чтоif issublass(C, str): return False следует добавить?
 Piotr Dobrogost23 окт. 2014 г., 11:54
(...), а в Python 2.6 и более поздних версиях это считается правильным способом воплощения таких проверок (...) Как можно когда-либо рассматривать злоупотребление хорошо известной концепцией абстрактного класса правильный путь вне моего понимания. Правильным способом было бы ввести некоторыевыглядит ка оператор вместо.
 Nick T22 апр. 2014 г., 08:30
В Python 3isinstance('spam', NonStringIterable) возвращаетTrue.
 Merlyn Morgan-Graham02 февр. 2015 г., 08:08
Алекс, вы можете ответить на утверждение Ника о том, что это не работает в Python 3? Мне нравится ответ, но я хотел бы убедиться, что я пишу код, ориентированный на будущее.
 Alex Martelli02 февр. 2015 г., 16:25
@ MerlynMorgan-Graham, это правильно, потому что__iter__ являетс теперь реализован в строках в Python 3. Таким образом, мой абзац «легко дополнить» становится применимым, например,if issublass(cls, str): return False необходимо добавить в начале__subclasshook__ (как и любые другие классы, которые определяют__iter__ но, по вашему мнению, не следует воспринимать как «нестроковые итерации»).

одна строка - это последовательность символов.

То, что ты действительно хочешь сделать, это выяснить, какую последовательностьarg с помощью isinstance или типа (a) == стр.

Если вы хотите реализовать функцию, которая принимает переменное количество параметров, вы должны сделать это следующим образом:

def function(*args):
    # args is a tuple
    for arg in args:
        do_something(arg)

function ("ff") и function ("ff", "ff") будут работать.

Я не вижу сценария, в котором нужна такая функция isiterable (), как ваша. Это не isinstance (), это плохой стиль, а ситуации, когда вам нужно использовать isinstance ().

 AkiRoss20 июн. 2015 г., 13:17
С помощьюtype(a) == str следует избегать. Это плохая практика, потому что она не учитывает похожие типы или типы, полученные изstr. type не поднимается по иерархии типов, аisinstance делает, поэтому лучше использоватьisinstance.

Ваш ответ на вопрос