나는 파이썬에서 오리 타이핑의 오래된 방법에 익숙하기 때문에 ABC (추상 기본 클래스)의 필요성을 이해하지 못합니다. 도움 을 사용하는 방법에 좋다.
나는 PEP 의 이론적 근거를 읽으려고 노력했지만 내 머리 위로 넘어 갔다. 가변 시퀀스 컨테이너를 찾고 있다면를 확인 __setitem__
하거나 사용하려고 시도 할 가능성이 높습니다 ( EAFP ). ABC를 사용 하는 숫자 모듈에 대한 실제 사용을 보지 못했지만 그것이 가장 이해해야합니다.
누구든지 나에게 근거를 설명 할 수 있습니까?
답변
짧은 버전
ABC는 클라이언트와 구현 된 클래스간에 더 높은 수준의 의미 계약을 제공합니다.
긴 버전
클래스와 호출자간에 계약이 있습니다. 수업은 특정 일을하고 특정 속성을 갖도록 약속합니다.
계약에는 다른 수준이 있습니다.
매우 낮은 수준에서 계약에는 메소드 이름 또는 매개 변수 수가 포함될 수 있습니다.
정적으로 유형이 지정된 언어에서는 해당 계약이 실제로 컴파일러에 의해 시행됩니다. Python에서는 EAFP 또는 유형 검사를 사용 하여 알 수없는 객체가이 예상 계약을 충족하는지 확인할 수 있습니다 .
그러나 계약에는 더 높은 수준의 의미 론적 약속이 있습니다.
예를 들어, __str__()
메서드 가 있으면 개체의 문자열 표현을 반환해야합니다. 그것은 수 , 개체의 모든 내용을 삭제 트랜잭션을 커밋하고 프린터에서 빈 페이지를 침 …하지만 파이썬 매뉴얼에 설명이 무엇을해야하는지에 대한 공통의 이해가있다.
시맨틱 계약이 매뉴얼에 설명되어있는 특별한 경우입니다. print()
방법은 어떻게해야 합니까? 프린터에 물체를 써야합니까, 아니면 화면에 다른 물체를 써야합니까? 그것은 달려 있습니다-전체 계약을 이해하려면 주석을 읽어야합니다. print()
메소드가 존재 하는지 단순히 확인하는 클라이언트 코드 는 계약의 일부를 확인했습니다. 메소드 호출이 가능하지만 호출의 상위 레벨 의미에 대한 동의는 없습니다.
ACC (Abstract Base Class) 정의는 클래스 구현 자와 호출자 간의 계약을 생성하는 방법입니다. 메서드 이름의 목록 일뿐 아니라 해당 메서드의 기능에 대한 공통된 이해입니다. 이 ABC로부터 상속받은 경우, print()
메소드 의 의미를 포함하여 주석에 설명 된 모든 규칙을 준수 할 것을 약속합니다 .
파이썬의 오리 타이핑은 정적 타이핑보다 유연성이 많은 장점이 있지만 모든 문제를 해결하지는 못합니다. ABC는 자유 형식의 Python과 정적으로 유형이 지정된 언어의 속박과 훈련 사이의 중간 솔루션을 제공합니다.
답변
@Oddthinking의 대답은 틀린 것은 아니지만 파이썬이 오리 타이핑 세계에서 ABC를 가지고 있는 실제 적이고 실용적인 이유를 놓친 것 같습니다 .
추상적 인 방법은 깔끔하지만 내 의견으로는 오리 타이핑으로 아직 다루지 않은 유스 케이스를 실제로 채우지는 않습니다. 추상 기본 클래스의 실제 성능은 및 의 동작을 사용자 정의 할 수있는 방식에 있습니다isinstance
issubclass
. ( __subclasshook__
기본적으로 파이썬 __instancecheck__
과__subclasscheck__
훅 위에 친숙한 API입니다 .) 커스텀 타입에서 작동하도록 내장 구문을 적용하는 것은 파이썬 철학의 많은 부분입니다.
파이썬의 소스 코드는 모범적입니다. 표준 라이브러리에서 작성 하는 방법 은 다음과 같습니다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__
. 그래서 나는 이것을 쓸 수 있습니다 :
class ContainAllTheThings(object):
def __contains__(self, item):
return True
>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True
다시 말해, 올바른 인터페이스를 구현하면 하위 클래스입니다! ABC는 오리 타이핑의 정신에 충실하면서 파이썬에서 인터페이스를 정의하는 공식적인 방법을 제공합니다. 게다가, 이것은 공개 폐쇄 원칙 을 존중하는 방식으로 작동합니다 .
파이썬의 객체 모델은 좀 더 “전통적인”OO 시스템과 비슷하게 보입니다 (Java를 의미합니다). 우리는 yer 클래스, yer 객체, yer 메소드를 얻었습니다. 더 유연합니다. 마찬가지로, 파이썬의 추상 기본 클래스 개념은 Java 개발자가 인식 할 수 있지만 실제로는 매우 다른 목적으로 사용됩니다.
때로는 단일 항목이나 항목 모음에서 작동 할 수있는 다형성 함수를 작성 isinstance(x, collections.Iterable)
하는 경우가 많으며 hasattr(x, '__iter__')
동등한 try...except
블록 보다 훨씬 읽기 쉽습니다. (파이썬을 모른다면,이 세 가지 중 코드의 의도를 가장 명확하게하는 것은 무엇입니까?)
즉, 나는 내 ABC를 거의 쓸 필요가 없으며 일반적으로 리팩토링을 통해 ABC의 필요성을 발견합니다. 많은 속성 검사를 수행하는 다형성 함수 또는 동일한 속성 검사를 수행하는 많은 함수가 보이면 그 냄새는 ABC가 추출되기를 기다리고 있음을 나타냅니다.
* 자바가 “전통적인”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
불행히도이 중 하나는 “그냥하지 말라”는 함정 (파이썬이 비교적 적은 수의 함정) : a __subclasshook__
와 non-abstract 메소드로 ABC를 정의하지 마십시오 . 또한 __subclasshook__
ABC가 정의한 일련의 추상 메소드와 일치하도록 정의를 작성해야 합니다.
답변
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 구문을 포함하려면 @PandasRocks 덕분에
답변
프로토콜에서 모든 메소드의 존재를 확인하지 않고도 또는 지원되지 않기 때문에 “적자”영역에서 예외를 트리거하지 않고 오브젝트가 주어진 프로토콜을 지원하는지 여부를 판별합니다.
답변
추상 메서드는 부모 클래스에서 호출하는 메서드가 자식 클래스에 나타나야합니다. 아래는 추상을 호출하고 사용하는 특별한 방법입니다. 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()
‘methodone ()이 호출되었습니다’
c.methodtwo()
NotImplementedError
추상적 인 방법으로
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()
TypeError : 추상 메서드 methodtwo로 추상 클래스 Son을 인스턴스화 할 수 없습니다.
childclass에서 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()
‘methodone ()이 호출되었습니다’