Mục đích của 'return self` từ một phương thức class?


46

Tôi đã bắt gặp một cái gì đó như thế này trong một dự án nguồn mở. Các phương thức sửa đổi các thuộc tính thể hiện trả về một tham chiếu đến thể hiện. Mục đích của công trình này là gì?

class Foo(object):

  def __init__(self):
    self.myattr = 0

  def bar(self):
    self.myattr += 1
    return self

2
Và đây là cách mà tất cả các jQuery được viết. Gần như mọi hàm trả về một đối tượng jQuery
CaffGeek

11
Tại sao bạn không tin tưởng mã được viết như vậy?
sepp2k

Câu trả lời:


65

Đó là cho phép xích.

Ví dụ:

var Car = function () {
    return {
        gas : 0,
        miles : 0,
        drive : function (d) {
            this.miles += d;
            this.gas -= d;
            return this;
        },
        fill : function (g) {
            this.gas += g;
            return this;
        },
    };
}

Bây giờ bạn có thể nói:

var c = Car();
c.fill(100).drive(50);
c.miles => 50;
c.gas => 50;

20
đối với bản ghi, loại chuỗi này thường được nhìn thấy trên mã với giao diện trôi chảy .
Lie Ryan

10

Như @Lie Ryan và @Frank Shearar đề cập, đó được gọi là "giao diện lưu loát" nhưng mô hình đó đã xuất hiện từ rất lâu.

Phần gây tranh cãi của mẫu đó là trong OO, bạn có trạng thái có thể thay đổi, do đó, một phương thức void có một loại giá trị trả về ngụ ý this- nghĩa là, đối tượng có trạng thái cập nhật là loại giá trị trả về.

Vì vậy, trong một ngôn ngữ OO có trạng thái có thể thay đổi, hai ngôn ngữ này tương đương ít nhiều:

a.doA()
a.doB()
a.doC()

...như trái ngược với

a.doA().doB().doC()

Vì vậy, tôi đã nghe thấy mọi người trong quá khứ chống lại các giao diện trôi chảy vì họ thích hình thức đầu tiên. Một tên khác tôi đã nghe cho "giao diện trôi chảy" là "tàu đắm";)

Tuy nhiên, tôi nói "ít nhiều tương đương" bởi vì các giao diện trôi chảy làm tăng thêm nếp nhăn. Họ không phải "trả lại cái này". Họ có thể "trở lại mới". Đó là một cách để đạt được các đối tượng bất biến trong OO.

Vì vậy, bạn có thể có một lớp A mà (mã giả)

function doA():
    return new A(value + 1)

function doB():
    return new A(value * 2)

function doC():
    return new A(sqrt(value))

Bây giờ, mỗi phương thức trả về một đối tượng hoàn toàn mới, giữ nguyên đối tượng ban đầu. Và đó là một cách để đi vào các đối tượng bất biến mà không thực sự thay đổi nhiều trong mã của bạn.


Ok nhưng nếu phương thức của bạn trở lại new(...), điều đó sẽ rò rỉ bộ nhớ.
smci

@smci Điều đó phụ thuộc nhiều vào ngôn ngữ, cách thực hiện và cách sử dụng. Chắc chắn, nếu là C ++, rất có thể bạn sẽ bị rò rỉ bộ nhớ ở mọi nơi. Nhưng tất cả điều này sẽ được làm sạch một cách tầm thường bởi một ngôn ngữ với bộ thu gom rác. Một số triển khai (có hoặc không có GC) thậm chí có thể phát hiện khi một trong những đối tượng mới đó không được sử dụng và thực sự không phân bổ bất cứ thứ gì.
8bittree

@ 8bittree: chúng ta đang nói về Python, câu hỏi này đã được gắn thẻ Python 8 năm trước. Python GC không tuyệt vời vì vậy tốt nhất không nên rò rỉ bộ nhớ ở nơi đầu tiên. Gọi __init__liên tục giới thiệu quá đầu.
smci

8

Hầu hết các ngôn ngữ đều nhận thức được thành ngữ 'tự trả về' và bỏ qua nó nếu nó không được sử dụng trong một dòng. Tuy nhiên, đáng chú ý là trong Python, các hàm trả về Nonetheo mặc định.

Khi tôi ở trường CS, người hướng dẫn của tôi đã nói rất nhiều về sự khác biệt giữa các chức năng, thủ tục, thói quen và phương pháp; nhiều câu hỏi tiểu luận nhồi nhét bằng tay đã được đưa ra với bút chì cơ học đang nóng trong tay tôi về tất cả những điều đó.

