2016-03-27 17 views
22

Tôi có một lớp đại diện cho đối tượng. Và tôi có một loạt các phương pháp sửa đổi trạng thái đối tượng này mà không có sự trở lại rõ ràng hoặc rõ ràng mà không có bất kỳ trở lại nào. Trong C# tôi sẽ khai báo tất cả các phương thức này là void và không thấy lựa chọn nào khác. Nhưng trong Python tôi về để làm cho tất cả các phương pháp return self để cung cấp cho bản thân mình khả năng để viết tuyệt vời một lớp lót như thế này:Trả lại tự trong python

classname().method1().method2().method3() 

là Pythonic này, hoặc nếu không thể chấp nhận được trong Python?

+1

Xem http://programmers.stackexchange.com/questions/29657/purpose-of-return-self-from-a-class-method – ilim

+7

kỹ thuật này được gọi là giao diện thông thạo. xem: https://en.wikipedia.org/wiki/Fluent_interface – Alex

+3

Câu hỏi về các phương pháp hay nhất phù hợp với Stack Overflow? Nó có vẻ phù hợp hơn với các lập trình viên hoặc Code Review. Tôi có khuynh hướng nói "đây là một kỹ thuật tốt hay xấu" quá rộng ở đây. –

Trả lời

20

Đây là một mail từ Guido van Rossum (tác giả của ngôn ngữ lập trình Python) về chủ đề này: https://mail.python.org/pipermail/python-dev/2003-October/038855.html

tôi muốn giải thích một lần nữa lý do tại sao tôi rất kiên quyết loại đó() không nên trả lại 'tự'.

Đây đến từ một phong cách mã hóa (phổ biến trong các ngôn ngữ khác nhau, tôi tin revels đặc biệt là Lisp trong đó), nơi một loạt các tác dụng phụ trên đối tượng đơn có thể được xích như thế này:

x.compress() .chop (y) .sort (z)

đó sẽ là tương tự như

x.compress() x.chop (y) x.sắp xếp (z)

Tôi thấy dạng chuỗi là mối đe dọa dễ đọc; nó đòi hỏi người đọc phải quen thuộc với từng phương pháp. Biểu mẫu thứ hai làm rõ rằng mỗi cuộc gọi này hoạt động trên cùng một đối tượng và vì vậy ngay cả khi bạn không biết lớp học và phương pháp của nó rất tốt, bạn có thể hiểu rằng cuộc gọi thứ hai và thứ ba được áp dụng cho x (và tất cả các cuộc gọi được thực hiện cho các tác dụng phụ của chúng), và không được để điều gì đó khác.

Tôi muốn đặt chaining cho các hoạt động trở lại những giá trị mới, như hoạt động chế biến chuỗi:

y = x.rstrip ("\ n") chia (":").. Thấp hơn()

Có một vài mô-đun thư viện tiêu chuẩn khuyến khích chuỗi các cuộc gọi có hiệu lực từ (pstat đến đầu óc). Không nên có bất kỳ số mới nào; pstat trượt qua bộ lọc của tôi khi nó yếu.

+0

Tôi có thể ở đâu đọc về quy ước đó –

+1

Tôi tìm thấy một thư từ Guido van Rossum (tác giả của ngôn ngữ lập trình Python) về chủ đề này – Querenker

+0

Một ví dụ chính khác là Django - nơi hướng dẫn tích cực khuyến khích chuỗi. với BDFL - nhưng tôi không thấy nó là mối đe dọa đối với khả năng đọc ở tất cả - nếu API được thực hiện tốt (tôi nghĩ DJango là hầu hết). –

10

Dưới đây là một (ngớ ngẩn) ví dụ đó cho thấy một kịch bản khi nó là một kỹ thuật tốt

class A: 
    def __init__(self, x): 
     self.x = x 
    def add(self, y): 
     self.x += y 
     return self 
    def multiply(self, y) 
     self.x *= y 
     return self 
    def get(self): 
     return self.x 
