views:

71

answers:

3

One of my classes has a logical numpy array as parameter in many methods repeated (idx_vector=None).

How can I use a decorator to:

  1. automatically specify idx_vector
  2. automatically insert the description into the docstring

Example without decorator:

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)

Usage:

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
+2  A: 

I'm not sure I understand you correctly. As I see it, your problem is that you have a lot of functions which all need to take the argument idx_vector, and you don't want to add it to each of their argument lists. If that's the case:

Short answer: you can't.

Longer answer: well, you could, but the function needs some way to refer to the argument which contains idx_vector. Now you could use *args to pick it up, and then args[0] to refer to it, but that doesn't change the fact that you still need to define some name within each of the functions for the variable which will hold idx_vector.

How about making idx_vector an attribute of myarray? I.e., if self.idx_vector is not None?

You can use a decorator to modify the docstring of a function, but remember that if the docstring is different in each case then there's not much point trying to abstract it away. And if it's the same for each function, well, it's not very good documentation! However, for the sake of example, here's the code:

def addDocstring( func ):
    func.__doc__ = "Some description of the function."
    return func
katrielalex
To put idx_vector as attribute of myarray sounds good! In that way you can keep also a specific indexing in case that more calculations are based on it.
Philipp der Rautenberg
Oh, another thing: you've made a "container" class -- `myarray` isn't an array, it's an empty class with an *attribute* that's an array. Why not simplify matters by making it inherit from `np.array`? Then it'll behave like one (so you can use numpy functions on it) but will still have the extra methods you define.
katrielalex
+2  A: 

Decorators are not the lexical equivalent of the C-preprocessor, nor should you try to make them such. Even if you could, your suggested use is both implicit (and therefore "ugly" per PEP 20) and uninformative which also violates "If the implementation is hard to explain, it's a bad idea."

Better would be:

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"""

Boilerplate comments are bad as they reduce readability. Redundancies like get_ are to be avoided as they are syntactic junk which also reduce readability. Finally if myarray is-a form of another array you might want to inherit from that instead of object. If you are trying to comply with some corporate standard that requires

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

then the standard is broken.

msw
Putting "get_" as a prefix to method names helps to differentiating methods. E.g. if a result comes from a complex calculation (which could take time), "get_", or "comp_" could be the prefix. If the result is a logical index, pointing to special values "where_" could be the prefix, [...] Doesn't that increase readability?
Philipp der Rautenberg
Good idea to collect this information within the class-doc string.
Philipp der Rautenberg
I'll admit that the `get_` bit is debatable, less so if there is no corresponding `set_`. Contrariwise, if you don't know whether a function returns a value, you probably shouldn't be calling it. No return value functions do have their place in Python and not executing `return value` prevents invalid assignments like `p = q.sort()`
msw
+1  A: 

This leads to a rather unobvious interface. Therefore, I don't recommend using the following for anything but play. However, it is possible to create a decorator to do what you request:

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

This shows the doc string has been modified too:

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)
unutbu