Rorschach
Rorschach

Reputation: 3802

Looping through python function arguments

I have a python function that receives many arguements. I want to change all the functions arguements to lowercase. I have this code:

functionOne(para, paraa, parab):
    arguments = locals()
    print arguments
    for keys in arguments:
       arguments[keys] = str(arguments[keys]).lower()
       print arguments[keys]
    print para

With the output:

{'para':'HP', 'paraa':'MA', 'parab':'aM'}
hp
ma
am
HP

I would like para to equal hp not HP.

Upvotes: 0

Views: 11591

Answers (4)

Jeremy
Jeremy

Reputation: 1718

Modifying the return dictionary of locals() is undefined behavior. It won't do what you want. May I suggest using a decorator instead:

from functools import wraps
def lower_args(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        args = list(args)
        for i, a in enumerate(args):
            if isinstance(a, basestring):
                args[i] = a.lower()
        for key, val in kwargs.items():
            if isinstance(val, basestring):
                kwargs[key] = val.lower()
        return f(*args, **kwargs)
    return wrapper

@lower_args
def functionOne(para, paraa, parab):
    print para

Upvotes: 0

mgilson
mgilson

Reputation: 309929

This isn't possible. Python's locals don't modify the names in the function's namespace. According to the docs:

Note The contents of this dictionary should not be modified; changes may1 not affect the values of local and free variables used by the interpreter.

The only solution is to keep using arguments to access the values, or to lowercase each one by hand, or to pass an iterable of arguments rather than explicitly naming each one.

1"may" is probably in there because implementations that aren't Cpython are free to implement locals differently. A better wording might be "changes are not required to affect ..."


At a higher level, having a function with enough arguments that lowercasing "by hand" is cumbersome is a bit of a "code-smell" and might mean that a refactor is in order.

Upvotes: 2

TigerhawkT3
TigerhawkT3

Reputation: 49318

There's no need to mess with locals(). If your function just has positional parameters, it shouldn't have too many, so you can change them one by one.

functionOne(para, paraa, parab):
    arguments = (para, paraa, parab)
    print arguments
    para, paraa, parab = map(str.lower, arguments)
    print para

If your function will have a larger, arbitrary number of arguments, use *:

functionOne(*args):
    print args
    args = tuple(map(str.lower, args))
    print args[0]

Upvotes: 3

Prune
Prune

Reputation: 77847

para = para.lower()
paraa = paraa.lower()
parab = parab.lower()

The loop you wrote works on a constructed list, not the original arguments. here's the full change:

def functionOne(para, paraa, parab):
    arguments = locals()
    print arguments
    for keys in arguments:
        arguments[keys] = str(arguments[keys]).lower()
        print arguments[keys]
    para = para.lower()
    paraa = paraa.lower()
    parab = parab.lower()
    print locals()

functionOne('HP', 'MA', 'aM')

Giving output:

{'parab': 'aM', 'paraa': 'MA', 'para': 'HP'}
am
ma
hp
{'keys': 'para', 'parab': 'am', 'paraa': 'ma', 'arguments': {...}, 'para': 'hp'}

Upvotes: 0

Related Questions