Noah McIlraith
Noah McIlraith

Reputation: 14292

Is there a better way to specify named arguments when calling in Python

template.render(current_user=current_user, thread=thread, messages=messages)

Is there a dont-repeat-yourself compliant way to do whatever=whatever? Like a magic symbol to prepend the variable name with or something like this ~whatever, ~something, ~etc?

Upvotes: 0

Views: 173

Answers (5)

martineau
martineau

Reputation: 123473

Here's a decorator, inspired to some degree by the one in a recipe titled Keyword Argument Injection with Python Decorators, that might help. It certainly cuts down on the repetition, maybe too much.

import sys

def injectlocalargs(inFunction):

    def outFunction(*args, **kwargs):
        # get decorated function's argument names
        func_argnames = inFunction.func_code.co_varnames

        # caller's local namespace
        namespace = sys._getframe(1).f_locals

        # add values of any arguments named in caller's namespace
        kwargs.update([(name,namespace[name]) for name in func_argnames if name in namespace])

        # call decorated function and return its result    
        return inFunction(*args, **kwargs)

    return outFunction

##### testbed #####

class template_class:
    @injectlocalargs
    def render(self, extra_stuff, current_user, thread, messages):
        print 'render() args'
        print '  extra_stuff:%s, current_user:%s, thread:%s, messages:%s' % (extra_stuff, current_user, thread, messages)

def test():
    current_user = 'joe'
    thread = 42
    messages = ['greetings',"how's it going?"]
    template = template_class()

    template.render('extra_stuff')

test()

Output:

render() args
  extra_stuff:extra_stuff, current_user:joe, thread:42, messages:['greetings', "how's it going?"]

Upvotes: 0

Ned Batchelder
Ned Batchelder

Reputation: 375604

If your template.render() method accepts any keyword arguments, and you don't mind passing it extra arguments that it won't use, then use:

template.render(**locals())

This will pass every local variable in under its own name.

Keep in mind that many people will object to the sloppiness implicit in this solution.

Upvotes: 1

SingleNegationElimination
SingleNegationElimination

Reputation: 156178

I sort of like aaronasterling's idea, but obviously it's not quite short. suppose you have a naming convention for local variables. variables that will be passed to the call have one naming style and everything else follows a different convention. For instance, suppose you just say that the leading character of 'private' locals begin with an underscore. thus:

def localargs (kwargs):
    return dict((k, v) for (k, v) in kwargs.iteritems() if k[0] != '_')

def somefunction(public_arg, _private_arg):
    public_local = "foo"
    _private_local = "bar"
    ...
    template.render(**localargs(locals()))

but that's hideous, don't do that.

Upvotes: 0

AndiDog
AndiDog

Reputation: 70148

You can pass keyword arguments from a dictionary if you want.

def a(b, c, d):
    pass

someArgs = {"b" : 1, "c" : 2, "d" : 3}
a(**someArgs)

If you want to prepend something to the variable names, you can change the dictionary keys:

a(**dict(("prefix_" + k, v) for k, v in someArgs.items()))

And as an aside, you know that you can't specify arguments called "~arg" in your source code?!

Upvotes: 0

Felix Kling
Felix Kling

Reputation: 816472

No, there is not.

For what it's worth, you can create a dictionary with your parameters and pass it with:

template.render(**parameters)

Note: You should always favor readability!

Upvotes: 4

Related Questions