Python'da sanal yöntemler nasıl uygulanır?


88

PHP veya Java'dan sanal yöntemler biliyorum.

Python'da nasıl uygulanabilir?

Yoksa soyut bir sınıfta boş bir yöntem tanımlamam ve onu geçersiz kılmam mı gerekiyor?

Yanıtlar:


104

Elbette ve temel sınıfta bir yöntem tanımlamanıza bile gerek yok. Python'da yöntemler sanaldan daha iyidir - Python'da yazmak ördek yazmak olduğu için tamamen dinamiktir .

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

Catve DogPython'da bu davranışa izin vermek için ortak bir temel sınıftan türetmek zorunda bile değilsiniz - ücretsiz olarak elde edersiniz. Bununla birlikte, bazı programcılar sınıf hiyerarşilerini daha katı bir şekilde tanımlamayı tercih ederek daha iyi belgelemeyi ve yazımın bir miktar katılığını empoze etmeyi tercih ediyor . Bu da mümkündür - örneğin abcstandart modüle bakın .


35
Bir örnek için +1. Bu arada köpekler hangi dilde "hau" diyor?
JeremyP

5
@JeremyP: hmm, iyi nokta :-) Sanırım "h" nin İspanyolca "hippy" nin veya "Javier" in ilk harfi gibi ses çıkardığı anlaşılan dillerde.
Eli Bendersky

5
@Eli: Üzgünüm ama sorunun cevabıyla ciddi olarak ilgilendim. İngilizcede "woof" diyorlar, ama yapmıyorlar ama bizim kediler için "miyav" a ve inekler için "moo" ya benzeyen kelime budur. O halde "hau" İspanyolca mı?
JeremyP

9
@JeremyP Polonyalı köpeklerin söylediği şeyin bu olduğundan eminim;)
j_kubik

2
@JeremyP evet, köpeklerin İspanyolca "Jau" dediğini ve İngilizce yazıldığında "Hau" olacağını
onaylıyorum

71

raise NotImplementedError()

Bu, bir yöntemi uygulamayan "soyut" temel sınıfların "saf sanal yöntemlerini" yükseltmek için önerilen istisnadır.

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

Bu istisnanın kaynağı RuntimeError. Kullanıcı tanımlı temel sınıflarda, soyut yöntemler, yöntemi geçersiz kılmak için türetilmiş sınıflara ihtiyaç duyduklarında bu istisnayı oluşturmalıdır.

Diğerlerinin dediği gibi, bu çoğunlukla bir dokümantasyon kuralıdır ve gerekli değildir, ancak bu şekilde eksik bir öznitelik hatasından daha anlamlı bir istisna elde edersiniz.

Örneğin:

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

verir:

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

İlgili: Python'da soyut sınıflar yapmak mümkün mü?



21

Aslında, 2.6 sürümünde python, soyut temel sınıflar olarak adlandırılan bir şey sağlar ve bunun gibi sanal yöntemleri açıkça ayarlayabilirsiniz:

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

Sınıf zaten metasınıfları kullanan sınıflardan miras almadığı sürece çok iyi çalışır.

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


Bu yönerge için python 3 eşdeğeri var mı?
locke14

9

Python yöntemleri her zaman sanaldır

Ignacio'nun henüz dediği gibi Bir şekilde sınıf mirası, istediğinizi uygulamak için daha iyi bir yaklaşım olabilir.

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

Sonuçlar şöyle olmalıdır:

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

4

C ++ 'da sanal bir yöntem gibi bir şey (türetilmiş bir sınıfın temel sınıfa bir başvuru veya işaretçi aracılığıyla yöntem uygulamasını çağırmak) Python'da yazım yapmadığından Python'da bir anlam ifade etmez. (Yine de Java ve PHP'de sanal yöntemlerin nasıl çalıştığını bilmiyorum.)

Ancak "sanal" derken kalıtım hiyerarşisinde en alttaki uygulamayı çağırmayı kastediyorsanız, birkaç yanıtın da işaret ettiği gibi Python'da her zaman bunu elde edersiniz.

Neredeyse her zaman ...

Dplamp'ın işaret ettiği gibi, Python'daki tüm yöntemler böyle davranmaz. Dunder yöntemi yok. Ve bence bu çok iyi bilinen bir özellik değil.

Bu yapay örneği düşünün

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

Şimdi

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

Ancak, bunu düşünün

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

Şimdi

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

Değiştirdiğimiz tek şey prop_a()bir dunder yöntemi yapmaktı.

İlk davranışla ilgili bir sorun, davranışını prop_a()etkilemeden türetilmiş sınıftaki davranışını değiştirememeniz olabilir prop_b(). Raymond Hettinger'in bu çok güzel konuşması, bunun uygunsuz olduğu bir kullanım durumu için bir örnek veriyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.