객체가 목록인지 튜플인지 확인하는 방법 (문자열은 아님)?


444

이것이 내가 일반적으로 입력이 있음을 확인하기 위하여 않는 것입니다 list/ tuple아니지만 - str. 함수가 str실수 로 객체를 전달하는 버그를 여러 번 발견했기 때문에 대상 함수 는 실제로 또는로 for x in lst가정합니다 .lstlisttuple

assert isinstance(lst, (list, tuple))

내 질문은 : 이것을 달성하는 더 좋은 방법이 있습니까?


9
type (lst)은 list입니까?
jackalope

1
isinstance (key, six.string_types)
wyx

답변:


332

파이썬 2에서만 (파이썬 3 아님) :

assert not isinstance(lst, basestring)

실제로 원하는 것입니다. 그렇지 않으면 목록처럼 작동하지만 list또는의 하위 클래스가 아닌 많은 것들을 놓칠 수 tuple있습니다.


91
네, 정답입니다. Python 3에서는 basestring사라졌으며 확인하십시오 isinstance(lst, str).
steveha 2009

5
리스트와 같이 반복 할 수있는 많은 것들이 있습니다 (예 : set생성자 표현식, 반복자). 같은 이국적인 것들이 있습니다 mmap. 덜 이국적인 것들이 array목록과 거의 비슷하게 작용하며 아마도 잊어 버린 것들이 더 많습니다.
Nick Craig-Wood

50
lst원본이 수행하는 동안 (예 : int가이 검사를 통과 할 때) 이것이 반복 가능 하다는 보장은하지 않습니다.
Peter Gibson

11
@PeterGibson-이 둘의 조합은 유효하고보다 제한적인 검사를 제공하고 1) lst가 반복 가능하고 2) lst가 문자열이 아닌지 확인합니다. assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
strongMA

4
글쎄,이 솔루션은 문자열 파생 유형 만 검사하지만 정수, 복식 또는 반복 불가능한 유형은 어떻습니까?
Eneko Alonso

171

파이썬에서는 "오리 타이핑"을 사용하고 싶다는 것을 기억하십시오. 따라서 목록처럼 작동하는 모든 것을 목록으로 취급 할 수 있습니다. 따라서 목록의 유형을 확인하지 말고 목록처럼 작동하는지 확인하십시오.

그러나 문자열도 목록처럼 작동하며 종종 우리가 원하는 것이 아닙니다. 심지어 문제 일 때도 있습니다! 따라서 문자열을 명시 적으로 확인한 다음 오리 타이핑을 사용하십시오.

다음은 내가 재미있게 작성한 기능입니다. repr()꺾쇠 괄호 ( '<', '>')로 시퀀스를 인쇄 하는 특수 버전입니다 .

def srepr(arg):
    if isinstance(arg, basestring): # Python 3: isinstance(arg, str)
        return repr(arg)
    try:
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    except TypeError: # catch when for loop fails
        return repr(arg) # not a sequence so just return repr

이것은 전체적으로 깨끗하고 우아합니다. 그러나 그 isinstance()검사는 무엇 입니까? 그것은 일종의 해킹입니다. 그러나 필수입니다.

이 함수는 목록처럼 작동하는 모든 것을 재귀 적으로 호출합니다. 문자열을 특수하게 처리하지 않으면 목록처럼 취급되어 한 번에 한 문자 씩 분할됩니다. 그러나 재귀 호출은 각 문자를 목록으로 취급하려고 시도하며 작동합니다! 한 문자 문자열조차도 목록으로 작동합니다! 이 함수는 스택 오버플로가 발생할 때까지 계속 재귀 적으로 호출합니다.

수행 할 작업을 세분화하는 각 재귀 호출에 의존하는 이와 같은 함수는 문자열을 특수 사례로 묶어야합니다. 한 문자열 이하의 문자열을 분해 할 수 없기 때문에 -문자열은 목록처럼 작동합니다.

