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

66

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

<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 (), потому что это не очень хорошая практика (или мне так сказали).

Какая версия Python? Я считаю, что ответ отличается от 2. * и 3 Kathy Van Stone
Я использую версию 2.5 priestc
Ой, подождите, может быть, он ссылается на принцип, что проверять тип объектов плохо, и что это свидетельствует о том, что программа не работает? Это верно в принципе (но не всегда на практике). Это может или не может быть такой случай. Но проблема не в функции экземпляра, а в привычке проверять типы. Lennart Regebro
@ Lennart: Canonical.org / ~ Kragen / isinstance это может быть устаревшим, хотя priestc
Тебе сказали неправильно, инстанс - не плохая практика. Lennart Regebro

Ваш Ответ

7   ответов
40

Используйте 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.

Прекрасно, scvalex. Я удаляю свой -1 сейчас и делаю его +1: -). Tom
2017: Этот ответ больше не действителен, см. Stackoverflow.com / а / 44328500/99834 для той, которая работает со всеми версиями Python. sorin
-1, условно будетTrue даже если inpu, t это число, функция, класс ... Nick T
Примечание: types.StringTypes недоступно в Python 3. Поскольку в py3k есть только один строковый тип, я думаю, что это безопасно дляdo isinstance(arg, str). Для обратно-совместимой версии рассмотрите возможность использования Pythonhosted.org / шесть / # six.string_types Theron Luhn
Я думаю, что идея плохой практики из-за утка набирает принцип. Быть членом определенного класса также не означает, что этотольк объект, который можно использовать, или что ожидаемые методы доступны. Но я думаю, что иногда вы просто не можете понять, что делает метод, даже если он присутствует, поэтомуisinstance может быть единственный путь. estani
3

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

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):
@ normanius Добавлена версия Python3 xvan
Ваш оператор импорта должен быть «тип», а не «тип» PaulR
4

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

14

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

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

(...), а в Python 2.6 и более поздних версиях это считается правильным способом воплощения таких проверок (...) Как можно когда-либо рассматривать злоупотребление хорошо известной концепцией абстрактного класса правильный путь вне моего понимания. Правильным способом было бы ввести некоторыевыглядит ка оператор вместо. Piotr Dobrogost
В Python 3isinstance('spam', NonStringIterable) возвращаетTrue. Nick T
Алекс, вы можете ответить на утверждение Ника о том, что это не работает в Python 3? Мне нравится ответ, но я хотел бы убедиться, что я пишу код, ориентированный на будущее. Merlyn Morgan-Graham
@ MerlynMorgan-Graham, это правильно, потому что__iter__ являетс теперь реализован в строках в Python 3. Таким образом, мой абзац «легко дополнить» становится применимым, например,if issublass(cls, str): return False необходимо добавить в начале__subclasshook__ (как и любые другие классы, которые определяют__iter__ но, по вашему мнению, не следует воспринимать как «нестроковые итерации»). Alex Martelli
@ AlexMartelli Для Python 3, вы не имеете в виду, чтоif issublass(C, str): return False следует добавить? Rob Smallshire
2

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

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.
Возможно, потому что__iter__ есть ли в строках в Python 3? davidrmcharles
@ DavidCharles О, правда? Виноват. Я пользователь Jython, и у Jython в настоящее время нет версии 3. mike rodent
1

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

То, что ты действительно хочешь сделать, это выяснить, какую последовательность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 ().

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

Похожие вопросы