Có cách nào trong Python để ghi đè một phương thức lớp ở cấp cá thể không? Ví dụ:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Có cách nào trong Python để ghi đè một phương thức lớp ở cấp cá thể không? Ví dụ:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Câu trả lời:
Vui lòng không làm điều này như được hiển thị. Mã của bạn trở nên không thể đọc được khi bạn bắt cặp một thể hiện khác với lớp.
Bạn không thể gỡ lỗi mã được khớp với khỉ.
Khi bạn tìm thấy một lỗi trong bobyvà print type(boby), bạn sẽ thấy rằng (a) đó là một con chó, nhưng (b) đối với một số lý do che khuất nó không sủa một cách chính xác. Đây là một cơn ác mộng. Đừng làm việc đó.
Hãy làm điều này thay thế.
class Dog:
def bark(self):
print "WOOF"
class BobyDog( Dog ):
def bark( self ):
print "WoOoOoF!!"
otherDog= Dog()
otherDog.bark() # WOOF
boby = BobyDog()
boby.bark() # WoOoOoF!!
Vâng nó có thể:
class Dog:
def bark(self):
print "Woof"
def new_bark(self):
print "Woof Woof"
foo = Dog()
funcType = type(Dog.bark)
# "Woof"
foo.bark()
# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)
foo.bark()
# "Woof Woof"
funcTypevà tại sao nó lại cần thiết.
funcType = types.MethodType(sau khi nhập types) thay vì funcType = type(Dog.bark).
__get__hàm để liên kết nó với phiên bản.
Bạn cần sử dụng MethodType từ typesmô-đun. Mục đích của MethodTypelà ghi đè các phương thức mức cá thể (để selfcó thể có sẵn trong các phương thức bị ghi đè).
Xem ví dụ dưới đây.
import types
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print "WoOoOoF!!"
boby.bark = types.MethodType(_bark, boby)
boby.bark() # WoOoOoF!!
Để giải thích câu trả lời tuyệt vời của @ codelogic, tôi đề xuất một cách tiếp cận rõ ràng hơn. Đây là kỹ thuật tương tự mà .toán tử dùng để ràng buộc một phương thức lớp khi bạn truy cập nó như một thuộc tính cá thể, ngoại trừ phương thức của bạn thực sự sẽ là một hàm được định nghĩa bên ngoài một lớp.
Làm việc với mã của @ codelogic, sự khác biệt duy nhất là cách phương thức được liên kết. Tôi đang sử dụng thực tế là các hàm và phương thức không phải là bộ mô tả dữ liệu trong Python và gọi __get__phương thức. Đặc biệt lưu ý rằng cả bản gốc và bản thay thế đều có chữ ký giống hệt nhau, có nghĩa là bạn có thể viết phương thức thay thế dưới dạng một phương thức lớp đầy đủ, truy cập tất cả các thuộc tính cá thể thông qua self.
lớp chó:
def vỏ (tự):
in "Gâu"
def new_bark (self):
in "Woof Woof"
foo = Con chó ()
# "Gâu"
foo.bark ()
# thay thế vỏ cây bằng vỏ cây mới chỉ cho đối tượng này
foo.bark = new_bark .__ get __ (foo, Dog)
foo.bark ()
# "Gâu gâu"
Bằng cách gán phương thức liên kết cho một thuộc tính cá thể, bạn đã tạo ra một mô phỏng gần như hoàn chỉnh về việc ghi đè một phương thức. Một tính năng tiện dụng bị thiếu là quyền truy cập vào phiên bản no-arg của super, vì bạn không có trong định nghĩa lớp. Một điều nữa là __name__thuộc tính của phương thức ràng buộc của bạn sẽ không lấy tên của hàm mà nó đang ghi đè, như trong định nghĩa lớp, nhưng bạn vẫn có thể đặt nó theo cách thủ công. Sự khác biệt thứ ba là phương thức được ràng buộc thủ công của bạn là một tham chiếu thuộc tính thuần túy chỉ là một hàm. Các .nhà điều hành không làm gì nhưng lấy tài liệu tham khảo đó. Mặt khác, khi gọi một phương thức thông thường từ một thể hiện, quá trình ràng buộc sẽ tạo ra một phương thức liên kết mới mỗi lần.
Lý do duy nhất mà điều này hoạt động, nhân tiện, là các thuộc tính cá thể ghi đè các bộ mô tả không phải dữ liệu . Bộ mô tả dữ liệu có __set__các phương thức, phương thức nào (may mắn cho bạn) thì không. Các bộ mô tả dữ liệu trong lớp thực sự được ưu tiên hơn bất kỳ thuộc tính cá thể nào. Đó là lý do tại sao bạn có thể gán cho một thuộc tính: __set__phương thức của chúng được gọi khi bạn cố gắng thực hiện một phép gán. Cá nhân tôi muốn thực hiện điều này một bước xa hơn và ẩn giá trị thực tế của thuộc tính cơ bản trong cá thể __dict__, nơi mà nó không thể truy cập được bằng cách thông thường chính xác vì thuộc tính che khuất nó.
Bạn cũng nên nhớ rằng điều này là vô nghĩa đối với các phương thức magic (dấu gạch dưới kép) . Tất nhiên, các phương pháp ma thuật có thể được ghi đè theo cách này, nhưng các thao tác sử dụng chúng chỉ nhìn vào kiểu. Ví dụ: bạn có thể đặt __contains__thành một cái gì đó đặc biệt trong trường hợp của mình, nhưng việc gọi x in instancesẽ bỏ qua điều đó và sử dụng type(instance).__contains__(instance, x)thay thế. Điều này áp dụng cho tất cả các phương thức ma thuật được chỉ định trong mô hình dữ liệu Python .
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
def new_bark():
print "WoOoOoF!!"
boby.bark = new_bark
boby.bark() # WoOoOoF!!
Bạn có thể sử dụng bobybiến bên trong hàm nếu cần. Vì bạn chỉ ghi đè phương thức cho một đối tượng cá thể này, nên cách này đơn giản hơn và có tác dụng giống hệt như cách sử dụng self.
patchingvà đây là cách chính xác để làm điều đó (ví dụ boby = Dog()và boby.bark = new_bark). Nó cực kỳ hữu ích trong việc kiểm tra đơn vị để kiểm soát. Để biết thêm giải thích, hãy xem tryolabs.com/blog/2013/07/05/run-time-method-patching-python (ví dụ) - không, tôi không liên kết với trang web hoặc tác giả được liên kết.
Vì không có ai đang đề cập functools.partialở đây:
from functools import partial
class Dog:
name = "aaa"
def bark(self):
print("WOOF")
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print("WoOoOoF!!")
boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!
Vì các hàm là các đối tượng lớp đầu tiên trong Python nên bạn có thể chuyển chúng trong khi khởi tạo đối tượng lớp của mình hoặc ghi đè nó bất cứ lúc nào cho một cá thể lớp nhất định:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
print "woof"
d=Dog()
print "calling original bark"
d.bark()
def barknew():
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
def barknew1():
print "nowoof"
d1.bark=barknew1
print "calling another new"
d1.bark()
và kết quả là
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
Mặc dù tôi thích ý tưởng kế thừa từ S. Lott và đồng ý với điều 'type (a)', nhưng vì các hàm cũng có các thuộc tính có thể truy cập được, tôi nghĩ nó có thể được quản lý theo cách này:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
"""original bark"""
print "woof"
d=Dog()
print "calling original bark"
d.bark()
print "that was %s\n" % d.bark.__doc__
def barknew():
"""a new type of bark"""
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
def barknew1():
"""another type of new bark"""
print "nowoof"
d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
và đầu ra là:
calling original bark
woof
that was original bark
calling the new bark
wooOOOoof
that was a new type of bark
another new
nowoof
that was another type of new bark
Thưa, đây không phải là ghi đè bạn chỉ đang gọi cùng một hàm hai lần với đối tượng. Về cơ bản ghi đè có liên quan đến nhiều hơn một lớp. khi cùng một phương thức chữ ký tồn tại trong các lớp khác nhau thì hàm nào bạn đang gọi nó sẽ quyết định đối tượng gọi hàm này. Có thể ghi đè trong python khi bạn tạo nhiều hơn một lớp đang viết các hàm giống nhau và một điều nữa cần chia sẻ là không được phép ghi đè trong python
Tôi thấy đây là câu trả lời chính xác nhất cho câu hỏi ban đầu
https://stackoverflow.com/a/10829381/7640677
import a
def _new_print_message(message):
print "NEW:", message
a.print_message = _new_print_message
import b
b.execute()