참고 : try/ except는 의도를 표현하는 가장 깨끗한 방법입니다. 그러나이 코드가 어떻게 시간이 중요하다면, 우리는이 코드가 arg시퀀스 인지 확인하기 위해 일종의 테스트로 대체하고 싶을 것입니다 . 유형을 테스트하는 대신 동작을 테스트해야합니다. .strip()메서드 가 있으면 문자열이므로 시퀀스로 간주하지 마십시오. 그렇지 않으면 색인 가능하거나 반복 가능하면 시퀀스입니다.

def is_sequence(arg):
    return (not hasattr(arg, "strip") and
            hasattr(arg, "__getitem__") or
            hasattr(arg, "__iter__"))

def srepr(arg):
    if is_sequence(arg):
        return '<' + ", ".join(srepr(x) for x in arg) + '>'
    return repr(arg)

편집 : 원래 위의 내용을 확인 __getslice__()했지만 collections모듈 설명서에서 흥미로운 방법은 다음과 같습니다 __getitem__(). 이것은 의미가 있습니다. 이것이 객체를 색인하는 방법입니다. 그보다 더 근본적인 것처럼 보이 __getslice__()므로 위의 내용을 변경했습니다.


2
@stantonk, 그렇게 말해 주셔서 감사합니다. 그러나이 글을 쓸 때 이미 받아 들여진 대답이 있다고 생각하며 실제로 받아 들인 대답이 변경 될 것으로 기대하지 않습니다.
steveha

@ steveha : srepr매우 흥미로운 아이디어입니다. 그러나 나는 특별한 경우가 필요한지에 대해 당신과 다른 의견을 가지고 있습니다 str. 그렇습니다 . str에서 무한 재귀를 일으키는 가장 명백하고 일반적인 반복 가능입니다 srepr. 그러나 나는 타당한 이유가 있거나없는 동일한 방식으로 동작하는 사용자 정의 반복 가능을 쉽게 상상할 수 있습니다. 특별한 경우가 아니라 str, 우리는이 접근법이 무한한 재귀를 겪을 수 있음을 인정하고 그것을 다루는 방법에 동의해야합니다. 답변에 제 제안을 게시하겠습니다.
최대

1
이것이 올바른 길이라고 생각합니다. 그러나 특수한 경우 (이 시나리오에서는 문자열)를 처리하기 위해 "인간은 어떻게 차이를 말할 수 있을까요?"라는 질문을하는 것이 좋습니다. 예를 들어, 이메일 주소 목록 또는 단일 이메일 주소가 될 수있는 함수 인수를 고려하십시오 (문자열은 단순히 문자 목록이라는 것을 명심하십시오). 이 변수를 인간에게 제공하십시오. 그것이 무엇인지 어떻게 알 수 있습니까? 내가 생각할 수있는 가장 쉬운 방법은 목록의 각 항목에 몇 개의 문자가 있는지 보는 것입니다. 1보다 크면 인수는 반드시 문자 목록이 될 수 없습니다.
Josh

1
나는 이것에 대해 조금 생각하고 다른 사람들과 그것에 대해 토론했고, 나는 srepr()괜찮다고 생각 합니다. 우리는 다른리스트 안에 중첩 된리스트와 같은 것을 처리하기 위해 재귀 함수가 필요하다. 그러나 문자열의 "foo"경우보다로 인쇄해야합니다 <'f', 'o', 'o'>. 따라서 문자열을 명시 적으로 확인하는 것이 좋습니다. 또한 반복이 항상 반복 가능 반환하고 재귀로 인해 스택 오버플로가 항상 발생하는 데이터 유형의 다른 예는 실제로 없습니다 . 따라서이를 테스트하기 위해 특별한 속성이 필요하지 않습니다 ( "실용성이 순도").
steveha

1
문자열이 있기 때문에 이것은 파이썬 3에서 작동하지 않습니다 __iter__()파이썬 3의 방법을하지만, 파이썬 2에서 당신은 괄호를 누락하지 is_sequence(), 그것을 읽어야합니다return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
MiniQuark

124
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.

