변수가 반복 가능하지만 문자열이 아니라는 것을 알려주는 방법


88

단일 항목 또는 이중 항목이 될 수있는 인수를받는 함수가 있습니다.

def iterable(arg)
    if #arg is an iterable:
        print "yes"
    else:
        print "no"

그래서:

>>> iterable (( "f", "f"))
예

>>> 반복 가능 ([ "f", "f"])
예

>>> iterable ( "ff")
아니

문제는 문자열이 기술적으로 반복 가능하므로 시도 할 때 ValueError를 잡을 수 없다는 것 arg[1]입니다. isinstance ()는 좋은 습관이 아니기 때문에 사용하고 싶지 않습니다.


1
어떤 버전의 Python? 답이 2. *와 3 사이에 다르다고 생각합니다
Kathy Van Stone

4
당신은 틀린 말을 들었는데, 인스턴스는 나쁜 습관이 아닙니다.
Lennart Regebro

3
오, 잠깐만 요, 그가 객체 유형을 확인하는 것은 나쁘다는 원칙을 언급하고 이것이 프로그램이 중단되었음을 나타내는 것입니까? 이것은 원칙적으로 사실입니다 (항상 그런 것은 아닙니다). 이것은 그러한 경우 일 수도 있고 아닐 수도 있습니다. 그러나 문제인 것은 기능이 인스턴스가 아니라 유형을 확인하는 습관입니다.
Lennart Regebro

@Lennart : canonical.org/~kragen/isinstance 그래도 구식 일 수 있습니다
priestc

@up 이것은 유형 기반 함수 오버로딩을 언급하지 않으며 isinstance동적 유형 언어로 수행하는 방법입니다. 매일 사용하지 않는 것이지만 정당한 경우에는 괜찮습니다.
Kos

답변:


50

isinstance 사용 (왜 나쁜 습관인지 모르겠습니다)

import types
if not isinstance(arg, types.StringTypes):

StringTypes 사용에 유의하십시오. 모호한 유형의 문자열을 잊지 않도록합니다.

장점은 파생 된 문자열 클래스에서도 작동합니다.

class MyString(str):
    pass

isinstance(MyString("  "), types.StringTypes) # true

또한이 이전 질문을 살펴볼 수도 있습니다 .

건배.


NB : 행동이 파이썬 3으로 변경 StringTypes하고 basestring더 이상 정의되지 않습니다. 필요에 따라, 당신은에 장착 할 수 isinstance에 의해 str, 또는 부분 집합의 튜플 (str, bytes, unicode)사이 썬 사용자를위한, 예. 으로 @Theron Luhn는 했나요, 당신은 또한 사용할 수 있습니다 six.


좋아, scvalex. 나는 지금 내 -1을 제거하고 +1 :-).
Tom

2
나쁜 연습 아이디어는 오리 타이핑 원칙 때문이라고 생각합니다 . 특정 클래스의 멤버가 된다고해서 사용할 수있는 유일한 개체이거나 예상되는 메서드를 사용할 수 있다는 의미는 아닙니다. 하지만 때로는 그 방법이 존재하더라도 그 방법이 무엇을하는지 추론 할 수 없을 때가 있습니다. 그래서 isinstance이것이 유일한 방법 일 수 있습니다.
estani

2
참고 : types.StringTypes는 Python 3에서 사용할 수 없습니다. py3k에는 문자열 유형이 하나뿐이므로 do isinstance(arg, str). 이전 버전과 호환되는 버전의 경우 pythonhosted.org/six/#six.string_types
Theron Luhn

나는 엄격하게 Python3을 types.StringTypes사용하며 Python3에서는 사용할 수 없음을 알았습니다. Python2의 가치는 무엇입니까?
kevinarpe

2
2017 :이 답변은 더 이상 유효하지 않습니다 . 모든 버전의 Python에서 작동하는 답변은 stackoverflow.com/a/44328500/99834 를 참조하세요 .
소린

26

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

이 bytestrings 2/3 사이에 약간의 불일치입니다,하지만 당신은 네이티브 "문자열"을 사용하는 경우 그들은 모두 거짓이야
닉 T

16

추상 기본 클래스가 도입 된 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.pycollections.pycollections.IterableIterable.register(str)class

물론 정의에서 구체적으로 제외하려는 다른 클래스를 호출 하기 전에 __subclasshook__반환 하여 쉽게 확장 할 수 있습니다.Falseany

어떤 경우에, 당신은이 새로운 모듈을 가져온 후 myiter, isinstance('ciao', myiter.NonStringIterable)False, 그리고 isinstance([1,2,3], myiter.NonStringIterable)될 것입니다 True추상 기본 클래스를 정의 ... 파이썬 2.6 이상이 구현 수표에 적절한 방법으로 간주됩니다 - 당신이 요청하는대로, 확인 isinstance하십시오.


