2013-01-31 26 views
34

Tôi thường thấy mình ghi đè phương thức của lớp cha và không bao giờ có thể quyết định rõ ràng có nên liệt kê các thông số đã cho hoặc chỉ sử dụng cấu trúc mền *args, **kwargs. Là một phiên bản tốt hơn so với khác? Có cách nào tốt nhất không? Tôi đang thiếu những lợi thế nào (dis-)?Thực hành tốt nhất thừa kế: * args, ** kwargs hoặc chỉ định rõ ràng các tham số

class Parent(object): 

    def save(self, commit=True): 
     # ... 

class Explicit(Parent): 

    def save(self, commit=True): 
     super(Explicit, self).save(commit=commit) 
     # more logic 

class Blanket(Parent): 

    def save(self, *args, **kwargs): 
     super(Blanket, self).save(*args, **kwargs) 
     # more logic 

lợi ích nhận thức của biến thể rõ ràng

  • rõ ràng hơn (Zen của Python)
  • dễ dàng hơn để nắm bắt
  • thông số chức năng dễ dàng truy cập

lợi ích cảm nhận của chăn biến nt

  • hơn DRY
  • tầng lớp phụ huynh có thể dễ dàng hoán đổi cho nhau
  • thay đổi các giá trị mặc định trong phương pháp cha mẹ là tuyên truyền mà không cần chạm mã khác
+2

Rất nhiều phụ thuộc vào chính xác những gì bạn đang phân lớp ...Nếu có một cơ hội cao mà bạn sẽ thêm (hoặc người khác sẽ) bổ sung kwargs vào các phương thức của lớp cơ sở, nó có nhiều ý nghĩa để gắn bó với '** kwargs'. Nếu điều này là không có khả năng, sau đó xác định rõ ràng kwargs chắc chắn là tốt hơn nhiều từ một quan điểm dễ đọc. –

+0

Vâng, bạn chắc chắn đang vứt bỏ thông tin. * Rõ ràng là tốt hơn so với ngầm * và tất cả những điều đó. – kojiro

+2

Bạn có hai lựa chọn: 1) đặt tên rõ ràng các tham số và * cấm * thay đổi chữ ký trong các lớp dẫn xuất (nếu không là goodluck với 'super' và multiple inheritance), hoặc 2) Sử dụng' ** kwargs' và cho phép thay đổi chữ ký của các phương thức . Mà một trong những nên được sử dụng có thể phụ thuộc vào tình hình. – Bakuriu

Trả lời

23

Liskov Substitution Nguyên tắc

Nói chung, bạn không muốn bạn phương pháp chữ ký khác nhau trong các loại có nguồn gốc. Điều này có thể gây ra vấn đề nếu bạn muốn trao đổi việc sử dụng các loại có nguồn gốc. Điều này thường được gọi là Liskov Substitution Principle.

Lợi ích của chữ ký Explicit

Cùng lúc đó tôi không nghĩ rằng đó là đúng cho tất cả các phương pháp của bạn để có một chữ ký của *args, **kwargs. chữ ký rõ ràng:

  • giúp đỡ để ghi lại phương pháp này thông qua tên lập luận tốt
  • giúp đỡ để ghi lại phương pháp này bằng cách xác định mà args được yêu cầu và có giá trị mặc định
  • cung cấp xác nhận ngầm (thiếu args cần ném ngoại lệ rõ ràng)

Arguments Variable Length và nối

Đừng nhầm lẫn các đối số độ dài thay đổi để thực hành khớp nối tốt. Nên có một số lượng nhất định của sự gắn kết giữa một lớp cha và các lớp dẫn xuất nếu không chúng sẽ không liên quan đến nhau. Nó là bình thường đối với mã liên quan đến kết quả trong khớp nối phản ánh mức độ gắn kết.

Địa điểm sử dụng đối số Chiều dài Variable

Sử dụng đối số chiều dài biến không nên lựa chọn đầu tiên của bạn. Nó nên được sử dụng khi bạn có lý do chính đáng như:

  • Xác định trình bao bọc chức năng (nghĩa là trang trí).
  • Xác định hàm đa hình tham số.
  • Khi các đối số bạn thực sự có thể biến hoàn toàn (ví dụ: chức năng kết nối DB tổng quát). Các hàm kết nối DB thường lấy một số connection string ở nhiều dạng khác nhau, cả ở dạng arg đơn và dưới dạng đa arg. Ngoài ra còn có các bộ tùy chọn khác nhau cho các cơ sở dữ liệu khác nhau.
  • ...

