객체가 목록인지 튜플인지 확인하는 방법 (문자열은 아님)? 버그를 여러 번 발견했기

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

assert isinstance(lst, (list, tuple))

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



답변

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

assert not isinstance(lst, basestring)

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


답변

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

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

다음은 내가 재미있게 작성한 기능입니다. 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__()므로 위의 내용을 변경했습니다.


답변

H = "Hello"

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


답변

파이썬 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"


답변

PHP 풍미를 가진 파이썬 :

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


답변

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

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

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


답변

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

파이썬 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

도움이 되길 바랍니다.