개발/Python

개체의 유형을 결정합니까?

MinorMan 2020. 9. 18. 02:36
반응형

<질문>

변수가 목록인지 사전인지, 아니면 다른 것인지 확인하는 간단한 방법이 있습니까? 두 유형 중 하나 일 수있는 개체를 되찾고 있으며 그 차이를 알 수 있어야합니다.


<답변1>

개체의 유형을 식별하는 데 도움이되는 두 가지 기본 제공 함수가 있습니다. 객체의 정확한 유형이 필요한 경우 type ()을 사용하고 객체의 유형을 무언가에 대해 확인하려면 isinstance ()를 사용할 수 있습니다. 일반적으로 isistance ()는 매우 강력하고 유형 상속도 지원하므로 대부분의 경우 사용합니다.

객체의 실제 유형을 얻으려면 내장 type () 함수를 사용합니다. 객체를 유일한 매개 변수로 전달하면 해당 객체의 유형 객체가 반환됩니다.

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True

물론 이것은 사용자 정의 유형에도 적용됩니다.

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

type ()은 객체의 즉각적인 유형 만 반환하지만 유형 상속에 대해서는 알려줄 수 없습니다.

>>> type(b) is Test1
False

이를 다루려면 isinstance 함수를 사용해야합니다. 물론 이것은 기본 제공 유형에서도 작동합니다.

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance ()는 파생 된 유형도 허용하므로 일반적으로 객체 유형을 확인하는 데 선호되는 방법입니다. 따라서 실제로 유형 객체가 필요하지 않으면 (어떤 이유로 든) isinstance () 사용이 type ()보다 선호됩니다.

isinstance ()의 두 번째 매개 변수는 유형의 튜플도 허용하므로 한 번에 여러 유형을 확인할 수 있습니다. 객체가 다음 유형 중 하나이면 isinstance는 true를 반환합니다.

>>> isinstance([], (tuple, list, set))
True

<답변2>

type ()을 사용하여 할 수 있습니다.

>>> a = []
>>> type(a)

>>> f = ()
>>> type(f)


<답변3>

try ... except 블록을 사용하는 것이 더 파이썬적일 수 있습니다. 이렇게하면 목록처럼 꽥꽥 거리는 클래스가 있거나 딕셔너리처럼 꽥꽥 거리는 클래스가 있으면 유형이 실제로 무엇인지에 관계없이 제대로 작동합니다.

명확히하기 위해 변수 유형 간의 "차이를 알려주는"방법은 덕 타이핑 (Duck Type)이라고하는 것입니다. 변수가 응답하는 메소드 (및 반환 유형)가 서브 루틴이 기대하는 것이라면 예상대로 처리합니다. 되려고. 예를 들어, getattr 및 setattr로 대괄호 연산자를 오버로드하는 클래스가 있지만 재미있는 내부 체계를 사용하는 경우 에뮬레이트하려는 경우 사전으로 동작하는 것이 적절할 것입니다.

type (A)의 다른 문제는 type (B) 검사입니다. A가 B의 하위 클래스 인 경우 프로그래밍 방식으로 true가되기를 바라는 경우 false로 평가됩니다. 객체가 목록의 하위 클래스 인 경우 목록처럼 작동해야합니다. 다른 답변에 표시된 유형을 확인하면이를 방지 할 수 있습니다. (그러나 isinstance는 작동합니다).


<답변4>

개체의 인스턴스에는 다음이 있습니다.

__class__

속성. 다음은 Python 3.3 콘솔에서 가져온 샘플입니다.

>>> str = "str"
>>> str.__class__

>>> i = 2
>>> i.__class__

>>> class Test():
...     pass
...
>>> a = Test()
>>> a.__class__

Python 3.x 및 New-Style 클래스 (Python 2.6에서 선택적으로 사용 가능)에서 클래스 및 유형이 병합되었으며 이로 인해 예기치 않은 결과가 발생할 수 있습니다. 주로 이런 이유로 타입 / 클래스를 테스트하는 가장 좋은 방법은 isinstance 내장 함수입니다.


