Как я могу использовать декораторы в Python для указания и документирования повторяющихся аргументов методов? - PullRequest
0 голосов
/ 27 июля 2010

Один из моих классов имеет логический массив NumPy в качестве параметра во многих повторяющихся методах ( idx_vector = Нет ).

Как использовать декоратор для:

  1. автоматически указывает idx_vector
  2. автоматически вставляет описание в строку документации

Пример без декоратора:

import numpy as np
class myarray(object):
  def __init__(self, data):
    self.data = np.array(data) 

  def get_sum(self, idx_vector=None):
    """
    Input parameter:
      ``idx_vector``: logical indexing
    ...
    ... (further description)
    """
    if idx_vector == None:
      idx_vector = np.ones(len(self.data))
    return sum(self.data*idx_vector)

  def get_max(self, idx_vector=None):
    """
    Input parameter:
      ``idx_vector``: logical indexing
    ...
    ... (further description)
    """
    if idx_vector == None:
      idx_vector = np.ones(len(self.data))
    return max(self.data*idx_vector)

Использование:

a = [1,2,3,4,5.0]
b = np.array([True, True, False, False, False])

ma = myarray(a)
print(ma.get_max())
# ----> 5.0
print(ma.get_max(b))
# ----> 2.0

Ответы [ 3 ]

2 голосов
/ 27 июля 2010

Декораторы не являются лексическим эквивалентом препроцессора C, и вы не должны пытаться сделать их такими.Даже если бы вы могли, предлагаемое вами использование является неявным (и, следовательно, «уродливым» для PEP 20 ) и неинформативным, что также нарушает «Если реализацию сложно объяснить, это плохая идея».

Лучше было бы:

import numpy as np
class myarray(object):
  """an array that has additional spam, eggs, and spam capabilities
     an idx_vector argument, if present, contains an mask for
     a subset the array, if not specified one will be created based 
     on the Frobnitz Conjecture"""
  def __init__(self, data):
    self.data = np.array(data) 

  def sum(self, idx_vector=None):
    """returns the sum of the elements of the array masked per idx_vector"""
    if idx_vector is None:
      idx_vector = np.ones(len(self.data))

  def max(self, idx_vector=None):
    """returns the largest element of the array masked per idx_vector"""

Комментарии на шаблоне плохие, поскольку они снижают читабельность.Следует избегать избыточностей, таких как get_, поскольку они являются синтаксическим мусором, который также снижает читабельность.Наконец, если myarray является формой другого array, вы можете наследовать его от объекта вместо объекта.Если вы пытаетесь соблюдать какой-либо корпоративный стандарт, который требует

"""
Input parameter:
  ``idx_vector``: logical indexing
..."""

, тогда этот стандарт нарушен.

2 голосов
/ 27 июля 2010

Я не уверен, что правильно вас понял. На мой взгляд, ваша проблема в том, что у вас есть много функций, которые должны принимать аргумент idx_vector, и вы не хотите добавлять его в каждый из их списков аргументов. Если это так:

Краткий ответ: вы не можете.

Более длинный ответ: вы могли бы, но функция нуждается в некотором способе ссылки на аргумент, который содержит idx_vector. Теперь вы можете использовать *args, чтобы поднять его, а затем args[0], чтобы сослаться на него, но это не меняет того факта, что вам все еще нужно определить некоторое имя в каждой из функций для переменная, которая будет содержать idx_vector.

Как насчет сделать idx_vector атрибутом myarray? Т.е. if self.idx_vector is not None?

Вы можете использовать декоратор для изменения строки документации функции, но помните, что если строка документации отличается в каждом случае, нет особого смысла пытаться абстрагировать ее. И если это одинаково для каждой функции, ну, это не очень хорошая документация! Однако, для примера, вот код:

def addDocstring( func ):
    func.__doc__ = "Some description of the function."
    return func
1 голос
/ 27 июля 2010

Это приводит к довольно неочевидному интерфейсу.Поэтому я не рекомендую использовать следующее ни для чего, кроме игры.Однако возможно создать декоратор, чтобы выполнить то, что вы запрашиваете:

import numpy as np
import functools

def add_idx_vector(method):
    @functools.wraps(method)
    def wrapper(self, idx_vector=None):
        if idx_vector == None: idx_vector = np.ones(len(self.data))
        # This uses func_globals to inject a key,value pair into method's 
        # globals to spoof a default value.
        method.func_globals['idx_vector']=idx_vector        
        return method(self)
    wrapper.__doc__='''
        Input parameter:
          ``idx_vector``: logical indexing
        '''+wrapper.__doc__    
    return wrapper

class myarray(object):
    def __init__(self, data):
        self.data = np.array(data)

    @add_idx_vector
    def get_sum(self):
        '''(further description)
        '''
        return sum(self.data*idx_vector)

a = [1,2,3,4,5.0]
b = np.array([True, True, False, False, False])
ma = myarray(a)
print(ma.get_sum())
# ----> 15.0
print(ma.get_sum(b))
# ----> 3.0

Это показывает, что строка документа также была изменена:

help(ma.get_sum)
# Help on method get_sum in module __main__:

# get_sum(self, idx_vector=None) method of __main__.myarray instance
#     Input parameter:
#       ``idx_vector``: logical indexing
#     (further description)
...