11
다른 주석 작성자가 지적한 것처럼 오리 타이핑의 파이썬 관용구를 사용하지 않는 것을 제외하고는 (질문에 직접적이고 깨끗하게 대답하지만).
Soren Bjornstad

7
오리 타이핑을 허용하지 않고 단순한 서브 클래 싱 (일반적인 예는 namedtuple 클래스)에서는 실패하기 때문에이 답변은 다른 것보다 덜 수용 가능합니다.
Philippe Gauthier

11
"오리 입력을 허용하지 않음"은 특히이 답변이 실제로 질문에 대한 답변이라는 점에서 대답을 덜 받아 들일 수 없습니다.
Petri

4
이 답변을 찬성했지만 if isinstance( H, (list, tuple) ): ...더 짧고 명확합니다.
shahar_m

2
대체 구문 :if type(H) in [list, tuple]:
Štefan Schindler

77

파이썬 3의 경우 :

import collections.abc

if isinstance(obj, collections.abc.Sequence) and not isinstance(obj, str):
    print("obj is a sequence (list, tuple, etc) but not a string")

버전 3.3으로 변경 : Collections 추상베이스 클래스를 collections.abc 모듈로 이동했습니다. 이전 버전과의 호환성을 위해 버전 3.8까지 작동을 멈출 때까지이 모듈에서 계속 볼 수 있습니다.

파이썬 2의 경우 :

import collections

if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring):
    print "obj is a sequence (list, tuple, etc) but not a string or unicode"

5
와! 이것은 정말 훌륭하게 작동하며 다른 정답보다 훨씬 간결합니다. 나는 내장 타입이 상속 받는다는 것을 몰랐지만 collections.Sequence테스트 해 보았습니다. 그렇습니다 xrange. 더 나은,이 테스트 제외 dict, 모두 갖고 __getitem____iter__.
Neil Mayhew

왜 결과에 inspect.getmro(list)포함 Sequence되지 않습니까? 우리가 모든 것을 보여주지 않을 isinstance때 우리가 할 수있는 일을 어떻게 알아 내야 getmro합니까?
Steve Jorgensen

@SteveJorgensen Method Resolution Order는 파이썬이 클래스에서 사용할 올바른 메소드를 검색하기 위해 사용하는 클래스 검색 경로를 정의합니다. Sequence추상 클래스입니다.
suzanshakya

3
Python3에서는 isinstance (obj, basestring)를 isinstance (obj, str)로 바꿀 수 있으며 작동해야합니다.
Adrian Keister

2
파이썬 3에서는 isinstance (obj, bytes)가 필요하지 않고 바이트 만 열거하지 않고 목록을 원한다면 ...
Erik Aronesty

35

PHP 풍미를 가진 파이썬 :

def is_array(var):
    return isinstance(var, (list, tuple))

6
파이썬은 오리 형 언어이므로 var에 attribute가 있는지 확인해야합니다 __getitem__. 또한 배열 모듈이 있기 때문에 이름이 잘못되었습니다. 그리고 var은 numpy.ndarray 또는 다른 유형일 수도 있습니다 __getitem__. 정답 은 stackoverflow.com/a/1835259/470560 을 참조하십시오 .
peterhil

9
@peterhil str또한 __getitem__귀하의 수표가 제외되지 않습니다str
erikbwork

9
받아쓰기도 마찬가지입니다. 확인하는 __getitem__것은 나쁜 조언입니다.
Petri

10

일반적으로 객체를 반복하는 함수는 튜플 및 목록뿐만 아니라 문자열에서도 작동한다는 사실은 버그보다 더 많은 기능입니다. 당신은 확실히 할 수 사용 isinstance하거나 입력 오리 인수를 확인하기 위해, 그런데 왜 당신해야?

그것은 수사적인 질문처럼 들리지만 그렇지 않습니다. "왜 인수 유형을 확인해야합니까?" 아마도 인식 된 문제가 아니라 실제 문제에 대한 해결책을 제안 할 것입니다. 문자열이 함수에 전달 될 때 왜 버그입니까? 또한 : 문자열 이이 함수에 전달 될 때 버그 인 경우 목록이 아닌 튜플 iterable이 아닌 다른 것이 전달됩니까? 그 이유는 무엇?

