The scenario is as follows. The API of my program will allow the user to perform some method on a particular element (let's say a tuple from a database). If this parameter is not passed to the function, then the "default" behavior is to look for a list of elements (from a database query, for example) and to act on all of them. Like this:

task(simple_object) # will process simple_object
task() # query the database with some previously defined query

And this is what I did (implementation in Python):

@task(query='SELECT * FROM User WHERE age < 18')
def task_disable_account(user):

This way I can call the task_disable_account with one particular User or I can call it with no arguments and then every tuple from the SQL query will be processed. The definition of task decorator is as follows:

task_registry = []
def task(query):
    Task = namedtuple('Task', ['func', 'query'])
    def decorate(func):
        def queried(*_args):
            if not _args:
                map(func, search(query)) # execute on result from SQL
            return func(*_args) # execute on 1 passed parameter
        task_registry.append(Task(queried, query))
        return queried
    return decorate

And this is how I use it (just an example, actual code is more complicated):

def main(): # lets assume a main() function
    for func, _ in task_registry:
        # I'm getting and ID from sys.argv and returning a User object
        # If not passed via arguments, call function with no parameters
        user = get_from_argv()
        if user:

The idea behind all this is:

  1. The tasked method should only take care of the processing of 1 element, centralising the way we search for, and how we iterate over them.
  2. Make it easy to add new task methods to our implementation, not having to repeat the boilerplate code over and over.
  • 1
    \$\begingroup\$ You append to the task_registry… but where or how do you ever use it? \$\endgroup\$ – 200_success Mar 30 '16 at 19:26
  • \$\begingroup\$ I added a small example usage. @200_success \$\endgroup\$ – licorna Mar 30 '16 at 20:28

Not going into the question as to whether or not this is a good idea, I can see very little wrong with the solution.

One thing I would very much like to suggest is the following.

Instead of

map(func, search(query))

please write

for result in search(query):

There are actually some differences:

  • In Python 3, map builds a iterable, and func(result) will only be called if the iterable is looped over. In this case, this is most definitely not what you want!
  • In Python 2, map builds a list, and thus evaluates everything. Better, but an intermediate list is now going to be build and thrown away.

In general I would recommend only using map for functions that are side-effect free.

Another thing I'd like to remark is where you build the namedtuple. You do it inside the function, building a new Task class for every task you add. Please move that code outside of the function task.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.