Tôi biết các phương pháp ảo từ PHP hoặc Java.
Làm thế nào chúng có thể được triển khai bằng Python?
Hay tôi phải xác định một phương thức trống trong một lớp trừu tượng và ghi đè nó?
Câu trả lời:
Chắc chắn, và bạn thậm chí không phải xác định một phương thức trong lớp cơ sở. Trong Python, các phương thức tốt hơn ảo - chúng hoàn toàn động, vì cách gõ trong Python là kiểu gõ vịt .
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
và Dog
trong Python thậm chí không phải bắt nguồn từ một lớp cơ sở chung để cho phép hành vi này - bạn có được nó miễn phí. Điều đó nói rằng, một số lập trình viên thích xác định cấu trúc phân cấp lớp của họ theo cách cứng nhắc hơn để ghi lại nó tốt hơn và áp đặt một số nghiêm ngặt khi nhập. Điều này cũng có thể - xem ví dụ như abc
mô-đun chuẩn .
raise NotImplementedError()
Đây là trường hợp ngoại lệ được khuyến nghị nêu ra trên "các phương thức ảo thuần túy" của các lớp cơ sở "trừu tượng" không triển khai một phương thức.
https://docs.python.org/3.5/library/exceptions.html#NotImplementedError nói:
Ngoại lệ này có nguồn gốc từ
RuntimeError
. Trong các lớp cơ sở do người dùng định nghĩa, các phương thức trừu tượng sẽ nêu ra ngoại lệ này khi chúng yêu cầu các lớp dẫn xuất ghi đè phương thức.
Như những người khác đã nói, đây chủ yếu là một quy ước tài liệu và không bắt buộc, nhưng bằng cách này, bạn sẽ nhận được một ngoại lệ có ý nghĩa hơn là một lỗi thuộc tính bị thiếu.
Ví dụ:
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()
cho:
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
Liên quan: Có thể tạo các lớp trừu tượng trong Python không?
Các phương thức Python luôn ảo.
Trên thực tế, trong phiên bản 2.6, python cung cấp một thứ gọi là lớp cơ sở trừu tượng và bạn có thể đặt các phương thức ảo một cách rõ ràng như sau:
from abc import ABCMeta
from abc import abstractmethod
...
class C:
__metaclass__ = ABCMeta
@abstractmethod
def my_abstract_method(self, ...):
Nó hoạt động rất tốt, miễn là lớp đó không kế thừa từ các lớp đã sử dụng kính đo.
Các phương thức Python luôn ảo
như Ignacio đã nói Bằng cách nào đó, kế thừa lớp có thể là một cách tiếp cận tốt hơn để thực hiện những gì bạn muốn.
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
Kết quả phải là:
I am an unknown animal
I am a dog named Rover
Rover has 4 legs
Một cái gì đó giống như một phương thức ảo trong C ++ (gọi thực thi phương thức của một lớp dẫn xuất thông qua một tham chiếu hoặc con trỏ đến lớp cơ sở) không có ý nghĩa trong Python, vì Python không có tính năng nhập. (Tôi không biết các phương thức ảo hoạt động như thế nào trong Java và PHP.)
Nhưng nếu bằng "ảo", bạn có nghĩa là gọi triển khai dưới cùng trong hệ thống phân cấp kế thừa, thì đó là những gì bạn luôn nhận được trong Python, như một số câu trả lời đã chỉ ra.
Chà, hầu như luôn luôn ...
Như dplamp đã chỉ ra, không phải tất cả các phương thức trong Python đều hoạt động như vậy. Phương pháp Dunder không. Và tôi nghĩ đó là một tính năng không quá nổi tiếng.
Hãy xem xét ví dụ nhân tạo này
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
Hiện nay
>>> B().prop_b()
20
>>> A().prob_b()
10
Tuy nhiên, hãy xem xét điều này
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
Hiện nay
>>> B().prop_b()
10
>>> A().prob_b()
10
Điều duy nhất chúng tôi đã thay đổi là tạo ra prop_a()
một phương pháp tốt hơn.
Một vấn đề với hành vi đầu tiên có thể là bạn không thể thay đổi hành vi của prop_a()
trong lớp dẫn xuất mà không ảnh hưởng đến hành vi của prop_b()
. Bài nói rất hay này của Raymond Hettinger đưa ra một ví dụ cho một trường hợp sử dụng mà điều này là bất tiện.