2015-09-16 11 views
5

Tôi đang viết một lớp học có một số phương pháp hoạt động trên các loại tương tự của đối số:cách Pythonic chuyển đổi các tham số để cùng một tiêu chuẩn trong tất cả các phương pháp của một lớp

class TheClass(): 
    def func1(self, data, params, interval): 
     .... 
    def func2(self, data, params): 
     .... 
    def func3(self, data, interval): 
     .... 
    def func4(self, params): 
     .... 
    ... 

Có một quy ước nhất định về những đối số (ví dụ: data/params phải là numpy.farrays, interval - a list của 2 floats), nhưng tôi muốn cho phép người dùng có nhiều tự do hơn: ví dụ: chức năng nên chấp nhận int như data hoặc params, hoặc chỉ có một int như một interval và sau đó cho rằng đây là điểm cuối với điểm xuất phát 0 vv

Vì vậy, để tránh tất cả những biến đổi trong phương pháp, mà nên làm chỉ logic, tôi đang sử dụng trang trí như thế này:

def convertparameters(*types): 
    def wrapper(func): 

     def new_func(self, *args, **kwargs): 
      # Check if we got enough parameters 
      if len(types) > len(args): 
       raise Exception('Not enough parameters') 
      # Convert parameters 
      new_args = list(args) 
      for ind, tip in enumerate(types): 
       if tip == "data": 
        new_args[ind] = _convert_data(new_args[ind]) 
       elif tip == "params": 
        new_args[ind] = _convert_params(new_args[ind]) 
       elif tip == "interval": 
        new_args[ind] = _convert_interval(new_args[ind]) 
       else: 
        raise Exception('Unknown type for parameter') 
      return func(self, *new_args, **kwargs) 

     return new_func 
    return wrapper 

đâu _convert_data, _convert_params_convert_interval làm công việc bẩn. Sau đó, tôi xác định các lớp như sau:

class TheClass(): 
    @convertparameters("data", "params", "interval") 
    def func1(self, data, params, interval): 
     .... 

    @convertparameters("data", "params") 
    def func2(self, data, params): 
     .... 

    @convertparameters("data", "interval") 
    def func3(self, data, interval): 
     .... 

    @convertparameters("params") 
    def func4(self, params): 
     .... 
    ... 

Nó hiện các trick, nhưng cũng có khá nhiều điều khá đáng lo ngại:

  1. Nó clutters mã (mặc dù nó là một vấn đề nhỏ, và tôi tìm giải pháp này nhỏ gọn hơn nhiều so với lời gọi rõ ràng của hàm chuyển đổi ở đầu mỗi phương thức)
  2. Nếu tôi cần kết hợp trang trí này với một trang trí khác (@staticmethod hoặc thứ gì đó sau quá trình xử lý) những trang trí này quan trọng
  3. .210
  4. Điều đáng lo ngại nhất là các trang trí của mẫu này hoàn toàn ẩn cấu trúc tham số của phương pháp và IPython thấy nó như func1(*args, **kwargs)

Có bất kỳ tốt hơn (hoặc nhiều "Pythonic") cách để làm biến đổi thông số khổng lồ như vậy?

UPDATE 1: GIẢI PHÁP dựa trên n9code 's đề nghị

Để tránh nhầm lẫn có một sửa đổi của wrapper convertparameters rằng giải quyết vấn đề thứ 3 (mặt nạ ký và docstring của phương pháp) - được đề xuất bởi n9code cho Python> 2.5.

Sử dụng decorator mô-đun (được cài đặt riêng: pip install decorator) chúng ta có thể chuyển tất cả của chức năng "siêu dữ liệu" (docstring, tên và chữ ký) đồng thời loại bỏ cấu trúc lồng nhau bên trong wrapper

from decorator import decorator 