질문에 대한 가장 일반적인 대답은 글을 쓰는 개발자 f("abc")가 작성한 것처럼 함수가 작동하기를 기대하고 있을 가능성이 있다고 생각합니다 f(["abc"]). 문자열의 문자를 반복하는 유스 케이스를 지원하는 것보다 개발자를 스스로 보호하는 것이 더 합리적인 상황 일 수 있습니다. 그러나 나는 그것에 대해 먼저 길고 열심히 생각할 것입니다.


16
"하지만 먼저 그것에 대해 오랫동안 열심히 생각합니다." 나는하지 않을 것입니다. 함수가되면 있어야 목록-Y 기능, 다음 네, 그들에게 동일한 치료해야한다 (즉,리스트로부터, 이전 버전과 같은 일을 뱉어). 그러나 인수 중 하나가 문자열 또는 문자열 목록 (거의 일반적인 요구) 일 수있는 함수 인 경우 해당 함수를 사용하는 개발자가 항상 배열 내부에 매개 변수를 입력 하도록 강요하는 것은 약간 많이 보입니다. . 또한 JSON 입력을 처리하는 방법에 대해 생각하십시오. 문자열과 다른 객체 목록을 처리하고 싶을 것입니다.
Jordan Reiter

8

가독성과 모범 사례를 위해 다음을 시도하십시오.

파이썬 2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something

파이썬 3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something

도움이 되길 바랍니다.


Python 3.6.5 :AttributeError: module 'types' has no attribute 'ListType'
Juha Untinen

