파이썬에서 클래스 또는 메소드 추상화를 어떻게 만들 수 있습니까?
나는 __new__()
이렇게 재정의를 시도했다 .
class F:
def __new__(cls):
raise Exception("Unable to create an instance of abstract class %s" %cls)
그러나 이제 다음과 같이 G
상속되는 클래스 를 만들면 F
:
class G(F):
pass
그런 다음 G
슈퍼 클래스의 __new__
메소드를 호출하기 때문에 인스턴스화 할 수 없습니다 .
추상 클래스를 정의하는 더 좋은 방법이 있습니까?
답변
abc
모듈을 사용하여 추상 클래스를 작성 하십시오 . abstractmethod
데코레이터를 사용하여 메소드 요약을 선언하고 Python 버전에 따라 세 가지 방법 중 하나를 사용하여 클래스 요약을 선언하십시오.
Python 3.4 이상에서는에서 상속 할 수 있습니다 ABC
. 이전 버전의 Python에서는 클래스의 메타 클래스를로 지정해야합니다 ABCMeta
. 메타 클래스를 지정하는 것은 Python 3과 Python 2에서 다른 구문을 갖습니다. 세 가지 가능성은 다음과 같습니다.
# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
@abstractmethod
def foo(self):
pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
__metaclass__ = ABCMeta
@abstractmethod
def foo(self):
pass
어떤 방법을 사용하든 추상 메소드가있는 추상 클래스를 인스턴스화 할 수는 없지만 해당 메소드의 구체적인 정의를 제공하는 서브 클래스를 인스턴스화 할 수 있습니다.
>>> Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
... pass
...
>>> StillAbstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
... def foo(self):
... print('Hello, World')
...
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>
답변
구식 ( PEP 3119 이전 ) 방법 raise NotImplementedError
은 추상 메소드가 호출 될 때 추상 클래스에있는 것입니다.
class Abstract(object):
def foo(self):
raise NotImplementedError('subclasses must override foo()!')
class Derived(Abstract):
def foo(self):
print 'Hooray!'
>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]
이것은 abc
모듈 을 사용하는 것과 같은 좋은 속성을 가지고 있지 않습니다 . 여전히 추상 기본 클래스 자체를 인스턴스화 할 수 있으며 런타임에 추상 메소드를 호출 할 때까지 실수를 찾을 수 없습니다.
그러나 몇 가지 간단한 메서드를 사용하여 간단한 클래스의 작은 집합을 다루는 경우이 방법은 abc
문서를 살펴 보는 것보다 약간 쉽습니다 .
답변
ABC 모듈을 다루지 않고도 매우 쉬운 방법이 있습니다.
__init__
추상 클래스가 되고자하는 클래스 의 메소드에서, 자기의 “유형”을 확인할 수 있습니다. self 유형이 기본 클래스 인 경우 호출자는 기본 클래스를 인스턴스화하려고 시도하므로 예외를 발생시킵니다. 다음은 간단한 예입니다.
class Base():
def __init__(self):
if type(self) is Base:
raise Exception('Base is an abstract class and cannot be instantiated directly')
# Any initialization code
print('In the __init__ method of the Base class')
class Sub(Base):
def __init__(self):
print('In the __init__ method of the Sub class before calling __init__ of the Base class')
super().__init__()
print('In the __init__ method of the Sub class after calling __init__ of the Base class')
subObj = Sub()
baseObj = Base()
실행하면 다음이 생성됩니다.
In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__ method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
baseObj = Base()
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly
이는 기본 클래스에서 상속되는 서브 클래스를 인스턴스화 할 수 있지만 기본 클래스를 직접 인스턴스화 할 수는 없음을 나타냅니다.
답변
대부분의 이전 답변은 정확했지만 Python 3.7 의 답변과 예는 다음과 같습니다 . 예, 추상 클래스와 메서드를 만들 수 있습니다. 리마인더처럼 클래스는 논리적으로 클래스에 속하는 메소드를 정의해야하지만 해당 클래스는 메소드 구현 방법을 지정할 수 없습니다. 예를 들어, 아래의 부모와 아기 수업에서 둘 다 먹는 것이지만, 아기와 부모가 다른 종류의 음식을 먹고 먹는 횟수가 다르기 때문에 구현 방식이 각각 다릅니다. 따라서 eat 메소드 서브 클래스는 AbstractClass.eat를 대체합니다.
from abc import ABC, abstractmethod
class AbstractClass(ABC):
def __init__(self, value):
self.value = value
super().__init__()
@abstractmethod
def eat(self):
pass
class Parents(AbstractClass):
def eat(self):
return "eat solid food "+ str(self.value) + " times each day"
class Babies(AbstractClass):
def eat(self):
return "Milk only "+ str(self.value) + " times or more each day"
food = 3
mom = Parents(food)
print("moms ----------")
print(mom.eat())
infant = Babies(food)
print("infants ----------")
print(infant.eat())
산출:
moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day
답변
이것은 파이썬 3에서 작동합니다
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo
답변
다른 답변에서 설명한 것처럼 그렇습니다 . abc
모듈을 사용하여 Python에서 추상 클래스를 사용할 수 있습니다 . 나는 추상적 사용하여 실제 예를 들어 줄 아래 @classmethod
, @property
그리고 @abstractmethod
(3.6 Python을 사용). 나를 위해 일반적으로 쉽게 복사하여 붙여 넣을 수있는 예제로 시작하는 것이 더 쉽습니다. 이 답변이 다른 사람들에게도 유용하기를 바랍니다.
먼저 다음과 같은 기본 클래스를 만듭니다 Base
.
from abc import ABC, abstractmethod
class Base(ABC):
@classmethod
@abstractmethod
def from_dict(cls, d):
pass
@property
@abstractmethod
def prop1(self):
pass
@property
@abstractmethod
def prop2(self):
pass
@prop2.setter
@abstractmethod
def prop2(self, val):
pass
@abstractmethod
def do_stuff(self):
pass
우리 Base
클래스에는 항상 from_dict
classmethod
, a property
prop1
(읽기 전용) 및 property
prop2
(설정 가능) 및 라는 함수가 do_stuff
있습니다. 현재 어떤 클래스를 기반으로 구축하든 Base
메소드 / 속성을 위해 이들 클래스를 모두 구현해야합니다. – 방법이 추상적이 될 수 있도록, 두 장식이 필요하다는 것을 유의하시기 바랍니다 classmethod
추상적 property
.
이제 다음 A
과 같은 클래스를 만들 수 있습니다 .
class A(Base):
def __init__(self, name, val1, val2):
self.name = name
self.__val1 = val1
self._val2 = val2
@classmethod
def from_dict(cls, d):
name = d['name']
val1 = d['val1']
val2 = d['val2']
return cls(name, val1, val2)
@property
def prop1(self):
return self.__val1
@property
def prop2(self):
return self._val2
@prop2.setter
def prop2(self, value):
self._val2 = value
def do_stuff(self):
print('juhu!')
def i_am_not_abstract(self):
print('I can be customized')
필요한 모든 메소드 / 프로퍼티가 구현되었으며 물론 Base
여기에 포함 되지 않은 추가 함수를 추가 할 수도 있습니다 (여기 🙂 i_am_not_abstract
.
이제 우리는 할 수 있습니다 :
a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})
a1.prop1
# prints 10
a1.prop2
# prints 'stuff'
원하는대로 설정할 수 없습니다 prop1
.
a.prop1 = 100
돌아올 것이다
AttributeError : 속성을 설정할 수 없습니다
또한 우리의 from_dict
방법은 잘 작동합니다.
a2.prop1
# prints 20
이제 다음 B
과 같이 두 번째 클래스를 정의한 경우 :
class B(Base):
def __init__(self, name):
self.name = name
@property
def prop1(self):
return self.name
다음과 같이 객체를 인스턴스화하려고했습니다.
b = B('iwillfail')
우리는 오류를 얻을 것이다
TypeError : 추상 메소드 do_stuff, from_dict, prop2로 추상 클래스 B를 인스턴스화 할 수 없습니다
에 Base
구현하지 않은 모든 것을 나열합니다 B
.
답변
또한 이것은 작동하며 간단합니다.
class A_abstract(object):
def __init__(self):
# quite simple, old-school way.
if self.__class__.__name__ == "A_abstract":
raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")
class B(A_abstract):
pass
b = B()
# here an exception is raised:
a = A_abstract()