파이썬에서 추상 클래스를 만들 수 있습니까? raise

파이썬에서 클래스 또는 메소드 추상화를 어떻게 만들 수 있습니까?

나는 __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()