Có thể nói, tự trả về là cách OO dứt khoát để tạo các phương thức lớp, nhưng Python cho phép nhiều giá trị trả về, bộ dữ liệu, danh sách, đối tượng, nguyên hàm hoặc Không có.

Chaining, như họ đã nói, chỉ đơn thuần là đưa câu trả lời cho thao tác cuối cùng vào hoạt động tiếp theo và thời gian chạy của Python có thể tối ưu hóa loại điều đó. Danh sách hiểu là một hình thức tích hợp của điều này. (Rất mạnh!)

Vì vậy, trong Python không quan trọng đến mức mọi phương thức hoặc hàm đều trả về mọi thứ, đó là lý do tại sao mặc định là Không có.

Có một trường phái cho rằng mọi hành động trong một chương trình nên báo cáo thành công, thất bại hoặc kết quả trở lại bối cảnh hoặc đối tượng của nó, nhưng sau đó không nói về Yêu cầu của DOD ADA ở đây. Nếu bạn cần nhận phản hồi từ một phương pháp, hãy tiếp tục hoặc không, nhưng cố gắng nhất quán về nó.

Nếu một phương thức có thể thất bại, nó sẽ trả về thành công hay thất bại hoặc đưa ra một ngoại lệ cần xử lý.

Một lưu ý là nếu bạn sử dụng thành ngữ tự trả về, Python sẽ cho phép bạn gán tất cả các phương thức của mình cho các biến và bạn có thể nghĩ rằng bạn đang nhận được kết quả dữ liệu hoặc danh sách khi bạn thực sự nhận được đối tượng.

Các ngôn ngữ hạn chế kiểu hét lên và la hét và phá vỡ khi bạn cố gắng làm điều này, nhưng những ngôn ngữ được giải thích (Python, Lua, Lisp) năng động hơn nhiều.


4

Trong Smalltalk, mọi phương thức không trả lại rõ ràng một cái gì đó, đều có một "tự trả về" ngầm.

Đó là bởi vì (a) có mọi phương thức trả về một cái gì đó làm cho mô hình điện toán trở nên đồng nhất hơn và (b) nó rất hữu ích khi các phương thức tự trả về. Josh K đưa ra một ví dụ tốt đẹp.


Thật thú vị ... trong Python, mọi phương thức không trả lại rõ ràng một cái gì đó, đều có một "trả lại Không" ngầm định.
Công việc

2

Ưu điểm của việc trả lại một đối tượng ( return self)

  • Thí dụ:

    print(foo.modify1().modify2())
    
    # instaed of
    foo.modify1()
    foo.modify2()
    print(foo)
    
  • Phong cách phổ biến hơn trong cộng đồng lập trình (tôi nghĩ).

Ưu điểm của việc biến đổi một đối tượng (không return self)

  • Khả năng sử dụng returncho các mục đích khác.
  • Guido van Rossum tán thành phong cách này (tôi không đồng ý với lập luận của ông).
  • Phong cách phổ biến hơn trong cộng đồng Python (tôi nghĩ).
  • Mặc định (mã nguồn ít hơn).

Cuộc tranh luận Guido đang hấp dẫn tôi. Đặc biệt là phần mà anh ấy nói về "người đọc phải thân thiết với từng phương thức" Tức là bạn buộc phải xác định xem đây có phải là chuỗi tự hoặc chuỗi đầu ra hoặc kết hợp trước khi bạn hiểu những gì bạn có ở cuối phần chuỗi
Nath

@Nat Các hoạt động xử lý chuỗi về cơ bản khác với các hoạt động còn lại như thế nào?
xged

Sự khác biệt là trường hợp chuỗi bạn đang tạo một đối tượng hoàn toàn khác nhau mỗi lần. Tức là không sửa đổi các mục tại chỗ. rõ ràng là đối tượng hiện tại không được sửa đổi. Trong một ngôn ngữ mà sự trở lại tự ẩn là điều ngược lại là đúng nhưng môi trường hỗn hợp có vẻ có vấn đề
Nath

@Matt Tôi chỉ nhớ rằng chuỗi Python là bất biến. Điều đó trả lời đầy đủ câu hỏi của tôi.
xged

1
@Matt "rõ ràng là đối tượng hiện tại không bị sửa đổi" - mặc dù nó không rõ ràng khi nhìn.
xged
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.