2017-01-23 16 views
8

Tôi tìm thấy điều này method chaining in python, nhưng ngay cả với nó tôi không thể hiểu được phương pháp chuỗi trong Python.Phương pháp cơ bản chaining

Ở đây, các mục tiêu là hai: giải quyết vấn đề mã hóa và hiểu chuỗi phương pháp (với điều kiện tôi vẫn không tự tin 100% với các cuộc gọi).

Xuống định nghĩa sự cố.

Tôi muốn một lớp học có hai phương pháp: một bộ tham số của đối tượng = 'dòng' và một ghi đè khác vào 'thanh'.

Đây là những gì tôi có cho đến nay:

class foo(): 
    def __init__(self, kind=None): 
     self.kind = kind 

    def __call__(self, kind=None): 
     return foo(kind=kind) 

    def my_print(self): 
     print (self.kind) 

    def line(self): 
     return self(kind='line') 
    def bar(self): 
     return self(kind='bar') 

Đáng buồn thay, với mã này tôi có thể đạt được mục tiêu của tôi làm điều này

a = foo() 
a.bar().line().bar().bar().line().my_print() 

Nhưng tôi muốn để có được những kết quả tương tự bằng cách viết này mã

a = foo() 
a.bar.line.bar.bar.line.my_print() 

Làm cách nào để đạt được điều này? Tôi đoán là một cái gì đó sai trong cách tôi xác định phương pháp __call__. Cảm ơn trước sự giúp đỡ của bạn.

Trả lời

18

Phương thức chuỗi chỉ đơn giản là có thể thêm .second_func() vào bất kỳ giá trị nào .first_func() trả lại. Nó được thực hiện khá dễ dàng bằng cách đảm bảo rằng tất cả các phương thức có thể thay đổi đều trả về self. (Lưu ý rằng việc này không liên quan gì đến __call()__).

class foo(): 
    def __init__(self, kind=None): 
     self.kind = kind 
    def my_print(self): 
     print (self.kind) 
     return self 
    def line(self): 
     self.kind = 'line' 
     return self 
    def bar(self): 
     self.kind='bar' 
     return self 

Bạn có thể sử dụng foo đối tượng trong một cách không bị xiềng xích bằng cách bỏ qua các giá trị trở lại của họ:

a = foo() 
a.line() 
a.my_print() 
a.bar() 
a.my_print() 

assert a.kind == 'bar' 

Hoặc, vì mỗi chức năng hiện nay trả về đối tượng riêng của mình, bạn có thể hoạt động trực tiếp trên trở giá trị. Bạn có thể sử dụng phương pháp chaining với mã tương đương này:

b = foo() 
b.line().my_print().bar().my_print() 
assert b.kind == 'bar' 

Hoặc thậm chí:

c = foo().line().my_print().bar().my_print() 
assert c.kind == 'bar' 

Câu hỏi về việc thoát khỏi cú pháp gọi () là một khái niệm hoàn toàn tách biệt từ phương pháp chaining. Nếu bạn muốn các thuộc tính chuỗi và có các thuộc tính đó làm thay đổi đối tượng của chúng, hãy sử dụng trình trang trí @property. (Tuy nhiên, đột biến đối tượng thông qua một tài sản dường như nguy hiểm tốt hơn để sử dụng một phương pháp và đặt tên cho nó với một động từ:.. .set_line() thay vì .line, ví dụ)

class foo(): 
    def __init__(self, kind=None): 
     self.kind = kind 
    def my_print(self): 
     print (self.kind) 
     return self 
    @property 
    def line(self): 
     self.kind = 'line' 
     return self 
    @property 
    def bar(self): 
     self.kind='bar' 
     return self 

a = foo() 
a.line 
a.my_print() 
a.bar 
a.my_print() 

assert a.kind == 'bar' 

b = foo() 
b.line.my_print().bar.my_print() 
assert b.kind == 'bar' 

c = foo().line.my_print().bar.my_print() 
assert c.kind == 'bar' 
+0

Cảm ơn câu trả lời rất đầy đủ !! – Pezze

3

Sử dụng các thuộc tính (bộ mô tả).

class foo: 
    def __init__(self, kind=None): 
     self.kind = kind 

    def __call__(self, kind=None): 
     return foo(kind=kind) 

    def my_print(self): 
     print (self.kind) 

    @property 
    def line(self): 
     return self(kind='line') 

    @property 
    def bar(self): 
     return self(kind='bar') 

Lưu ý, tuy nhiên, mà bạn ghi đè lên gì cả, việc sửa đổi không làm việc inplace (mà được cho là tốt, btw). Dù sao, điều này không giống như một sự lựa chọn thiết kế tốt cho hầu hết các trường hợp trong thế giới thực, bởi vì tại một số điểm phương pháp của bạn sẽ yêu cầu đối số.

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