Bagaimana cara menerapkan metode virtual dengan Python?

88

Saya tahu metode virtual dari PHP atau Java.

Bagaimana mereka bisa diimplementasikan dengan Python?

Atau haruskah saya mendefinisikan metode kosong dalam kelas abstrak dan menimpanya?

Meloun
sumber

Jawaban:

104

Tentu, dan Anda bahkan tidak perlu mendefinisikan metode di kelas dasar. Dalam Python, metode lebih baik daripada virtual - metode ini benar-benar dinamis, karena pengetikan dengan Python adalah pengetikan bebek .

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

Catdan Dogdi Python bahkan tidak harus diturunkan dari kelas dasar yang sama untuk mengizinkan perilaku ini - Anda mendapatkannya secara gratis. Meskipun demikian, beberapa programmer lebih memilih untuk mendefinisikan hierarki kelas mereka dengan cara yang lebih kaku untuk mendokumentasikannya dengan lebih baik dan menerapkan beberapa ketelitian pengetikan. Ini juga dimungkinkan - lihat misalnya abcmodul standar .

Eli Bendersky
sumber
35
+1 sebagai contoh. Ngomong-ngomong, dalam bahasa apa anjing mengatakan "hau"?
JeremyP
5
@JeremyP: hmm, poin yang bagus :-) Saya rasa dalam bahasa di mana "h" dipahami sebagai membuat suara seperti huruf pertama dari "hippy", atau "Javier" dalam bahasa Spanyol.
Eli Bendersky
5
@Eli: Maaf, tapi saya sangat tertarik dengan jawaban pertanyaan itu. Dalam bahasa Inggris mereka mengatakan "woof", well they don't but that is we use analog to "meong" untuk kucing dan "moo" untuk sapi. Apakah "hau" itu bahasa Spanyol?
JeremyP
9
@JeremyP Saya tahu pasti itu yang dikatakan anjing Polandia;)
j_kubik
2
@JeremyP ya saya mengonfirmasi anjing mengatakan "Jau" dalam bahasa Spanyol dan jika ditulis dalam bahasa Inggris akan menjadi "Hau" :) hth
SkyWalker
71

raise NotImplementedError()

Ini adalah pengecualian yang disarankan untuk dimunculkan pada "metode virtual murni" dari kelas dasar "abstrak" yang tidak mengimplementasikan metode.

https://docs.python.org/3.5/library/exceptions.html#NotImplementedError mengatakan:

Pengecualian ini berasal dari RuntimeError. Dalam kelas dasar yang ditentukan pengguna, metode abstrak harus memunculkan pengecualian ini ketika mereka membutuhkan kelas turunan untuk mengganti metode.

Seperti yang dikatakan orang lain, ini sebagian besar adalah konvensi dokumentasi dan tidak diperlukan, tetapi dengan cara ini Anda mendapatkan pengecualian yang lebih berarti daripada kesalahan atribut yang hilang.

Misalnya:

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

memberikan:

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

Terkait: Apakah mungkin membuat kelas abstrak dengan Python?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
55

Metode Python selalu virtual.

Ignacio Vazquez-Abrams
sumber
Kecuali metode dunder.
Konstantin
1
Jawaban ini tidak terlalu membantu dalam tujuan mengimplementasikan kelas antarmuka yang merupakan salah satu alasan utama penggunaan metode virtual.
Jean-Marc Volle
21

Sebenarnya, di versi 2.6 python menyediakan sesuatu yang disebut kelas dasar abstrak dan Anda dapat secara eksplisit mengatur metode virtual seperti ini:

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

Ia bekerja dengan sangat baik, asalkan kelas tersebut tidak mewarisi dari kelas yang sudah menggunakan metaclass.

sumber: http://docs.python.org/2/library/abc.html

pengguna2795020
sumber
Apakah ada python 3 yang setara untuk petunjuk ini?
locke14
9

Metode Python selalu virtual

seperti yang dikatakan Ignacio. Entah bagaimana, warisan kelas mungkin merupakan pendekatan yang lebih baik untuk menerapkan apa yang Anda inginkan.

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

Hasilnya harus:

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

Sesuatu seperti metode virtual di C ++ (implementasi metode panggilan dari kelas turunan melalui referensi atau penunjuk ke kelas dasar) tidak masuk akal di Python, karena Python tidak memiliki pengetikan. (Saya tidak tahu bagaimana metode virtual bekerja di Java dan PHP.)

Tetapi jika yang Anda maksud dengan "virtual" adalah memanggil implementasi paling bawah dalam hierarki pewarisan, maka itulah yang selalu Anda dapatkan dengan Python, seperti yang ditunjukkan beberapa jawaban.

Hampir selalu ...

Seperti yang ditunjukkan dplamp, tidak semua metode dalam Python berperilaku seperti itu. Metode Dunder tidak. Dan menurut saya itu adalah fitur yang tidak begitu terkenal.

Pertimbangkan contoh buatan ini

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

Sekarang

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

Namun, pertimbangkan yang satu ini

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

Sekarang

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

Satu-satunya hal yang kami prop_a()ubah adalah membuat metode yang konyol.

Masalah dengan perilaku pertama adalah Anda tidak dapat mengubah perilaku prop_a()di kelas turunan tanpa memengaruhi perilaku prop_b(). Ini sangat bagus bicara oleh Raymond Hettinger memberikan contoh untuk kasus penggunaan di mana ini tidak nyaman.

Konstantin
sumber