a = A(0) 
print a.add(5).mulitply(2).get() 

Trong trường hợp này bạn có thể tạo ra một đối tượng, trong đó thứ tự mà các hoạt động được thực hiện được xác định chặt chẽ theo thứ tự của cuộc gọi hàm có thể làm cho mã dễ đọc hơn (nhưng cũng dài hơn)

+1

Điều này không trả lời câu hỏi của anh ấy. Anh ấy muốn biết đó có phải là kỹ thuật tốt hay không! – Floam

+5

@Floam Câu trả lời rõ ràng cho biết anh ấy đưa ra một ví dụ về _when_ nó có thể là một kỹ thuật tốt –

+0

@Liongold Tôi đã bỏ lỡ điểm trước đó. Tôi đã chỉnh sửa câu trả lời của mình để kết hợp thay đổi này :) –

13

Đây là một ý tưởng tuyệt vời cho các API nơi bạn đang xây dựng trạng thái thông qua các phương pháp. SQLAlchemy sử dụng này để hiệu ứng tuyệt vời cho ví dụ:

>>> from sqlalchemy.orm import aliased 
>>> adalias1 = aliased(Address) 
>>> adalias2 = aliased(Address) 
>>> for username, email1, email2 in \ 
...  session.query(User.name, adalias1.email_address, adalias2.email_address).\ 
...  join(adalias1, User.addresses).\ 
...  join(adalias2, User.addresses).\ 
...  filter(adalias1.email_address=='[email protected]').\ 
...  filter(adalias2.email_address=='[email protected]'): 
...  print(username, email1, email2) 

Lưu ý rằng nó không trả lại self trong nhiều trường hợp; nó sẽ trả về một bản sao của đối tượng hiện tại với một khía cạnh nhất định bị thay đổi. Bằng cách này, bạn có thể tạo các chuỗi phân kỳ dựa trên cơ sở dùng chung; base = instance.method1().method2(), sau đó foo = base.method3()bar = base.method4().

Trong ví dụ trên, đối tượng Query được trả về bởi cuộc gọi Query.join() hoặc Query.filter() không phải là cùng một ví dụ, nhưng một phiên bản mới có bộ lọc hoặc tham gia được áp dụng cho nó.

Sử dụng Generative base class để xây dựng; nên thay vì return self, mô hình sử dụng là:

def method(self): 
    clone = self._generate() 
    clone.foo = 'bar' 
    return clone 

mà SQLAlchemy tiếp tục đơn giản hóa bằng cách sử dụng a decorator:

def _generative(func): 
    @wraps(func) 
    def decorator(self, *args, **kw): 
     new_self = self._generate() 
     func(new_self, *args, **kw) 
     return new_self 
    return decorator 

class FooBar(GenerativeBase): 
    @_generative 
    def method(self): 
     self.foo = 'bar' 

Tất cả các phương pháp @_generative -decorated đã làm là làm cho những thay đổi trên bản sao, các trang trí sẽ chăm sóc sản xuất bản sao, ràng buộc phương pháp vào bản sao thay vì bản gốc và trả lại cho người gọi cho bạn.

4

Nếu bạn muốn, bạn có thể sử dụng trang trí ở đây. Nó sẽ nổi bật với ai đó xem xét mã của bạn để xem giao diện và bạn không phải rõ ràng return self từ mọi chức năng (có thể gây phiền nhiễu nếu bạn có nhiều điểm thoát).

import functools 


def fluent(func): 
    @functools.wraps(func) 
    def wrapped(*args, **kwargs): 
     # Assume it's a method. 
     self = args[0] 
     func(*args, **kwargs) 
     return self 
    return wrapped 


class Foo(object): 
    @fluent 
    def bar(self): 
     print("bar") 

    @fluent 
    def baz(self, value): 
     print("baz: {}".format(value)) 

foo = Foo() 
foo.bar().baz(10) 

Prints:

bar 
baz: 10 
+0

Oooh, sáng bóng. : D – cat

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