2008-12-27 58 views
55

Có cách nào trong Python ghi đè lên một phương thức lớp ở cấp độ cá thể không? Ví dụ:Ghi đè phương thức ở cấp độ

class Dog: 
    def bark(self): 
     print "WOOF" 

boby = Dog() 
boby.bark() # WOOF 
# METHOD OVERRIDE 
boby.bark() # WoOoOoF!! 

Trả lời

22

Hãy không làm điều này như được hiển thị. Bạn không thể đọc được mã khi bạn bắt chước một cá thể khác với lớp.

Bạn không thể gỡ lỗi mã khỉ đã được mã hóa.

Khi bạn tìm thấy lỗi trong bobyprint type(boby), bạn sẽ thấy rằng (a) đó là Chó, nhưng (b) vì một số lý do mơ hồ không sủa 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!! 
+6

@arivero: Tôi nghĩ rằng "Xin đừng làm điều này như được hiển thị" được thực hiện hoàn toàn rõ ràng. Bạn muốn thấy những từ nào khác hoặc khác biệt để làm rõ hơn rằng điều này không trả lời câu hỏi đã được hỏi, nhưng đang cung cấp lời khuyên về lý do tại sao đó là một ý tưởng tồi? –

+17

Tôi không đồng ý với lời khuyên, cũng như OP. Nhưng tôi cho rằng mọi người có lý do để hỏi. Hoặc ngay cả khi OP không có, các khách truy cập khác trong tương lai có thể. Vì vậy, IMHO, một câu trả lời cộng với một khiển trách là tốt hơn mà chỉ là một khiển trách. – arivero

+7

@arivero: Điều đó không trả lời được câu hỏi của tôi. –

120

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" 
+1

Một nhận xét nhỏ, khi bạn thực hiện funcType (new_bark, foo, Dog), hãy thêm tên == bark và phương thức dụ Dog.new_bark vào foo .__ dict__ phải không? Vì vậy, khi bạn gọi nó lần đầu tiên tra cứu vào từ điển thể hiện và gọi cho điều đó, –

+5

Hãy giải thích điều này, đặc biệt là những gì 'funcType' làm và tại sao nó là cần thiết. –

+1

Tôi nghĩ rằng điều này có thể được thực hiện một chút đơn giản và rõ ràng hơn bằng cách sử dụng 'funcType = types.MethodType' (sau khi nhập' loại') thay vì 'funcType = type (Dog.bark)'. –

22
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 biến boby bên trong hàm nếu bạn cần. Vì bạn đang ghi đè phương thức chỉ cho đối tượng một đối tượng này, cách này đơn giản hơn và có tác dụng giống hệt như sử dụng self.

+1

IMHO sử dụng chữ ký gốc thêm vào khả năng đọc, đặc biệt nếu hàm được định nghĩa ở nơi khác trong mã, không phải gần trường hợp. Trường hợp ngoại lệ là trường hợp phương pháp ghi đè cũng được sử dụng độc lập như một hàm. Tất nhiên, trong ví dụ đơn giản này, nó không quan trọng. – codelogic

+1

Tôi không hiểu tại sao đây không phải là câu trả lời được chấp nhận. Nó được gọi là "vá" và đây là cách chính xác để thực hiện nó (ví dụ: 'boby = Dog()' và 'boby.bark = new_bark'). Nó là vô cùng hữu ích trong đơn vị * thử nghiệm * cho điều khiển. Để được giải thích thêm, hãy xem https://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. – Geoff

+2

phương thức new_bark không có quyền truy cập vào bản thân (ví dụ) để không có cách nào người dùng có thể truy cập các thuộc tính cá thể trong new_bark. Thay vào đó, bạn cần sử dụng MethodType từ mô đun các loại (xem câu trả lời của tôi bên dưới). –

0

Kể từ khi chức năng là đối tượng hạng nhất trong Python bạn có thể vượt qua chúng khi khởi tạo đối tượng lớp học của bạn hoặc ghi đè lên nó bất cứ lúc nào cho một cá thể của lớp đưa ra:

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 
-4