<답변5>

유형을 사용하여 개체 유형 결정

>>> obj = object()
>>> type(obj)

작동하지만 __class__와 같은 이중 밑줄 속성은 피하십시오. 의미 상 공개되지 않으며,이 경우에는 아니지만 내장 함수가 일반적으로 더 나은 동작을합니다.

>>> obj.__class__ # avoid this!

변수가 목록인지 사전인지, 아니면 다른 것인지 확인하는 간단한 방법이 있습니까? 두 유형 중 하나 일 수있는 개체를 되찾고 있으며 그 차이를 알 수 있어야합니다.

다른 질문입니다. type을 사용하지 마세요. isinstance를 사용하세요.

def foo(obj):
    """given a string with items separated by spaces, 
    or a list or tuple, 
    do something sensible
    """
    if isinstance(obj, str):
        obj = str.split()
    return _foo_handles_only_lists_or_tuples(obj)

이것은 사용자가 str을 서브 클래 싱하여 영리하거나 현명한 작업을 수행 할 수있는 경우를 다룹니다. Liskov Substitution의 원칙에 따라 코드를 손상시키지 않고 서브 클래스 인스턴스를 사용할 수 있기를 원하며 isinstance가이를 지원합니다.

더 좋은 점은 컬렉션이나 숫자에서 특정 추상 기본 클래스를 찾을 수 있다는 것입니다.

from collections import Iterable
from numbers import Number

def bar(obj):
    """does something sensible with an iterable of numbers, 
    or just one number
    """
    if isinstance(obj, Number): # make it a 1-tuple
        obj = (obj,)
    if not isinstance(obj, Iterable):
        raise TypeError('obj must be either a number or iterable of numbers')
    return _bar_sensible_with_iterable(obj)

또는 가장 좋은 방법은 덕 타이핑을 사용하고 코드를 명시 적으로 입력하지 않는 것입니다. 덕 타이핑은 더 우아하고 자세한 설명으로 Liskov Substitution을 지원합니다.

def baz(obj):
    """given an obj, a dict (or anything with an .items method) 
    do something sensible with each key-value pair
    """
    for key, value in obj.items():
        _baz_something_sensible(key, value)

<답변6>

type () 또는 isinstance ()를 사용할 수 있습니다.

>>> type([]) is list
True

동일한 이름의 현재 범위에 변수를 할당하여 목록 또는 다른 유형을 제거 할 수 있다는 점에주의하십시오.

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

위에서 우리는 dict가 문자열에 다시 할당되는 것을 볼 수 있으므로 테스트는 다음과 같습니다.

type({}) is dict

... 실패.

이 문제를 해결하고 type ()을 더 조심스럽게 사용하려면 :

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True

<답변7>

질문이 꽤 오래되었지만 적절한 방법을 찾는 동안 이것을 우연히 발견했으며 적어도 Python 2.x에 대해서는 여전히 명확해야한다고 생각합니다 (Python 3에서는 확인하지 않았지만 문제는 클래식 클래스에서 발생하기 때문에 그러한 버전에서 사라진 것은 아마도 중요하지 않습니다).

여기에서 제목의 질문에 답하려고합니다. 임의의 객체 유형을 어떻게 결정할 수 있습니까? isinstance를 사용하거나 사용하지 않는 것에 대한 다른 제안은 많은 의견과 답변에서 괜찮지 만 그러한 문제를 다루지는 않습니다.

type () 접근 방식의 주요 문제는 이전 스타일 인스턴스에서 제대로 작동하지 않는다는 것입니다.

class One:
    pass

class Two:
    pass


o = One()
t = Two()

o_type = type(o)
t_type = type(t)

print "Are o and t instances of the same class?", o_type is t_type

이 스 니펫을 실행하면 다음이 생성됩니다.

Are o and t instances of the same class? True

나는 대부분의 사람들이 기대하는 것이 아니라고 주장합니다.

__class__ 접근 방식은 정확성에 가장 가깝지만 한 가지 중요한 경우에는 작동하지 않습니다. 전달 된 객체가 인스턴스가 아닌 구식 클래스 인 경우 해당 객체에는 이러한 속성이 없기 때문입니다.

