3

Let's say that I have a list of integer arguments in a function, and I want to add 1 to each one of them. Is there a clean way of doing such, something like this pseudo-code:

def foo(a, b, c):
    for argument in arguments:
        argument += 1
    ...

In particular, I want to avoid explicitly referencing each of these arguments inside the code, and just iterate over all the arguments. Changing the function arguments to just a list is not an option, since a, b, c have a specific meaning inside the code.

5
  • If you're expecting that to have any effect outside the function, you need to read up on how Python variables and assignment work. Commented Jun 26, 2017 at 1:06
  • In my case, I don't really care about the values outside the function. Commented Jun 26, 2017 at 1:07
  • But then for what will you use these? Since integers are immutable, you cannot simply alter the variables with which you made the call, so what are you aiming to do? Commented Jun 26, 2017 at 1:10
  • 1
    It would help if you were to explain what is the real problem you are trying to solve here in which you are trying to do this? Commented Jun 26, 2017 at 1:13
  • 1
    This was a simplification of my real function, for which an answer is non-dependent on the usage. For completeness, I need to round the values before calling another function. I cannot do such in the function calling foo since I have no control over that codebase. Commented Jun 26, 2017 at 1:13

3 Answers 3

3

With explicit referencing

A clean way to do this is probably using iterable unpacking and a mapping, like:

def foo(a,b,c):
    a,b,c = map(lambda x:x+1,(a,b,c))
    # ...

Or for rounding:

 def foo(a,b,c):
     a,b,c = map(round,(a,b,c))
     # ...

Since this is still explicit, and furthermore keeps the parameters in the function signature of foo.

You can of course use *args and **kwargs and manipulate these (directly), but that could result in losing information about what the parameters a, b and c mean (their semantics).

Using a decorator (apply the function on all parameters)

Another way to do it, that will remove the semantics of the parameters is using a decorator:

def apply_func_args(func):
    def dec(f):
        def g(*args,**kwargs):
            return f(*map(func,args),**{k:func(v) for k,v in kwargs.items()})
        g.__name__ = f.__name__
        g.__doc__ = f.__doc__
        return g
    return dec

Then you can put the decorator on foo, like:

@apply_func_args(round)
def foo(a,b,c):
    # ...

Now if you call foo(1.4,1.3,1.9), foo will obtain (a,b,c) == (1,1,2). For example, if you want to print a, b, and c:

>>> def apply_func_args(func):
...     def dec(f):
...         def g(*args,**kwargs):
...             return f(*map(func,args),**{k:func(v) for k,v in kwargs.items()})
...         g.__name__ = f.__name__
...         g.__doc__ = f.__doc__
...         return g
...     return dec
... 
>>> @apply_func_args(round)
... def foo(a,b,c):
...     print((a,b,c))
... 
>>> foo(1.4,1.3,1.9)
(1, 1, 2)
>>> foo(1.4,2.5,c=0)
(1, 2, 0)
>>> foo(b=1.4,a=2.5,c=0)
(2, 1, 0)

So here we defined a clean way to apply the function (round) on all named and unnamed parameters of foo. As a result, if we call foo, foo will always obtain rounded values.

Sign up to request clarification or add additional context in comments.

4 Comments

Can this be done without explicitly referencing each of the arguments? I tried using a for loop with both *args and **kwargs, but both are syntactically incorrect.
@DaniyalShahrokhian: yes, you can use a decorator, like demonstrated. This will apply the function to all parameters.
Very complete answer @Willem. I think if there is no simpler implementation, I will go with the explicit referencing. I was hoping that in python there was a way to just reference the function arguments similarly to my pseudo-code, but I think that defying a decorator for just one function seems overkill.
@DaniyalShahrokhian: well you can of course reuse the decorator. Furthermore I think you still have to make it explicit what you are doing (not per se by referencing the variables, but it should be clear what is happening).
2
def foo(a, b, c):
    return call_other(round(a),round(b),round(c))

or

def foo(a, b, c):
    return call_other(a+1,b+1,c+1)

I think if i am understanding what you are trying to do... (from reading the comments)

2 Comments

The main reason I posted this question was to avoid explicitly referencing each one of the arguments. My concern was on a clean way to do it, instead of having multiple mentions of the function round() and variables a, b, c.
explicit is better than implicit
0

Here is an example with no side-effects:

a = [1, 2, 3, 4]
b = [5, 6, 7, 8]
c = [9, 8, 7, 6]
def foo(*args):
    modified = list()
    for idx, arg in enumerate(args):
        modified.append([x + 1 for x in arg])
    print modified
    # do something else

# In [17]: foo(a, b, c)
# [[2, 3, 4, 5], [6, 7, 8, 9], [10, 9, 8, 7]]

With this implementation you can input as many arrays as you like.

1 Comment

This solution would kill the readability of the function, since the arguments stop having a meaning. I modified my question to reflect that the arguments are not just meaningless integers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.