2011-08-18 23 views
5

Tôi có một "giao diện" sẽ được thực hiện bởi mã khách hàng:Làm thế nào tôi có thể cân bằng "Pythonic" và "thuận tiện" trong trường hợp này?

class Runner: 
    def run(self): 
     pass 

run nên nói chung trả về một docutilsnode nhưng vì xa xa nhất trường hợp phổ biến là văn bản đơn giản, người gọi cho phép run trở lại một chuỗi, sẽ được kiểm tra bằng cách sử dụng type() và được chuyển thành một node.

Tuy nhiên, cách tôi hiểu "Pythonic", đây không phải là "Pythonic" vì kiểm tra type() của một cái gì đó không cho phép nó "là" một loại bằng cách "hành động" như một - tức là "Pythonic" mã nên sử dụng gõ vịt.

tôi coi

def run_str(self): 
    pass 

def run_node(self): 
    return make_node(self.run_str()) 

nhưng tôi không quan tâm cho điều này bởi vì nó đặt sự trở lại loại không quá thú vị ngay trong tên; nó mất tập trung.

Có bất kỳ ý tưởng nào tôi đã bỏ lỡ không? Ngoài ra, có vấn đề tôi có thể gặp phải xuống đường với hệ thống "xấu" của tôi (có vẻ như ít hoặc an toàn hơn đối với tôi)?

+0

Tôi hơi bối rối. Bạn đang nói về giá trị được truyền cho 'run' (thông qua' arg')? Hay bạn đang nói về cách xử lý giá trị giả định được trả về bởi phương thức 'run' của một đối tượng thực hiện giao diện' Runner'? – senderle

+0

Tôi có nghĩa là sự trở lại. Tôi đã chỉnh sửa 'arg' để nó không bị phân tâm (cảm ơn vì đã chỉ ra điều này). – Owen

Trả lời

5

Tôi nghĩ đây là một ví dụ hơi lừa đảo; có điều gì đó bạn chưa nói. Tôi đoán rằng khi bạn nói rằng bạn "có một giao diện", ý của bạn là bạn có một số mã chấp nhận một đối tượng và gọi phương thức run của nó.

Nếu bạn không thử nghiệm loại đối tượng đó trước khi gọi phương thức run, bạn đang sử dụng tính năng nhập vịt, đồng bằng và đơn giản! (Trong trường hợp này, nếu nó có phương thức run thì đó là phương thức Runner.) Miễn là bạn không sử dụng type hoặc isinstance trên đối tượng với phương thức run thì bạn là Pythonic.

Câu hỏi về việc bạn có nên chấp nhận các chuỗi đơn giản hay chỉ các đối tượng nút là một câu hỏi khác biệt phụ. Các chuỗi và các đối tượng node có thể không thực hiện cùng một giao diện! Chuỗi về cơ bản là không quack như một node, vì vậy bạn không phải đối xử với chúng như một. Điều này giống như một con voi đi cùng, và nếu bạn muốn nó quack như một con vịt, bạn phải cung cấp cho voi một máy nghe nhạc băng và đào tạo con voi để sử dụng nó đầu tiên.

Vì vậy, đây không phải là vấn đề "nhập vịt" nữa mà là thiết kế giao diện. Bạn đang cố gắng quyết định mức độ nghiêm ngặt của giao diện của mình.

Để cung cấp cho bạn câu trả lời, sau đó, ở cấp độ này, tôi nghĩ rằng đó là phần lớn Pythonic giả định rằng run trả về đối tượng node. Không cần sử dụng isinstance hoặc type để kiểm tra điều đó. Chỉ cần giả vờ là đối tượng node và nếu lập trình viên sử dụng giao diện của bạn bị sai và thấy ngoại lệ, thì họ sẽ phải đọc chuỗi tài liệu của bạn, điều này sẽ cho họ biết rằng run phải vượt qua đối tượng node.

