จะใช้วิธีการเสมือนใน Python ได้อย่างไร?


89

ฉันรู้จักวิธีการเสมือนจริงจาก PHP หรือ Java

จะนำไปใช้ใน Python ได้อย่างไร?

หรือฉันต้องกำหนดวิธีการว่างเปล่าในคลาสนามธรรมแล้วลบล้าง?

คำตอบ:


106

แน่นอนและคุณไม่จำเป็นต้องกำหนดวิธีการในคลาสพื้นฐาน ในหลามวิธีการที่ดีกว่าเสมือน - พวกเขากำลังแบบไดนามิกอย่างสมบูรณ์การพิมพ์ในหลามเป็นเป็ดพิมพ์

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()

CatและDogใน Python ไม่จำเป็นต้องได้มาจากคลาสพื้นฐานทั่วไปเพื่ออนุญาตพฤติกรรมนี้ - คุณจะได้รับมันฟรี ที่กล่าวว่าโปรแกรมเมอร์บางคนชอบที่จะกำหนดลำดับชั้นของชั้นเรียนของตนในทางที่เข้มงวดมากขึ้นเพื่อจัดทำเอกสารได้ดีและกำหนดบางเข้มงวดของการพิมพ์ นอกจากนี้ยังเป็นไปได้ - ดูตัวอย่างโมดูลมาตรฐานabc


36
+1 สำหรับตัวอย่าง สุนัขพูดว่า "hau" ในภาษาอะไร?
JeremyP

5
@JeremyP: อืมจุดดี :-) ฉันเดาว่าในภาษาที่เข้าใจว่า "h" ทำให้เสียงเหมือนอักษรตัวแรกของ "hippy" หรือของ "Javier" ในภาษาสเปน
Eli Bendersky

5
@ เอลี: ขออภัย แต่ฉันสนใจคำตอบของคำถามอย่างจริงจัง ในภาษาอังกฤษพวกเขาพูดว่า "woof" แต่นั่นเป็นคำที่เราใช้คล้ายกับ "meow" สำหรับแมวและ "moo" สำหรับวัว แล้ว "hau" เป็นภาษาสเปนหรือไม่?
JeremyP

9
@JeremyP ฉันรู้แน่นอนว่านั่นคือสิ่งที่สุนัขโปแลนด์พูด;)
j_kubik

2
@JeremyP ใช่ฉันยืนยันว่าสุนัขพูดว่า "Jau" เป็นภาษาสเปนและเมื่อเขียนเป็นภาษาอังกฤษจะเป็น "Hau" :) hth
SkyWalker

71

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

ที่เกี่ยวข้อง: สามารถสร้างคลาสนามธรรมใน Python ได้หรือไม่?


55

วิธีการ Python เป็นเสมือนจริงเสมอ


ยกเว้นวิธีการดักฟัง
Konstantin

1
คำตอบนี้ไม่ได้ช่วยในวัตถุประสงค์ของการใช้คลาสอินเทอร์เฟซซึ่งเป็นหนึ่งในเหตุผลหลักในการใช้วิธีการเสมือนจริง
Jean-Marc Volle

21

จริงๆแล้วในเวอร์ชัน 2.6 python มีสิ่งที่เรียกว่าคลาสพื้นฐานแบบนามธรรมและคุณสามารถตั้งค่าวิธีการเสมือนอย่างชัดเจนได้ดังนี้:

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 เป็นเสมือนจริงเสมอ

เช่นเดียวกับที่อิกนาซิโอกล่าว แต่การสืบทอดคลาสอาจเป็นแนวทางที่ดีกว่าในการนำสิ่งที่คุณต้องการไปใช้

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 ที่ทำงานเช่นนั้น วิธี Dunder ไม่ และฉันคิดว่านั่นเป็นคุณสมบัติที่ไม่ค่อยเป็นที่รู้จัก

ลองพิจารณาตัวอย่างเทียมนี้

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()วิธีการดักฟัง

ปัญหาที่มีลักษณะการทำงานที่แรกที่สามารถเป็นไปได้ว่าคุณจะไม่สามารถเปลี่ยนพฤติกรรมของในชั้นเรียนมาโดยไม่ส่งผลกระทบต่อการทำงานของprop_a() คำพูดที่ดีมากโดย Raymond Hettinger ยกตัวอย่างกรณีการใช้งานที่ไม่สะดวกprop_b()

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.