Mặc dù tôi thích ý tưởng thừa kế từ S. Lott và đồng ý với các 'loại (a)' chuyện, nhưng kể từ khi chức năng cũng có thuộc tính truy cập, tôi nghĩ rằng 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 
+2

Nếu nó cần phải được "quản lý", sau đó - với tôi - có gì đó sai. Đặc biệt là khi có một tính năng ngôn ngữ hạng nhất đã thực hiện công việc. –

-4

Kính thưa đây không phải là trọng bạn chỉ là cách gọi chức năng tương tự hai lần với đối tượng. Về cơ bản, việc 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ì chức năng của bạn đang gọi điều này quyết định đối tượng gọi nó là gì. Ghi đè có thể trong python khi bạn tạo nhiều hơn một lớp được viết cùng chức năng và một điều nữa để chia sẻ quá tải đó không được phép trong python

12

Bạn cần sử dụng MethodType từ mô-đun loại. Mục đích của MethodType là ghi đè lên các phương thức mức cá thể (để tự có thể có sẵn trong phương thức ghi đè).

xem ví dụ bên dưới.

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!! 
-3

Tôi thấy điều nà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() 
1

Để giải thích câu trả lời tuyệt vời @ 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ử . sử dụng triệt để để ràng buộc một phương thức lớp khi bạn truy cập nó như là một thuộc tính instance, ngoại trừ phương thức của bạn sẽ thực 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, điểm khác biệt duy nhất là cách phương thức bị ràng buộc. Tôi đang sử dụng thực tế rằng các hàm và phương thức không phải là dữ liệu descriptors bằng Python và gọi phương thức __get__. Lưu ý đặc biệt là cả bản gốc và bản thay thế có chữ ký giống hệt nhau, nghĩa là bạn có thể viết thay thế làm phương thức toàn bộ lớp, truy cập tất cả các thuộc tính cá thể qua self.

 
class Dog: 
    def bark(self): 
     print "Woof" 

def new_bark(self): 
    print "Woof Woof" 

foo = Dog() 

# "Woof" 
foo.bark() 

# replace bark with new_bark for this object only 
foo.bark = new_bark.__get__(foo, Dog) 

foo.bark() 
# "Woof Woof" 

Bằng cách gán phương thức ràng buộc cho thuộc tính thể hiện, bạn đã tạo mô phỏng gần như hoàn thành ghi đè phương pháp. Một tính năng tiện dụng bị thiếu là truy cập vào phiên bản no-arg của super, vì bạn không có định nghĩa lớp. Một điều nữa là thuộc tính __name__ 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ó ghi đè, vì nó sẽ ở đị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 gắn bó thủ công của bạn là một tham chiếu thuộc tính đơn giản chỉ là một hàm. Toán tử . không làm gì ngoài việc tìm nạp tham chiếu đó. Khi gọi một phương thức thông thường từ một cá thể, mặt khác, quá trình liên kết tạo ra một phương thức ràng buộc mới mỗi lần.

Lý do duy nhất mà tính năng này hoạt động, bằng cách này, là các thuộc tính thể hiện ghi đè không phải là dữ liệu mô tả. Bộ mô tả dữ liệu có các phương thức __set__, phương pháp nào (may mắn cho bạn) thì không. 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: phương thức __set__ của chúng được gọi khi bạn cố gắng thực hiện một nhiệm vụ. Cá nhân tôi muốn tiến thêm một bước nữa và ẩn giá trị thực của thuộc tính cơ bản trong ví dụ __dict__, nơi nó không thể truy cập được bằng các phương tiện bình thường chính xác vì thuộc tính đổ bóng nó.

Bạn cũng nên nhớ rằng đây là vô nghĩa đối với magic (double underscore) methods. Tất nhiên các phương pháp ma thuật có thể bị ghi đè theo cách này, nhưng các hoạt động sử dụng chúng chỉ nhìn vào loại. Ví dụ: bạn có thể đặt __contains__ thành thứ gì đó đặc biệt trong trường hợp của mình, nhưng gọi số x in instance sẽ 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 Python data model.

Các vấn đề liên quan