Python에서 가상 메서드를 구현하는 방법은 무엇입니까?


88

PHP 또는 Java의 가상 방법을 알고 있습니다.

파이썬에서 어떻게 구현할 수 있습니까?

아니면 추상 클래스에서 빈 메서드를 정의하고 재정의해야합니까?

답변:


104

물론, 기본 클래스에서 메서드를 정의 할 필요조차 없습니다. 파이썬에서 메서드는 가상보다 낫습니다 . 파이썬에서 타이핑하는 것이 오리 타이핑 이기 때문에 완전히 동적 입니다.

class Dog:
  def say(self):
    print "hau"

class Cat:
  def say(self):
    print "meow"

pet = Dog()
pet.say() # prints "hau"
another_pet = Cat()
another_pet.say() # prints "meow"

my_pets = [pet, another_pet]
for a_pet in my_pets:
  a_pet.say()

CatDog파이썬에서도이 동작을 할 수 있도록 공통 기본 클래스에서 파생 필요가 없습니다 - 당신은 무료로 얻을 수 있습니다. 즉 어떤 프로그래머가 더 잘 문서화하고 부과하는 더 엄격한 방법으로 자신의 클래스 계층을 정의하는 것을 선호했다 일부 입력의 엄격합니다. 이것은 또한 가능합니다-예를 들어 abc표준 모듈을보십시오 .


32
예를 들어 +1. 그런데 개는 어떤 언어로 "하우"라고 말합니까?
JeremyP 2011 년

4
@JeremyP : 흠, 좋은 점 :-) "h"가 "hippy"의 첫 글자 나 스페인어로 "Javier"와 같은 소리를내는 것으로 이해되는 언어에서는 추측합니다.
Eli Bendersky 2011 년

4
@Eli : 미안하지만 질문에 대한 답에 진지하게 관심이있었습니다. 영어에서는 "woof"라고 말하지만 그렇지는 않지만 우리가 사용하는 단어는 고양이의 경우 "meow", 소의 경우 "moo"와 유사합니다. 그럼 "hau"가 스페인어인가요?
JeremyP

9
내가 확실히 알고 @JeremyP 무엇의 폴란드 개는 말할 것을)
j_kubik

2
@JeremyP 예 개가 스페인어로 "Jau"라고 말하고 영어로 작성하면 "Hau"가됩니다. :) hth
SkyWalker

67

raise NotImplementedError()

메서드를 구현하지 않는 "추상"기본 클래스의 "순수 가상 메서드"에서 발생하는 권장 예외입니다.

https://docs.python.org/3.5/library/exceptions.html#NotImplementedError 말한다 :

이 예외는 RuntimeError. 사용자 정의 기본 클래스에서 추상 메서드는 메서드를 재정의하기 위해 파생 클래스가 필요할 때이 예외를 발생시켜야합니다.

다른 사람들이 말했듯이 이것은 대부분 문서 규칙이며 필수는 아니지만 이렇게하면 누락 된 속성 오류보다 더 의미있는 예외가 발생합니다.

예 :

class Base(object):
    def virtualMethod(self):
        raise NotImplementedError()
    def usesVirtualMethod(self):
        return self.virtualMethod() + 1

class Derived(Base):
    def virtualMethod(self):
        return 1

print Derived().usesVirtualMethod()
Base().usesVirtualMethod()

제공합니다 :

2
Traceback (most recent call last):
  File "./a.py", line 13, in <module>
    Base().usesVirtualMethod()
  File "./a.py", line 6, in usesVirtualMethod
    return self.virtualMethod() + 1
  File "./a.py", line 4, in virtualMethod
    raise NotImplementedError()
NotImplementedError

관련 : 파이썬에서 추상 클래스를 만드는 것이 가능합니까?


52

Python 메서드는 항상 가상입니다.


던더 방법을 제외하고.
Konstantin

이 대답은 가상 메서드를 사용하는 주된 이유 중 하나 인 인터페이스 클래스를 구현하려는 목적에 실제로 도움이되지 않습니다.
Jean-Marc Volle

21

실제로 버전 2.6에서 파이썬은 추상 기본 클래스 라는 것을 제공하며 다음 과 같이 명시 적으로 가상 메서드를 설정할 수 있습니다.

from abc import ABCMeta
from abc import abstractmethod
...
class C:
    __metaclass__ = ABCMeta
    @abstractmethod
    def my_abstract_method(self, ...):

클래스가 이미 메타 클래스를 사용하는 클래스에서 상속하지 않는 경우 매우 잘 작동합니다.

출처 : http://docs.python.org/2/library/abc.html


이 지시문에 해당하는 Python 3이 있습니까?
locke14

9

Python 메서드는 항상 가상입니다.

Ignacio가 말했듯이 어쨌든 클래스 상속은 원하는 것을 구현하는 더 나은 접근 방식 일 수 있습니다.

class Animal:
    def __init__(self,name,legs):
        self.name = name
        self.legs = legs

    def getLegs(self):
        return "{0} has {1} legs".format(self.name, self.legs)

    def says(self):
        return "I am an unknown animal"

class Dog(Animal): # <Dog inherits from Animal here (all methods as well)

    def says(self): # <Called instead of Animal says method
        return "I am a dog named {0}".format(self.name)

    def somethingOnlyADogCanDo(self):
        return "be loyal"

formless = Animal("Animal", 0)
rover = Dog("Rover", 4) #<calls initialization method from animal

print(formless.says()) # <calls animal say method

print(rover.says()) #<calls Dog says method
print(rover.getLegs()) #<calls getLegs method from animal class

결과는 다음과 같아야합니다.

I am an unknown animal
I am a dog named Rover
Rover has 4 legs

4

C ++의 가상 메서드 (기본 클래스에 대한 참조 또는 포인터를 통해 파생 클래스의 메서드 구현 호출)와 같은 것은 Python에서 입력이 없기 때문에 Python에서는 의미가 없습니다. (하지만 가상 메서드가 Java와 PHP에서 어떻게 작동하는지 모르겠습니다.)

그러나 "가상"이란 상속 계층 구조에서 최하위 구현을 호출하는 것을 의미한다면 여러 답변이 지적했듯이 Python에서 항상 얻는 것입니다.

글쎄, 거의 항상 ...

dplamp가 지적했듯이 Python의 모든 메소드가 그렇게 작동하는 것은 아닙니다. 던더 방법은하지 않습니다. 그다지 잘 알려지지 않은 기능이라고 생각합니다.

이 인공적인 예를 고려하십시오

class A:
    def prop_a(self):
        return 1
    def prop_b(self):
        return 10 * self.prop_a()

class B(A):
    def prop_a(self):
        return 2

지금

>>> B().prop_b()
20
>>> A().prob_b()
10

그러나 이것을 고려하십시오

class A:
    def __prop_a(self):
        return 1
    def prop_b(self):
        return 10 * self.__prop_a()

class B(A):
    def __prop_a(self):
        return 2

지금

>>> B().prop_b()
10
>>> A().prob_b()
10

우리가 변경 한 유일한 것은 prop_a()dunder 방법 을 만드는 것입니다.

첫 번째 동작의 문제는의 동작에 prop_a()영향을주지 않고 파생 클래스 의 동작을 변경할 수 없다는 것 입니다 prop_b(). Raymond Hettinger 의이 멋진 강연은 이것이 불편한 사용 사례에 대한 예를 제공합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.