1
파이썬 3에서, 그것은 from typing import List-> isinstance([1, 2, 3], List= True이고 isinstance("asd", List)= False
Juha Untinen

5

str객체는없는 __iter__속성을

>>> hasattr('', '__iter__')
False 

그래서 당신은 확인을 할 수 있습니다

assert hasattr(x, '__iter__')

그리고 이것은 AssertionError반복 불가능한 다른 객체에도 좋을 것 입니다.

편집 : Tim이 주석에서 언급했듯이 이것은 3.x가 아닌 python 2.x에서만 작동합니다.


8
주의 : Python 3에서는을 hasattr('','__iter__')반환합니다 True. 물론 문자열을 반복 할 수 있기 때문에 의미가 있습니다.
Tim Pietzcker 2009

1
정말? 나는 몰랐다. 나는 항상 이것이 문제에 대한 우아한 해결책이라고 생각했다.
Moe

1
이 테스트는 pyodbc.Row에서 작동하지 않았습니다. 그것은 전혀 없습니다 ITER를 __ ()하지만 더 많거나 적은 동작합니다 목록 같은 (심지어 "__setitem을 정의 "). 요소를 잘 반복 할 수 있습니다. len () 함수가 작동하고 해당 요소를 색인화 할 수 있습니다. 모든 목록 유형을 포착하지만 문자열을 제외시키는 올바른 조합을 찾기 위해 고심하고 있습니다. 나는 기본 문자열 을 명시 적으로 배제하면서 " getitem "과 " len " 에 대한 점검을 할 것이라고 생각한다 .
haridsv

5

이것은 OP에 직접 대답하기위한 것이 아니라 관련 아이디어를 공유하고 싶었습니다.

나는 위의 @ steveha 답변에 매우 관심이 있었고 오리 타이핑이 깨지는 것처럼 보이는 예를 보였습니다. 그러나 두 번째 생각으로, 그의 예는 오리 타이핑이 준수하기 어렵다는 것을 제안하지만, 특별한 취급 이 필요 하다는 것은 아닙니다str .

결국, 비 str유형 (예를 들어, 복잡한 재귀 구조를 유지하는 사용자 정의 유형)은 @steveha srepr함수가 무한 재귀를 유발할 수 있습니다 . 이 방법은 다소 드물지만이 가능성을 무시할 수는 없습니다. 따라서, 특별-케이스 대신 strsrepr, 우리는 우리가 원하는 것을 명확히해야 srepr할 때 무한 재귀 결과 할 수 있습니다.

하나의 합리적인 접근 방식은 간단에서 재귀를 파괴하는 것입니다 것처럼 보일 수 있습니다 srepr순간 list(arg) == [arg]. 실제로 이것은로 문제를 완전히 해결할 strisinstance입니다.

그러나 매우 복잡한 재귀 구조는 list(arg) == [arg]결코 발생하지 않는 무한 루프를 일으킬 수 있습니다 . 따라서 위의 검사는 유용하지만 충분하지 않습니다. 재귀 깊이에 대한 하드 제한과 같은 것이 필요합니다.

내 요점은 임의의 인수 유형을 처리하려는 경우 str오리 이론을 통해 처리하는 것이 일반적인 이론 유형보다 더 일반적인 유형을 처리하는 것보다 훨씬 쉽다는 것입니다. 따라서 str인스턴스 를 제외 해야한다고 생각하면 인수가 명시 적으로 지정하는 몇 가지 유형 중 하나의 인스턴스 여야합니다.


1
흠, 나는 당신이 생각하는 방식을 좋아합니다. 내 코드가 실용적이라고 주장 할 수 없다고 생각 str합니다. 특수 사례 코드가 처리하는 공통 사례가 정확히 하나 있습니다. 그러나 코드가 검사 할 수있는 새로운 표준 속성 .__atomic__이있을 수 있습니다. atomic()파이썬 에 다른 내장 함수를 추가하기에는 너무 늦었을 수도 있지만 추가 from collections import atomic하거나 무언가를 추가 할 수 있습니다 .
steveha

5

tensorflow에서 is_sequence 라는 함수 를 찾습니다 .

def is_sequence(seq):
  """Returns a true if its input is a collections.Sequence (except strings).
  Args:
    seq: an input sequence.
  Returns:
    True if the sequence is a not a string and is a collections.Sequence.
  """
  return (isinstance(seq, collections.Sequence)
and not isinstance(seq, six.string_types))

그리고 귀하의 요구를 충족시키는 것으로 확인되었습니다.


2

내 테스트 케이스 에서이 작업을 수행합니다.

def assertIsIterable(self, item):
    #add types here you don't want to mistake as iterables
    if isinstance(item, basestring): 
        raise AssertionError("type %s is not iterable" % type(item))

    #Fake an iteration.
    try:
        for x in item:
            break;
    except TypeError:
        raise AssertionError("type %s is not iterable" % type(item))

발전기에서 테스트하지 않은 상태에서 발전기를 통과하면 다음 '수율'에 남아있어 다운 스트림을 망칠 수 있습니다. 그러나 다시, 이것은 '유니트 테스트'입니다


2

「오리 타이핑」방식으로 어떻습니까

try:
    lst = lst + []
except TypeError:
    #it's not a list

또는

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

각기. 이것은 isinstance/ hasattr내부 검사를 피합니다 .

당신은 또한 그 반대로 확인할 수 있습니다 :

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

모든 변형은 실제로 변수의 내용을 변경하지 않지만 재 할당을 암시합니다. 일부 환경에서는 이것이 바람직하지 않을 수 있습니다.

흥미롭게도, "in place"할당 으로 어떤 경우에도 리스트 가 없다면 ( tuple이 아님) 발생 +=하지 TypeError않을 것 입니다. 이것이 과제가 이런 식으로 수행되는 이유입니다. 어쩌면 누군가가 왜 그런지 밝힐 수 있습니다.lst


1

가장 간단한 방법은 ...을 사용 any하고isinstance

>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True

1

문자열 형 객체를 다른 시퀀스 형 객체와 구별하는 데 도움이되는 다른 오리 타이핑 버전입니다.

문자열 같은 객체의 문자열 표현은 문자열 자체이므로 str생성자 에서 동일한 객체를 다시 얻을 수 있는지 확인할 수 있습니다 .

# If a string was passed, convert it to a single-element sequence
if var == str(var):
    my_list = [var]

# All other iterables
else: 
    my_list = list(var)

이것은 str모든 종류의 반복 가능한 객체와 호환되는 모든 객체에 적용됩니다 .


0

파이썬 3에는 다음이 있습니다.

from typing import List

def isit(value):
    return isinstance(value, List)

isit([1, 2, 3])  # True
isit("test")  # False
isit({"Hello": "Mars"})  # False
isit((1, 2))  # False

따라서 List와 Tuple을 모두 확인하려면 다음과 같습니다.

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)

