복붙노트

[PYTHON] 파이썬에서 추상 기본 클래스를 사용하는 이유는 무엇입니까?

PYTHON

파이썬에서 추상 기본 클래스를 사용하는 이유는 무엇입니까?

필자는 Python에서 오래된 타이핑 방식에 익숙하기 때문에 ABC (추상 기본 클래스)의 필요성을 이해하지 못합니다. 도움은 그들을 사용하는 방법에 좋습니다.

나는 PEP에서 그 이론적 근거를 읽으려고 애썼지 만 그것은 내 머리 위로 갔다. 변경할 수있는 시퀀스 컨테이너를 찾고 있다면 __setitem__을 확인하거나 더 많이 사용하려고합니다 (EAFP). 나는 ABC를 사용하는 numbers 모듈에 대한 실제 사용을 보지 못했지만 이해해야 할 가장 가까운 모듈입니다.

누구든지 나에게 이론적 설명을 해줄 수 있니?

해결법

  1. ==============================

    1.ABC는 클라이언트와 구현 된 클래스간에 더 높은 수준의 의미 계약을 제공합니다.

    ABC는 클라이언트와 구현 된 클래스간에 더 높은 수준의 의미 계약을 제공합니다.

    학급과 그 발신자 사이에는 계약이 있습니다. 수업은 특정 일을하고 특정 속성을 가질 것을 약속합니다.

    계약에는 여러 수준이 있습니다.

    매우 낮은 수준에서 계약에는 메소드의 이름 또는 매개 변수의 수를 포함 할 수 있습니다.

    정적 형식의 언어에서이 계약은 실제로 컴파일러에 의해 시행됩니다. 파이썬에서는 AFP 또는 인트로 스펙 션을 사용하여 알 수없는 객체가이 예상 계약을 충족시키는 지 확인할 수 있습니다.

    그러나 계약에는 더 높은 수준의 의미 론적 약속이 있습니다.

    예를 들어 __str __ () 메서드가 있으면 객체의 문자열 표현을 반환해야합니다. 객체의 모든 내용을 삭제하고, 트랜잭션을 커밋하고, 빈 페이지를 프린터 밖으로 침을 뱉을 수는 있지만, 파이썬 매뉴얼에 설명되어있는대로 무엇을해야하는지에 대한 일반적인 이해가 있습니다.

    이것은 의미 론적 계약이 매뉴얼에 기술되어있는 특별한 경우입니다. print () 메서드는 무엇을해야합니까? 객체를 프린터 나 라인에 화면에 씁니까? 아니면 다른 것을 써야합니까? 그것은 달려 있습니다 - 여기에 전체 계약을 이해하기 위해 코멘트를 읽을 필요가 있습니다. print () 메서드가 있는지 단순히 확인하는 클라이언트 코드 조각은 계약의 일부를 확인했습니다. 메서드 호출은 가능하지만 호출의 상위 수준 의미에 대해서는 동의하지 않습니다.

    추상 기본 클래스 (ABC)를 정의하는 것은 클래스 구현 자와 호출자간에 계약을 생성하는 방법입니다. 메서드 이름의 목록 일뿐만 아니라 이러한 메서드가 수행해야하는 작업에 대한 공유 된 이해입니다. 이 ABC에서 상속받은 경우 print () 메서드의 의미를 비롯하여 주석에 설명 된 모든 규칙을 따르겠다고 약속하고 있습니다.

    파이썬의 오리 타이핑은 정적 타이핑에 비해 많은 이점을 가지고 있지만 모든 문제를 해결하지는 못합니다. ABC는 자유형의 Python과 정적 형식 언어의 속박 및 규율 사이의 중간 솔루션을 제공합니다.

  2. ==============================

    2.@ Oddthinking의 대답은 잘못이 아니지만 파이썬에는 오리 타이핑의 세계에서 ABC가있는 실제적이고 실용적인 이유가 빠져 있다고 생각합니다.

    @ Oddthinking의 대답은 잘못이 아니지만 파이썬에는 오리 타이핑의 세계에서 ABC가있는 실제적이고 실용적인 이유가 빠져 있다고 생각합니다.

    추상적 인 방법은 깔끔하지만, 제 생각에 그들은 오리 입력에 아직 적용되지 않은 사용 사례를 실제로 채우지 않습니다. 추상 기본 클래스의 진정한 힘은 isinstance 및 issubclass의 동작을 사용자 정의 할 수있는 방법에 있습니다. (__subclasshook__은 기본적으로 Python의 __instancecheck__ 및 __subclasscheck__ 훅에 더 친숙한 API입니다.) 내장 된 구문을 사용자 정의 유형에 적용하는 것은 Python의 철학 중 상당 부분을 차지합니다.

    파이썬의 소스 코드는 모범적입니다. collections.Container가 표준 라이브러리에 정의 된 방법은 다음과 같습니다 (작성 시점).

    class Container(metaclass=ABCMeta):
        __slots__ = ()
    
        @abstractmethod
        def __contains__(self, x):
            return False
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Container:
                if any("__contains__" in B.__dict__ for B in C.__mro__):
                    return True
            return NotImplemented
    

    이 __subclasshook__의 정의에 따르면 __contains__ 특성이있는 클래스는 직접 하위 클래스로 분류되지 않더라도 Container의 하위 클래스로 간주됩니다. 그래서 이것을 쓸 수 있습니다 :

    class ContainAllTheThings(object):
        def __contains__(self, item):
            return True
    
    >>> issubclass(ContainAllTheThings, collections.Container)
    True
    >>> isinstance(ContainAllTheThings(), collections.Container)
    True
    

    즉, 올바른 인터페이스를 구현하면 하위 클래스가됩니다! ABC는 Python에서 인터페이스를 정의하는 공식적인 방법을 제공하는 동시에 오리 타이핑의 정신에 충실합니다. 게다가이 방법은 공개 원칙을 존중하는 방식으로 작동합니다.

    파이썬의 객체 모델은 표면적으로 더 전통적인 "OO"시스템 (자바 *를 의미)과 유사합니다. 우리는 클래스, yer 객체, yer 메소드를 갖지만, 여러분이 표면을 긁을 때 훨씬 더 풍부한 것을 발견하게 될 것입니다. 더 유연합니다. 마찬가지로, 파이썬의 추상 기본 클래스 개념은 자바 개발자에게 인식 될 수도 있지만, 실제로는 매우 다른 목적으로 사용됩니다.

    때로는 하나의 아이템이나 아이템 콜렉션에 작용할 수있는 다형 함수를 작성하고, isattance (x, collections.Iterable)가 hasattr (x, '__iter__') 또는 이와 동등한 시도보다 훨씬 더 읽기 쉽다고 생각합니다. .. 블록을 제외하고. (파이썬에 대해 잘 모르는 경우, 세 가지 중 어느 것이 코드의 의도를 가장 명확하게 나타낼 것입니까?)

    나는 드물게 자신의 ABC를 쓸 필요가 없다는 것을 알았다. 나는 오리 타이핑에 의존하기를 좋아한다. 그리고 나는 일반적으로 리팩토링을 통해 필요성을 발견했다. 많은 속성 검사를하는 다형성 함수 나 동일한 속성 검사를하는 많은 함수를 보면, 그 냄새는 추출을 기다리는 ABC의 존재를 암시합니다.

    Java가 전통적인 "OO"시스템인지에 대한 논쟁에 빠지지 않고 *

    부록 : 추상 기본 클래스는 isinstance 및 issubclass의 동작을 무시할 수 있지만 여전히 가상 하위 클래스의 MRO를 입력하지는 않습니다. 이것은 클라이언트를위한 잠재적 인 함정입니다 : isinstance (x, MyABC) == True 인 모든 객체가 MyABC에 정의 된 메소드를 가지고있는 것은 아닙니다.

    class MyABC(metaclass=abc.ABCMeta):
        def abc_method(self):
            pass
        @classmethod
        def __subclasshook__(cls, C):
            return True
    
    class C(object):
        pass
    
    # typical client code
    c = C()
    if isinstance(c, MyABC):  # will be true
        c.abc_method()  # raises AttributeError
    

    불행하게도이 중 하나는 "그냥하지 마라"는 함정 (파이썬에는 상대적으로 적다!) : __subclasshook__과 비 추상적 인 방법으로 ABC를 정의하지 않는다. 또한 __subclasshook__에 대한 정의를 ABC에서 정의한 추상 메소드 세트와 일치시켜야합니다.

  3. ==============================

    3.ABC의 편리한 기능은 필요한 모든 메서드 (및 속성)를 구현하지 않으면 누락 된 메서드를 실제로 사용하려고 할 때 AttributeError 대신 인스턴스화시 오류가 발생할 가능성이 훨씬 높다는 것입니다.

    ABC의 편리한 기능은 필요한 모든 메서드 (및 속성)를 구현하지 않으면 누락 된 메서드를 실제로 사용하려고 할 때 AttributeError 대신 인스턴스화시 오류가 발생할 가능성이 훨씬 높다는 것입니다.

    from abc import ABCMeta, abstractmethod
    
    # python2
    class Base(object):
        __metaclass__ = ABCMeta
    
        @abstractmethod
        def foo(self):
            pass
    
        @abstractmethod
        def bar(self):
            pass
    
    # python3
    class Base(object, metaclass=ABCMeta):
        @abstractmethod
        def foo(self):
            pass
    
        @abstractmethod
        def bar(self):
            pass
    
    class Concrete(Base):
        def foo(self):
            pass
    
        # We forget to declare `bar`
    
    
    c = Concrete()
    # TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"
    

    https://dbader.org/blog/abstract-base-classes-in-python의 예

    편집 : python3 구문을 포함하여 감사합니다 @ PanasRocks

  4. ==============================

    4.프로토콜에서 모든 메소드의 존재 여부를 확인하거나 비 지원으로 인해 "적"영역의 예외를 훨씬 쉽게 트리거하지 않고도 객체가 주어진 프로토콜을 지원하는지 여부를 결정합니다.

    프로토콜에서 모든 메소드의 존재 여부를 확인하거나 비 지원으로 인해 "적"영역의 예외를 훨씬 쉽게 트리거하지 않고도 객체가 주어진 프로토콜을 지원하는지 여부를 결정합니다.

  5. ==============================

    5.추상 메서드는 부모 클래스에서 호출하는 메서드가 자식 클래스에 있어야하는지 확인합니다. 아래는 추상을 사용하고 사용하는 noraml 방법입니다. python3로 작성된 프로그램

    추상 메서드는 부모 클래스에서 호출하는 메서드가 자식 클래스에 있어야하는지 확인합니다. 아래는 추상을 사용하고 사용하는 noraml 방법입니다. python3로 작성된 프로그램

    일반적인 통화 방법

    class Parent:
    def methodone(self):
        raise NotImplemented()
    
    def methodtwo(self):
        raise NotImplementedError()
    
    class Son(Parent):
       def methodone(self):
           return 'methodone() is called'
    
    c = Son()
    c.methodone()
    
    c.methodtwo()
    

    추상 방법으로

    from abc import ABCMeta, abstractmethod
    
    class Parent(metaclass=ABCMeta):
        @abstractmethod
        def methodone(self):
            raise NotImplementedError()
        @abstractmethod
        def methodtwo(self):
            raise NotImplementedError()
    
    class Son(Parent):
        def methodone(self):
            return 'methodone() is called'
    
    c = Son()
    

    자식 클래스에서는 methodtwo가 호출되지 않으므로 오류가 발생합니다. 적절한 구현은 다음과 같습니다.

    from abc import ABCMeta, abstractmethod
    
    class Parent(metaclass=ABCMeta):
        @abstractmethod
        def methodone(self):
            raise NotImplementedError()
        @abstractmethod
        def methodtwo(self):
            raise NotImplementedError()
    
    class Son(Parent):
        def methodone(self):
            return 'methodone() is called'
        def methodtwo(self):
            return 'methodtwo() is called'
    
    c = Son()
    c.methodone()
    
  6. from https://stackoverflow.com/questions/3570796/why-use-abstract-base-classes-in-python by cc-by-sa and MIT license