2013-02-12 34 views
5

Tôi đang xây dựng một nền tảng rất cơ bản dưới dạng mô-đun Python 2.7. Mô-đun này có vòng lặp đọc-eval-in trong đó các lệnh người dùng đã nhập được ánh xạ tới các cuộc gọi chức năng. Vì tôi đang cố gắng làm cho nó dễ dàng để xây dựng các mô-đun plugin cho nền tảng của tôi, các cuộc gọi chức năng sẽ từ mô-đun chính của tôi đến một mô-đun plugin tùy ý. Tôi muốn một trình xây dựng plugin có thể chỉ định lệnh mà anh ta muốn kích hoạt chức năng của mình, vì vậy tôi đã tìm kiếm một cách Pythonic để nhập từ xa một ánh xạ trong lệnh-> function dict trong module Chính từ mô-đun plugin.Hầu hết các cách Pythonic để cung cấp siêu dữ liệu chức năng tại thời gian biên dịch?

tôi đã xem xét một vài điều:

  1. Phương pháp phân tích cú pháp tên: module chính sẽ nhập khẩu các module cắm và quét nó cho tên phương pháp phù hợp với một định dạng nhất định. Đối với ví dụ , có thể thêm phương thức download_file_command (tệp) vào phương thức dict là "tệp tải xuống" -> download_file_command. Tuy nhiên, việc nhận được một tên lệnh ngắn gọn, dễ gõ (ví dụ: "dl") yêu cầu tên của hàm cũng ngắn, điều này không tốt cho mã khả năng đọc. Nó cũng yêu cầu nhà phát triển plugin phải tuân theo định dạng đặt tên chính xác .

  2. Cross-mô-đun trang trí: trang trí sẽ cho phép các nhà phát triển Plugin tên chức năng của mình bất cứ điều gì ông muốn và chỉ cần thêm một cái gì đó như @ Main.register ("dl"), nhưng họ sẽ thiết yêu cầu tôi đều sửa đổi không gian tên của mô-đun khác và giữ trạng thái toàn cầu trong mô-đun chính là . Tôi hiểu điều này rất tệ.

  3. Cùng module trang trí: sử dụng logic tương tự như trên, tôi có thể thêm một trang trí có thêm tên của chức năng đối với một số lệnh name> chức năng lập bản đồ địa phương để các mô-đun plugin và lấy ánh xạ đến Mô-đun chính có cuộc gọi API . Điều này đòi hỏi phải có một số phương thức nhất định hoặc mặc dù được thừa hưởng và - nếu sự hiểu biết của tôi về trang trí là chính xác - chức năng sẽ chỉ đăng ký lần đầu tiên nó chạy và sẽ không cần đăng ký lại sau mỗi lần sau đó.

Vì vậy, những gì tôi thực sự cần là cách Pythonic để chú thích hàm có tên lệnh sẽ kích hoạt hàm và cách đó không thể là tên của hàm. Tôi cần phải có khả năng trích xuất lệnh tên-> ánh xạ hàm khi tôi nhập mô-đun và mọi công việc ít hơn ở phía nhà phát triển plugin là một điểm cộng lớn.

Cảm ơn sự giúp đỡ, và lời xin lỗi của tôi nếu có bất kỳ sai sót nào trong sự hiểu biết Python của tôi; Tôi tương đối mới với ngôn ngữ.

+1

Chỉ cần kiểm tra bạn nhận ra điều này, nhưng trang trí (và chức năng định nghĩa) thực hiện tại thời gian chạy, điều duy nhất Python thực hiện tại thời gian biên dịch là biên dịch mã của bạn. – Duncan

+1

Không có gì sai cả với phương pháp 2. Yêu cầu họ làm điều gì đó như 'import plugin' và làm' @ plugin.register ('my_method_name') \ ndef somefunc_meeting_an_api_spec(): ... ' –

+0