0
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

2
이것이 좋은 방법입니까?
ersh

1
SO에 오신 것을 환영합니다. 이 코드가 질문에 대답하는 이유에 대한 설명이 도움이 될 것입니다.
Nick

물론 파이프가 일종으로 취급되기 때문에 이와 유사한 방법을 사용하므로 유형이 오류 처리를 위해 사용자 정의 메시지 오류를 출력하는 목록 또는 유형 튜플이어야한다고 주장하고 있습니다. 나는 그것이 질문에 대한 대답이라고 생각하지만, 가장 최적화 된 코드를 작성하려고 노력 하면서이 작업을 수행하는 효과적인 방법 인 것처럼 궁금합니다. 그러나이 코드가 목록 / 튜플처럼 작동하지만 하위 클래스가 아닌 것들이 누락되어 수용 된 답변이 어떻게 그 가능성을 해결하는지 확실하지 않습니다. 감사!
ersh

-1

그냥하세요

if type(lst) in (list, tuple):
    # Do stuff

5
isinstance (lst, (list, tuple))
Davi Lima

@DaviLima OK, 다른 방법입니다. 그러나 type ()은 내장 형식과 클래스의 인스턴스에 권장됩니다.
ATOzTOA

-1

파이썬에서> 3.6

import collections
isinstance(set(),collections.abc.Container)
True
isinstance([],collections.abc.Container)
True
isinstance({},collections.abc.Container)
True
isinstance((),collections.abc.Container)
True
isinstance(str,collections.abc.Container)
False

2
마지막 검사에서는 str문자열이 아닌 type을 사용 합니다. 시도 isinstance('my_string', collections.abc.Container)하고 당신은 그것이 반환됩니다 볼 수 있습니다 True. 이 때문이다 abc.Container공급한다 __contains__방법, 문자열 물론, 그것이있다.
조지

-6

나는 이것을하는 경향이 있습니다 (내가 정말로 정말로해야한다면) :

for i in some_var:
   if type(i) == type(list()):
       #do something with a list
   elif type(i) == type(tuple()):
       #do something with a tuple
   elif type(i) == type(str()):
       #here's your string

5
거의이 작업을 수행해서는 안됩니다. some_var하위 클래스 인 클래스의 인스턴스 인 경우 어떻게됩니까 list()? 코드는 "목록으로 무언가 수행"코드에서 완벽하게 작동하지만 코드로 무엇을해야할지 전혀 모릅니다. 그리고리스트와 튜플의 차이점에 대해 거의 신경 쓸 필요가 없습니다. 죄송합니다 -1
steveha 2009

1
쓸 필요는 type(tuple())- tuple할 수 없습니다. 목록과 동일합니다. 또한 실제 문자열 유형 인 strunicodeextend 모두 basestring대신 확인하십시오.
당신의 개조를 잘 다루십시오

@ DrBloodmoney : 우발적 다운 보트. 다운 보트를 제거 할 수 있도록 답을 (사소하게) 편집하십시오.
SabreWolfy

평등은 유형에 대한 의미있는 비교처럼 보이지 않습니다. 대신 신원을 테스트합니다 type(i) is list. 또한, type(list())그냥 list그 자체 ... 마지막으로,이 서브 클래스와 함께 정상적으로 작동하지 않습니다. 경우 i사실과 OrderedDict, 또는 namedtuple의 일종이며, 취급이 코드를 문자열로입니다.
bukzor 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.