2009-09-18 34 views
8

A generic function được gửi dựa trên loại của tất cả các đối số của nó. Lập trình viên định nghĩa một số hiện thực của một hàm. Giá trị đúng được chọn vào thời gian gọi dựa trên các loại đối số của nó. Điều này rất hữu ích cho việc thích nghi đối tượng trong số những thứ khác. Python có một vài chức năng chung bao gồm len().Triển khai hàm chung chung được duy trì tốt nhất cho Python là gì?

Những gói có xu hướng để cho phép mã mà trông như thế này:

@when(int) 
def dumbexample(a): 
    return a * 2 

@when(list) 
def dumbexample(a): 
    return [("%s" % i) for i in a] 

dumbexample(1) # calls first implementation 
dumbexample([1,2,3]) # calls second implementation 

Một ví dụ ít câm Tôi đã suy nghĩ về thời gian gần đây sẽ là một thành phần web mà đòi hỏi phải có một tài khoản. Thay vì yêu cầu một khuôn khổ web cụ thể, trình tích hợp sẽ chỉ cần viết một cái gì đó như:

class WebComponentUserAdapter(object): 
    def __init__(self, guest): 
     self.guest = guest 
    def canDoSomething(self): 
     return guest.member_of("something_group") 

@when(my.webframework.User) 
componentNeedsAUser(user): 
    return WebComponentUserAdapter(user) 

Python có một vài triển khai chức năng chung. Tại sao tôi lại chọn một trong những người khác? Cách triển khai đó được sử dụng trong các ứng dụng như thế nào?

Tôi quen thuộc với zope.component.queryAdapter(object, ISomething) của Zope. Lập trình viên đăng ký một bộ điều hợp có thể gọi mà lấy một lớp đối tượng cụ thể làm đối số của nó và trả về một cái gì đó tương thích với giao diện. Đó là một cách hữu ích để cho phép các plugin. Không giống như đắp vá khỉ, nó hoạt động ngay cả khi một đối tượng cần phải thích nghi với nhiều giao diện với cùng một tên phương thức.

+1

Hàm 'len' chỉ đơn giản gọi phương thức' __len__' của đối tượng. Đây có phải là ý bạn của hàm "chung" không? Nếu có, có rất ít chức năng hoạt động như thế này. Bạn có cái gì khác trong tâm trí? Nếu vậy, hãy chọn một ví dụ tốt hơn. –

+0

Bạn đang nói về đa hình? Hay bạn đang nói về các loại khỉ hiện có đang vá để thêm tính đa hình? –

+0

nó có vẻ như (dựa trên? Và các liên kết wikipedia cung cấp) joeforker đang tìm kiếm để thực hiện các chức năng quá tải. Tôi tin rằng anh ấy đang đề cập đến "chung chung" theo nghĩa là hàm được gọi bằng 1 tên nhưng thực thi mã khác dựa trên các tham số được truyền. Như vậy: my_function (a) -> thực hiện function_a my_function (b) -> thực hiện function_b Đó là một thực hiện nhiều phong cách của phân tích cú pháp ** kwargs để có được mà mã để thực hiện – Mark

Trả lời

7

Tôi muốn giới thiệu thư viện PEAK-Rules bởi P. Eby. Bởi cùng một tác giả (không được chấp nhận) là gói RuleDispatch (tiền thân của Quy tắc PEAK). Loại thứ hai không còn được duy trì IIRC nữa.

PEAK-Rules có rất nhiều tính năng đẹp, một là, đó là (tốt, không dễ dàng, nhưng) có thể mở rộng. Bên cạnh công văn "cổ điển" trên các loại ony, nó có tính năng công văn trên các biểu thức tùy ý là "người giám hộ".

Chức năng len() không phải là một hàm tổng quát thật sự (ít nhất là trong ý nghĩa của gói đề cập ở trên, và cũng theo nghĩa, thuật ngữ này được sử dụng trong các ngôn ngữ như Common Lisp, Dylan hoặc Cecil), vì nó đơn giản là một là cú pháp thuận lợi cho một cuộc gọi đến đặc biệt được đặt tên (nhưng nếu không thường xuyên) phương pháp:

len(s) == s.__len__() 

Cũng lưu ý, rằng đây là đơn cử chỉ, có nghĩa là, người nhận thực tế (s trong đoạn code trên) xác định việc thực hiện phương pháp gọi là. Và thậm chí một giả thuyết

def call_special(receiver, *args, **keys): 
    return receiver.__call_special__(*args, **keys) 