Bạn có thể muốn xem xét cách [plac] (http://pypi.python.org/pypi/plac) thực hiện điều đó. – georg

Trả lời

4

Các hàm do người dùng xác định có thể có thuộc tính tùy ý. Vì vậy, bạn có thể chỉ định rằng các hàm plug-in có một thuộc tính với một tên nào đó. Ví dụ:

Sau đó, trong mô-đun của bạn, bạn có thể xây dựng một bản đồ như thế này:

import inspect #from standard library 

import plugin 

mapping = {} 

for v in plugin.__dict__.itervalues(): 
    if inspect.isfunction(v) and v.hasattr('command_name'): 
     mapping[v.command_name] = v 

Để đọc về các thuộc tính tùy ý cho các chức năng người dùng định nghĩa thấy the docs

+0

Đây chính là loại điều tôi đang tìm kiếm! – mieubrisse

+0

Tôi nhận thấy câu hỏi này hơi cũ, nhưng hãy xem câu hỏi liên quan của tôi tại đây http://stackoverflow.com/questions/37192712/how-can-attributes-on-functions-survive-wrapping. Bạn có gặp vấn đề với việc gói các thuộc tính được lưu trữ trên các hàm không? – matthewatabet

1

Có hai phần trong một hệ thống plugin:

  1. Khám phá plugin
  2. kích hoạt một số thực thi mã trong một plugin

Các giải pháp được đề xuất trong địa chỉ câu hỏi của bạn chỉ là phần thứ hai.

Có nhiều cách để thực hiện cả hai tùy thuộc vào yêu cầu của bạn ví dụ như, để cho phép plugin, họ có thể được xác định trong một tập tin cấu hình cho ứng dụng của bạn:

plugins = some_package.plugin_for_your_app 
    another_plugin_module 
    # ... 

Để thực hiện tải các module plugin:

plugins = [importlib.import_module(name) for name in config.get("plugins")] 

Để có được một cuốn từ điển: command name -> function:

commands = {name: func 
      for plugin in plugins 
      for name, func in plugin.get_commands().items()} 

Tác giả trình cắm có thể sử dụng bất kỳ phương pháp nào để triển khai get_commands() ví dụ: sử dụng tiền tố hoặc trang trí - ứng dụng chính của bạn không nên quan tâm miễn là get_commands() trả về từ điển lệnh cho mỗi plugin.

Ví dụ, some_plugin.py (nguồn đầy đủ):

def f(a, b): 
    return a + b 

def get_commands(): 
    return {"add": f, "multiply": lambda x,y: x*y} 

Nó định nghĩa hai lệnh add, multiply.

+0

Cảm ơn bạn; Tôi thích lời giải thích thiết kế đằng sau này. Lời giải thích này cho thấy rằng một siêu lớp thực hiện danh sách get_commands() có thể là một ý tưởng hay, vì vậy các nhà phát triển phải làm ít công việc nhất có thể. – mieubrisse

+0

@mieubrisse: không yêu cầu lớp học. Tôi đã thêm ví dụ plugin hoàn chỉnh. – jfs

4

tòa nhà hoặc Đứng trên phần đầu tiên của câu trả lời của @ ericstalbot, bạn có thể thấy thuận tiện khi sử dụng một trình trang trí như sau.

################################################################################ 
import functools 
def register(command_name): 
    def wrapped(fn): 
     @functools.wraps(fn) 
     def wrapped_f(*args, **kwargs): 
      return fn(*args, **kwargs) 
     wrapped_f.__doc__ += "(command=%s)" % command_name 
     wrapped_f.command_name = command_name 
     return wrapped_f 
    return wrapped 
################################################################################ 
@register('cp') 
def copy_all_the_files(*args, **kwargs): 
    """Copy many files.""" 
    print "copy_all_the_files:", args, kwargs 
################################################################################ 

print "Command Name: ", copy_all_the_files.command_name 
print "Docstring : ", copy_all_the_files.__doc__ 

copy_all_the_files("a", "b", keep=True) 

Output khi chạy:

Command Name: cp 
Docstring : Copy many files.(command=cp) 
copy_all_the_files: ('a', 'b') {'keep': True} 
Các vấn đề liên quan