development

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

big-blog 2020. 2. 19. 22:05
반응형

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


이것이 내가 일반적으로 입력이 있음을 확인하기 위하여 않는 것입니다 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

도움이 되길 바랍니다.


str객체는없는 __iter__속성을

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

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

assert hasattr(x, '__iter__')

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

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


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

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

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

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

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

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


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

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


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

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

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


가장 간단한 방법은 ...을 사용 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

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

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


그냥하세요

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

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

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

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

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

참고 URL : https://stackoverflow.com/questions/1835018/how-to-check-if-an-object-is-a-list-or-tuple-but-not-string



반응형