2009-06-26 31 views
38

Có thể ghi đè + = bằng Python không?Ghi đè "+ =" bằng Python? (__iadd __() method)

+1

Bản sao chính xác của http://stackoverflow.com/questions/728361/is-there-a-way-to-overload-in-python –

+4

có, nhưng sử dụng tìm kiếm trang web cho: + = python không có kết quả. đặt dấu ngoặc kép xung quanh "+ =" cho kết quả nhưng phần lớn không liên quan đến đánh giá bởi các tiêu đề. tìm thấy điều này thông qua một tìm kiếm của Google mặc dù. –

Trả lời

76

Có, ghi đè phương thức __iadd__. Ví dụ:

def __iadd__(self, other): 
    self.number += other.number 
    return self  
+30

Bạn không nên triển khai '__iadd__' nếu lớp của bạn đại diện cho các đối tượng bất biến. Trong trường hợp đó chỉ cần thực hiện '__add__' sẽ được sử dụng để ghi đè' + = 'thay thế. Ví dụ, bạn có thể sử dụng '+ =' trên các kiểu bất biến như chuỗi và số nguyên, không thể thực hiện bằng '__iadd__'. –

+0

@ScottGriffiths, vậy bạn có nói rằng nếu bạn đã triển khai '__add__' thì bạn không nhất thiết phải triển khai' __iadd__'? Tôi đọc câu hỏi trùng lặp mà bạn đã liên kết nhưng tôi đã nhầm lẫn vì tôi không hiểu tại sao bạn sẽ thực hiện '__add__' để nó biến đổi đối tượng –

+0

@ScottGriffiths có nghĩa là hỏi" bạn không nhất thiết phải triển khai '__iadd__' __to sử dụng + = __? " –

12

Ngoài quá tải __iadd__ (nhớ tự trả lại!), Bạn cũng có thể dự phòng trên __add__, vì x + = y sẽ hoạt động như x = x + y. (Đây là một trong những cạm bẫy của + = toán tử.)

>>> class A(object): 
... def __init__(self, x): 
...  self.x = x 
... def __add__(self, other): 
...  return A(self.x + other.x) 
>>> a = A(42) 
>>> b = A(3) 
>>> print a.x, b.x 
42 3 
>>> old_id = id(a) 
>>> a += b 
>>> print a.x 
45 
>>> print old_id == id(a) 
False 

Nó thậm chí trips up experts:

class Resource(object): 
    class_counter = 0 
    def __init__(self): 
    self.id = self.class_counter 
    self.class_counter += 1 

x = Resource() 
y = Resource() 

gì giá trị gì bạn mong đợi x.id, y.id, và Resource.class_counter để có?

+7

Ví dụ thứ hai của bạn không liên quan gì đến iadd hoặc + =. Kết quả tương tự cũng xảy ra nếu bạn sử dụng self.class_counter = self.class_counter + 1 Nó chỉ là một vấn đề phạm vi, sử dụng tự khi Tài nguyên nên được sử dụng. – FogleBird

+0

Đây là ví dụ về cách sử dụng + = có thể dẫn đến sự cố. Nếu bạn đang quá tải __iadd__, thì bạn đang mở người dùng lớp học của bạn (bao gồm cả chính bạn) cho điều này, và, ít nhất, bạn nên biết vấn đề tồn tại trước đó. –

+2

@FogleBird: Nó là một bản ghi nhớ bởi vì 'foo + = bar' có thể có nghĩa là" thay đổi đối tượng hiện có 'foo' đề cập đến" hoặc "gán' foo' cho đối tượng xuất phát từ biểu thức 'foo + bar'". Và điều đó xảy ra phụ thuộc vào việc 'foo' có phương thức' __iadd__' hay không. – Claudiu

9

Ngoài những gì được đưa ra chính xác trong câu trả lời ở trên, cần làm rõ một cách rõ ràng rằng khi __iadd__ bị ghi đè, hoạt động x += y KHÔNG kết thúc với phương thức __iadd__.

Thay vào đó, kết thúc bằng x = x.__iadd__(y). Nói cách khác, Python gán giá trị trả về của việc triển khai __iadd__ cho đối tượng bạn đang "thêm vào", SAU KHI triển khai hoàn tất.

Điều này có nghĩa là có thể biến đổi bên trái của thao tác x += y để bước ẩn cuối cùng không thành công. Hãy xem xét những gì có thể xảy ra khi bạn đang làm tăng thêm cái gì đó là trong một danh sách:

>>> x[1] += y # x has two items

Bây giờ, nếu thực hiện __iadd__ của bạn (một phương pháp của một đối tượng ở x[1]) sai lầm hoặc về mục đích loại bỏ các mục đầu tiên (x[0]) từ đầu danh sách, Python sẽ chạy phương thức __iadd__ của bạn) & cố gắng gán giá trị trả về của nó cho x[1]. Mà sẽ không còn tồn tại (nó sẽ là x[0]), dẫn đến một ÌndexError.

Hoặc, nếu __iadd__ chèn của bạn một cái gì đó để đầu x của ví dụ trên, đối tượng của bạn sẽ có mặt tại x[2], không x[1], và bất cứ điều gì là trước đó tại x[0] bây giờ sẽ có mặt tại x[1] và được gán giá trị trả về của __iadd__ yêu cầu.

Trừ khi người ta hiểu điều gì đang xảy ra, kết quả là lỗi có thể là một cơn ác mộng cần khắc phục.