파이썬 3 명 isinstance('spam', NonStringIterable)돌아갑니다 True.
Nick T

1
(...) 그리고 Python 2.6 이상에서는 이러한 검사를 구현하는 적절한 방법으로 간주됩니다. (...) 잘 알려진 추상 클래스 개념을 그러한 방식으로 남용하는 것이 적절한 방식 으로 간주 될 수있는 방법 은 제 이해를 넘어서는 것입니다. 적절한 방법은 대신 외모 연산자 를 도입하는 것입니다.
Piotr Dobrogost 2014 년

Alex, 이것이 Python 3에서 작동하지 않는다는 Nick의 주장을 해결할 수 있습니까? 대답이 마음에 들지만 미래 보장형 코드를 작성하고 있는지 확인하고 싶습니다.
Merlyn Morgan-Graham

때문에, 맞습니다 @ MerlynMorgan - 그레이엄 __iter__ 되어 지금 내 "쉽게 확장하기 위해"제 적용 및 예를 들어이된다 그래서 파이썬 3에서 문자열 구현 if issublass(cls, str): return False의 시작 부분에 추가 할 필요가 __subclasshook__아니라 정의하는 다른 클래스로 ( __iter__만에 사고 방식은 "비 문자열 반복 가능"으로 받아 들여서는 안됩니다).
Alex Martelli

@AlexMartelli Python 3의 if issublass(C, str): return False경우 추가해야 한다는 뜻이 아닙니까?
Rob Smallshire

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

이것은 두 번째 인수가 문자열이나 유니 코드 문자열이 아닐 때 hasattra를 TypeError발생 시키는 내장을 사용하여 (mis)에 의해 문자열이 아닌 이터 러블을 확인 합니다.


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):

import 문은 'type'이 아닌 'types'여야합니다
PaulR

3

2.x

나는 제안했을 것이다 :

hasattr(x, '__iter__')

또는 David Charles의 주석이 Python3에 대해 이것을 조정 한 것을 고려하여 다음과 같습니다.

hasattr(x, '__iter__') and not isinstance(x, (str, bytes))

3.x

내장 basestring추상 유형 이 제거되었습니다 . str대신 사용하십시오 . strbytes유형은 공유 기본 클래스를 보증하는 공통 기능 충분하지 않습니다.


3
아마도 __iter__파이썬 3에는 문자열이 있기 때문 일까요?
davidrmcharles 16.01.21

@DavidCharles 오, 정말? 내 잘못이야. 나는 자이 썬 사용자입니다 및 자이 썬은 현재 어떤 버전 3이 없습니다
마이크 설치류

이것은 실제로 대답이 아니며, 더 많은 의견 / 질문이며, 3.x에서는 잘못되었습니다. 정리해 주 시겠어요? " 'str'및 'bytes'유형은 공유 된 기본 클래스를 보장 할 수있는 공통 기능이 충분하지 않습니다." 라고 주장하는 이유를 추가 할 수 있습니까? 3.x의 핵심 포인트 중 하나는 유니 코드 바이트를 일류 시민으로 만드는 것이 었습니다.
smci

위의 내용을 왜 썼는지 모르겠습니다. 나는 이미 내 대답을 편집했지만 "3.x"아래의 모든 텍스트를 삭제할 것을 제안합니다. 원하는 경우 더 편집하십시오.
마이크 설치류

0

올바르게 지적했듯이 단일 문자열은 문자 시퀀스입니다.

그래서 당신이 정말로하고 싶은 것은 argisinstance 또는 type (a) == str을 사용하여 어떤 종류의 시퀀스인지 알아내는 것 입니다.

가변적 인 양의 매개 변수를 사용하는 함수를 구현하려면 다음과 같이해야합니다.

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

function ( "ff") 및 function ( "ff", "ff")가 작동합니다.

당신과 같은 isiterable () 함수가 필요한 시나리오를 볼 수 없습니다. 나쁜 스타일은 isinstance ()가 아니라 isinstance ()를 사용해야하는 상황입니다.


4
사용 type(a) == str은 피해야합니다. 에서 파생 된 유사한 유형이나 유형을 고려하지 않기 때문에 나쁜 습관입니다 str. type유형 계층 구조를 올라가지 않지만를 isinstance사용하는 것이 좋습니다 isinstance.
AkiRoss 2015-06-20

0

Alex Martelli의 뛰어난 해킹을 명시 적으로 확장하고 이에 대한 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]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.