Sau đó, nếu bạn muốn cũng chấp nhận chuỗi hoặc những thứ hoạt động như chuỗi, bạn có thể làm như vậy. Và vì chuỗi là loại nguyên thủy, tôi có thể nói không thích hợp khi sử dụng isinstance(obj, basestring) (nhưng không phảitype(obj) == str vì chuỗi đó loại bỏ chuỗi unicode, v.v.). Về cơ bản, đây là bạn đang rất tự do và tốt bụng với người dùng lười biếng của chương trình của bạn; bạn đã đi xa hơn và vượt ra ngoài bằng cách chấp nhận voi cũng như những thứ quack như vịt.

(Cụ thể hơn, tôi muốn nói đây là một chút như gọi iter vào một cuộc tranh cãi vào lúc bắt đầu của một hàm mà bạn muốn chấp nhận cả hai máy phát điện và trình tự.)

+0

Đây là một sự cân bằng tốt về "độ tinh khiết Pythonic" so với thiết kế API thực dụng. Tôi cũng sẽ dự đoán rằng người dùng của bạn sẽ "trở lại" Không có gì bằng cách không trả lại bất kỳ điều gì, vì vậy bạn cũng nên viết mã cho điều đó. – PaulMcG

1

Check-out Errors and Exceptions. Bạn có thể làm một cái gì đó như thế này:

def run(self,arg): 
    try: 
     return make_node(arg) 
    except AlreadyNodeError: 
     pass 

Bên chức năng make_node của bạn, có nó nâng cao một AlreadyNodeError nếu đối số đã là một nút.

+0

+1 Suy nghĩ của tôi chính xác (gần như chính xác). – zeekay

+0

Điều này không thay đổi cuộc gọi unkosher thành 'type()' thành hàm 'make_node()'? – Owen

+0

Không nếu hàm make_node có một khối Thử: Tăng lên: trong đó nó cố gắng biến nó thành một nút và, khi phát hiện ra nó đã là một nút, hãy nêu ra một lỗi. – Jonathanb

2

Bạn không nhất thiết phải có phương pháp xử lý từng loại, đặc biệt nếu thao tác đơn giản là tất cả sẽ xảy ra. Một cách tiếp cận Pythonic chung sẽ làm điều gì đó như:

def run(self): 
    try: 
     ...assume it's a str 
    except TypeError: 
     ...oops, not a str, we'll address that 

này theo Easier to ask for forgiveness than permission (EAFP) phong cách mã hóa, mà nói chung là nhanh hơn và đơn giản hơn.

+0

Tôi nghĩ chúng ta đang nói về những thứ khác nhau, ý tôi là kiểu trả về; nhưng câu trả lời của bạn cũng sẽ áp dụng ở đó - nó có thể hoạt động tốt trong trường hợp của tôi. – Owen

+0

Tôi nghĩ rằng đó là một ý tưởng tồi để có một phương thức trả về các kiểu đối tượng hoàn toàn khác nhau. Tôi muốn nhiều hơn để đối phó với hai phương pháp riêng biệt trong trường hợp đó. – zeekay

+0

Không bạn đúng, nó * là * xấu; nó chỉ là như vậy * thuận tiện * mà nó hấp dẫn ngay bây giờ. – Owen

1

Sử dụng type() phát hiện loại của một biến thực sự là một thực tế xấu, vì nó sẽ không cho phép một đối tượng kế thừa từ các loại mong muốn (str trong trường hợp của bạn), một cách tốt hơn là sử dụng isinstance():

if isinstance(my_var, str): 
    my_code_here() 

Ngoài ra, một cách thức nhiệt tình để làm điều này có thể là cách gõ vịt như bạn đã đề cập, tại sao bạn không đặt mã trong khối try/except? Vì vậy, một ngoại lệ sẽ chỉ bị bắt nếu giá trị không hành động như mong đợi (nếu nó quacks và đi như vịt, đó là một con vịt).

0
class Node(object): 
    def __new__(cls, contents): 
    return contents if isinstance(contents, cls) else object.__new__(cls) 
    def __init__(self, contents): 
    # construct from string... 

class Manager(object): 
    def do_something(self, runner, *args): 
    do_something_else(Node(runner(*args))) 

Bây giờ nó doesn' t vấn đề nếu các Á hậu trả về một nút hoặc một chuỗi.

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