vẫn là một chức năng gửi đơn, vì chỉ người nhận được sử dụng khi phương pháp được gọi được giải quyết. Các đối số còn lại chỉ đơn giản là truyền vào, nhưng chúng không ảnh hưởng đến việc lựa chọn phương thức.

Điều này khác với nhiều công văn, nơi không có bộ thu chuyên dụng và tất cả đối số được sử dụng để tìm cách triển khai phương pháp thực tế để gọi. Đây là, những gì thực sự làm cho toàn bộ điều đáng giá. Nếu đó chỉ là một loại đường cú pháp kỳ quặc, không ai có thể bận tâm với việc sử dụng nó, IMHO.

from peak.rules import abstract, when 

@abstract 
def serialize_object(object, target): 
    pass 

@when(serialize_object, (MyStuff, BinaryStream)) 
def serialize_object(object, target): 
    target.writeUInt32(object.identifier) 
    target.writeString(object.payload) 

@when(serialize_object, (MyStuff, XMLStream)) 
def serialize_object(object, target): 
    target.openElement("my-stuff") 
    target.writeAttribute("id", str(object.identifier)) 
    target.writeText(object.payload) 
    target.closeElement() 

Trong ví dụ này, một cuộc gọi như

serialize_object(MyStuff(10, "hello world"), XMLStream()) 

coi cả lập luận để quyết định, phương pháp nào thực sự phải được gọi.

Để có một kịch bản sử dụng tốt đẹp của các hàm chung trong Python, tôi khuyên bạn nên đọc mã đã được cấu trúc lại của peak.security mang lại một giải pháp rất thanh lịch để kiểm tra quyền sử dụng các hàm chung (sử dụng RuleDispatch).

+0

gì về http: // pypi .python.org/pypi/simplegeneric cũng bởi PJE? – joeforker

+0

AFAIK đây là một triển khai khá đơn giản, chỉ thực hiện công văn dựa trên loại. Tôi đã không thực sự nhìn vào nó, vì vậy tôi không thể nói bất cứ điều gì về nó. Kinh nghiệm của tôi chủ yếu là với 'RuleDispatch' và người kế nhiệm của nó. – Dirk

+1

simplegeneric là đơn giản hơn FAR, nhưng, tôi nghĩ rằng, không phải là chủ động duy trì như PEAK phức tạp hơn và phong phú hơn. –

0

Bạn có thể sử dụng một công trình như thế này:

def my_func(*args, **kwargs): 
    pass 

Trong trường hợp này args sẽ là một danh sách của bất kỳ đối số vô danh, và kwargs sẽ là một từ điển trong những người đặt tên. Từ đây bạn có thể phát hiện các loại của chúng và hành động phù hợp.

+1

Điều này không có ý nghĩa trong ngữ cảnh của câu hỏi được sửa đổi. –

+0

Ya, nó có vẻ như là câu trả lời đúng lúc đó. – defrex

0

Tôi không thể thấy điểm trong các hàm "chung" này. Nghe có vẻ giống như đa hình đơn giản.

Các tính năng "chung" của bạn có thể được triển khai như thế này mà không cần sử dụng bất kỳ nhận dạng loại thời gian chạy nào.

class intWithDumbExample(int): 
    def dumbexample(self): 
     return self*2 

class listWithDumbExmaple(list): 
    def dumpexample(self): 
     return [("%s" % i) for i in self] 

def dumbexample(a): 
    return a.dumbexample() 
+1

Cách tiếp cận của bạn không cho phép các cuộc gọi đến 'dumbexample (1)' theo thông số kỹ thuật của OP (và tất cả các gói chức năng chung đều cho phép) - bạn cần gói gọn trên mỗi cuộc gọi như vậy, thường là chuyển đổi dựa trên loại (eep). Nhưng đa hình dựa trên ONE loại vẫn gần như OK: nơi OOP cổ điển hoàn toàn sụp đổ và các chức năng chung (như trong Common Lisp, Dylan, ...) thực sự tỏa sáng là trong dispatching dựa trên các loại đối số MULTIPLE (tránh klutzes như Visitor, __radd__ so với __add__, và hàng triệu kiều khác cần có). –

+0

@ Alex Martelli: Và, có lẽ, tránh các vấn đề ép buộc và các vấn đề công văn kép. Câu hỏi của OP - và ví dụ - không chạm vào bất kỳ điều nào trong số này. Tôi vẫn chưa rõ ràng về câu hỏi * thực *. Hoặc có lẽ câu hỏi chỉ là giả thuyết. –

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