이것은 일관된 방식으로 이러한 합법적 인 질문을 만족시키는 가장 작은 코드 조각입니다.

#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
    obj_type = getattr(obj, "__class__", _NO_CLASS)
    if obj_type is not _NO_CLASS:
        return obj_type
    # AFAIK the only situation where this happens is an old-style class
    obj_type = type(obj)
    if obj_type is not ClassType:
        raise ValueError("Could not determine object '{}' type.".format(obj_type))
    return obj_type

<답변8>

isinstance 사용에주의

isinstance(True, bool)
True
>>> isinstance(True, int)
True

하지만 입력

type(True) == bool
True
>>> type(True) == int
False

<답변9>

이전 답변 외에도 duck-typing을 보완하는 여러 추상 기본 클래스 (ABC)를 포함하는 collections.abc의 존재를 언급 할 가치가 있습니다.

예를 들어 다음을 사용하여 목록이 있는지 명시 적으로 확인하는 대신

isinstance(my_obj, list)

가지고있는 객체가 항목을 가져올 수 있는지 확인하는 데에만 관심이 있다면 collections.abc.Sequence를 사용할 수 있습니다.

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

항목 (즉, 변경 가능한 시퀀스)을 가져오고, 설정하고, 삭제할 수있는 객체에 엄격하게 관심이 있다면 collections.abc.MutableSequence를 선택하십시오.

다른 많은 ABC가 정의되어 있습니다. Mapping for objects that can be used as maps, Iterable, Callable, etc. 이 모든 것의 전체 목록은 collections.abc에 대한 문서에서 볼 수 있습니다.


<답변10>

일반적으로 클래스 이름이있는 객체에서 문자열을 추출 할 수 있습니다.

str_class = object.__class__.__name__

비교를 위해 사용하면

if str_class == 'dict':
    # blablabla..
elif str_class == 'customclass':
    # blebleble..

<답변11>

많은 실제 경우에서 type 또는 isinstance를 사용하는 대신 @ functools.singledispatch를 사용하여 일반 함수 (다른 유형에 대해 동일한 작업을 구현하는 여러 함수로 구성된 함수)를 정의 할 수도 있습니다.

즉, 다음과 같은 코드가있을 때 사용하고 싶을 것입니다.

def do_something(arg):
    if isinstance(arg, int):
        ... # some code specific to processing integers
    if isinstance(arg, str):
        ... # some code specific to processing strings
    if isinstance(arg, list):
        ... # some code specific to processing lists
    ...  # etc

다음은 작동 방식에 대한 간단한 예입니다.

from functools import singledispatch


@singledispatch
def say_type(arg):
    raise NotImplementedError(f"I don't work with {type(arg)}")


@say_type.register
def _(arg: int):
    print(f"{arg} is an integer")


@say_type.register
def _(arg: bool):
    print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with 

추가로 추상 클래스를 사용하여 한 번에 여러 유형을 다룰 수 있습니다.

from collections.abc import Sequence


@say_type.register
def _(arg: Sequence):
    print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!

<답변12>

type ()은 특히 부울의 경우 isinstance ()보다 나은 솔루션입니다.

True와 False는 파이썬에서 1과 0을 의미하는 키워드입니다. 그러므로,

isinstance(True, int)

isinstance(False, int)

둘 다 True를 반환합니다. 두 부울 모두 정수의 인스턴스입니다. 그러나 type ()은 더 영리합니다.

type(True) == int

False를 반환합니다.


<답변13>

완전성을 위해 isinstance는 인스턴스가 아닌 하위 유형의 유형 검사에 대해 작동하지 않습니다. 그것이 완벽하게 이해되지만 (수락 된 답변을 포함하여) 어떤 답변도 그것을 다루지 않습니다. 이를 위해 issubclass를 사용하십시오.

>>> class a(list):
...   pass
... 
>>> isinstance(a, list)
False
>>> issubclass(a, list)
True
반응형