def convertparameters(*types): 

    @decorator 
    def wrapper(func, self, *args, **kwargs): 
     # Check if we got enough parameters 
     if len(types) > len(args): 
      raise Exception('Not enough parameters') 
     # Convert parameters 
     new_args = list(args) 
     for ind, tip in enumerate(types): 
      if tip == "data": 
       new_args[ind] = _convert_data(new_args[ind]) 
      elif tip == "params": 
       new_args[ind] = _convert_params(new_args[ind]) 
      elif tip == "interval": 
       new_args[ind] = _convert_interval(new_args[ind]) 
      else: 
       raise Exception('Unknown type for parameter') 
     return func(self, *new_args, **kwargs) 

    return wrapper 

UPDATE 2: SỬA ĐỔI GIẢI PHÁP dựa trên zmbq 's đề nghị

Sử dụng mô-đun inspect chúng tôi cũng có thể thoát khỏi các đối số của trang trí, kiểm tra tên của argu ments của hàm ban đầu.Điều này sẽ loại bỏ một lớp trang trí khác

from decorator import decorator 
import inspect 

@decorator 
def wrapper(func, self, *args, **kwargs): 
    specs = inspect.getargspec(func) 

    # Convert parameters 
    new_args = list(args) 
    for ind, name in enumerate(specs.args[1:]): 
     if name == "data": 
      new_args[ind] = _convert_data(new_args[ind]) 
     elif name == "params": 
      new_args[ind] = _convert_params(new_args[ind]) 
     elif name == "interval": 
      new_args[ind] = _convert_interval(new_args[ind]) 
    return func(self, *new_args, **kwargs) 

Và việc sử dụng đơn giản hơn nhiều. Điều quan trọng duy nhất là tiếp tục sử dụng cùng tên cho các đối số giữa các hàm khác nhau.

class TheClass(): 
    @convertparameters 
    def func1(self, data, params, interval): 
     .... 

    @convertparameters 
    def func2(self, data, params): 
     .... 

Trả lời

3
  1. tôi sẽ đồng ý với bạn, rằng đây là một giải pháp tốt hơn, vì vậy không có gì để làm ở đây.
  2. Tôi nghĩ rằng bạn sẽ đưa ra một thứ tự tốt và sẽ tiếp tục, điều này sẽ không trở thành vấn đề trong tương lai.
  3. Đây là vấn đề, bạn nói đúng, nhưng nó đang được giải quyết dễ dàng với functools.wraps. Đơn giản chỉ cần trang trí newfunc của bạn với nó, và bạn sẽ lưu chữ ký của chức năng ban đầu.

    from functools import wraps 
    
    def convertparameters(*types): 
        def wrapper(func): 
         @wraps(func) 
         def new_func(self, *args, **kwargs): 
          pass # Your stuff 
    

    Tough, công trình này chỉ dành cho Python 3. Trong Python 2, chữ ký sẽ không được duy trì, chỉ __name____doc__ sẽ. Vì vậy, trong trường hợp của Python, bạn có thể sử dụng các mô-đun decorator:

    from decorator import decorator 
    
    def convertparameters(*types): 
        @decorator 
        def wrapper(func, self, *args, **kwargs): 
         pass # return a result 
        return wrapper 
    

EDIT dựa trên user3160867's cập nhật.

+0

Cảm ơn, tôi không biết về 'kết thúc tốt đẹp'. Tôi giả sử có nên cũng 'return new_func' và' return wrapper'? – Vladimir

+0

Tôi có thêm một câu hỏi: 'kết thúc tốt đẹp' chuyển đúng chuỗi tài liệu. Tuy nhiên chữ ký vẫn là 'func1 (* args, ** kwargs)'. Có cách nào để chuyển chữ ký không? – Vladimir

+0

Tôi đoán bạn đang sử dụng Python 2. Trong trường hợp của Python 3 chữ ký cũng sẽ được bảo tồn. Kiểm tra cập nhật của tôi. – bagrat

1

Để làm cho mã ngắn hơn một chút, không cung cấp đối số cho người trang trí - suy luận chúng từ hàm được trang trí.

+0

Điều đó thật thú vị! Bạn có nghĩa là tuyên bố 'def wrapper (func, self, ** kwargs)' mà không có 'args' và suy ra từ' kwargs'? – Vladimir

+0

Không, bạn có thể sử dụng: http://stackoverflow.com/questions/218616/getting-method-parameter-names-in-python – zmbq

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