Bạn có làm sai điều gì?

Nếu bạn thấy bạn thường tạo ra các phương thức có nhiều đối số hoặc phương pháp dẫn xuất với chữ ký khác nhau, bạn có thể gặp vấn đề lớn hơn về cách bạn đang tổ chức mã của mình.

13

lựa chọn của tôi sẽ là:

class Child(Parent): 

    def save(self, commit=True, **kwargs): 
     super(Child, self).save(commit, **kwargs) 
     # more logic 

Nó tránh truy cập đối số cam kết từ *args**kwargs và giữ mọi thứ an toàn nếu chữ ký của Parent:save thay đổi (ví dụ: thêm đối số mặc định mới).

Cập nhật: Trong trường hợp này, có dấu * args có thể gây phiền hà nếu đối số vị trí mới được thêm vào phụ huynh. Tôi sẽ chỉ giữ lại **kwargs và chỉ quản lý các đối số mới với các giá trị mặc định. Nó sẽ tránh lỗi truyền bá.

+1

Tôi tin rằng đây là câu trả lời hay nhất, bởi vì nó giải quyết các nhu cầu được phơi bày bởi câu hỏi. Cụ thể: giữ đối số rõ ràng so với khả năng thêm đối số cho phụ huynh trong tương lai. Mặc dù câu trả lời được cung cấp bởi @dietbuddha là "học tập" chính xác, nó không cung cấp một giải pháp thay thế có thể được sử dụng trong thực tế, khi bạn không biết chắc chắn sẽ là số phận của mã của bạn trong tương lai. Câu trả lời này giải quyết vấn đề đó. –

+0

Câu trả lời được cung cấp bởi Alex Martelli [trong chủ đề SO này] (http://stackoverflow.com/questions/1098549/proper-way-to-use-kwargs-in-python) có thêm chi tiết về chủ đề này. –

+0

Cách bạn viết nó, bạn ngụ ý rằng chữ ký gốc cũng có '** kwargs', đó không phải là trường hợp trong ví dụ của câu hỏi. –

4

Nếu bạn chắc chắn rằng trẻ em sẽ giữ chữ ký, chắc chắn là cách tiếp cận rõ ràng là một lợi thế, nhưng khi trẻ em sẽ thay đổi chữ ký cá nhân tôi thích sử dụng cả hai phương pháp:

class Parent(object): 
    def do_stuff(self, a, b): 
     # some logic 

class Child(Parent): 
    def do_stuff(self, c, *args, **kwargs): 
     super(Child, self).do_stuff(*args, **kwargs) 
     # some logic with c 

Bằng cách này, những thay đổi trong chữ ký là khá dễ đọc trong Child, trong khi chữ ký gốc là khá dễ đọc trong Parent.

Theo tôi đây cũng là cách tốt hơn khi bạn có nhiều thừa kế, bởi vì gọi super một vài lần là khá ghê tởm khi bạn không có args và kwargs.

Đối với những gì nó có giá trị, đây cũng là cách ưa thích trong một vài libs và khung công tác Python (Django, Tornado, Yêu cầu, Markdown, để đặt tên một vài). Mặc dù người ta không nên căn cứ vào sự lựa chọn của mình trên những thứ như vậy, tôi chỉ đơn thuần ngụ ý rằng cách tiếp cận này khá phổ biến.

2

Tôi thích các đối số rõ ràng vì tự động hoàn thành cho phép bạn xem chữ ký phương thức của hàm trong khi thực hiện cuộc gọi hàm.

3

Không thực sự là một câu trả lời nhưng nhiều hơn một mặt lưu ý: Nếu bạn thực sự, thực sự muốn chắc chắn các giá trị mặc định cho các tầng lớp phụ huynh được tuyên truyền để các lớp con bạn có thể làm một cái gì đó như:

class Parent(object): 

    default_save_commit=True 
    def save(self, commit=default_save_commit): 
     # ... 

class Derived(Parent): 

    def save(self, commit=Parent.default_save_commit): 
     super(Derived, self).save(commit=commit) 

Tuy nhiên Tôi phải thừa nhận điều này trông khá xấu xí và tôi sẽ chỉ sử dụng nó nếu tôi cảm thấy tôi thực sự cần nó.

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