【问题标题】:Pythonic way of changing function arguments inside the function在函数内部更改函数参数的 Pythonic 方式
【发布时间】:2017-06-26 01:05:15
【问题描述】:

假设我在一个函数中有一个整数参数列表,我想给每个参数加 1。有没有一种干净的方法可以做到这一点,比如这个伪代码:

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

特别是,我想避免在代码中显式引用每个参数,而只是遍历所有参数。将函数参数仅更改为列表不是一种选择,因为 a, b, c 在代码中具有特定含义。

【问题讨论】:

  • 如果您希望它在函数之外产生任何影响,您需要阅读how Python variables and assignment work
  • 就我而言,我并不真正关心函数之外的值。
  • 但是你会用这些做什么呢?由于整数是不可变的,您不能简单地更改调用时使用的变量,那么您的目标是什么?
  • 如果您要解释您在此处尝试解决的真正问题是什么,这将有所帮助?
  • 这是我实际功能的简化,其答案不依赖于使用情况。为了完整起见,我需要在调用另一个函数之前对值进行四舍五入。我不能在调用 foo 的函数中这样做,因为我无法控制该代码库。

标签: python function arguments


【解决方案1】:

带有显式引用

clean 方法可能是使用 iterable unpacking 和映射,例如:

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

或四舍五入:

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

由于这仍然是显式的,而且还保留了foo的函数签名中的参数。

您当然可以使用*args**kwargs 并(直接)操作它们,但这可能会导致丢失有关参数abc 的含义(它们的语义)的信息。

使用装饰器(将函数应用于所有参数)

另一种去除参数语义的方法是使用装饰器

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

然后你可以把装饰器放在foo上,比如:

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

现在如果您调用foo(1.4,1.3,1.9)foo 将获得(a,b,c) == (1,1,2)。例如,如果你想printabc

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

因此,我们在这里定义了一种简洁的方法,将函数 (round) 应用于foo所有 命名和未命名参数。因此,如果我们调用foofoo 总是会得到四舍五入的值。

【讨论】:

  • 可以在不明确引用每个参数的情况下做到这一点吗?我尝试对 *args**kwargs 使用 for 循环,但两者在语法上都不正确。
  • @DaniyalShahrokhian:是的,你可以使用装饰器,就像演示的那样。这会将函数应用于所有参数。
  • 非常完整的答案@Willem。我认为如果没有更简单的实现,我将使用显式引用。我希望在 python 中有一种方法可以像我的伪代码一样引用函数参数,但我认为只为一个函数挑战装饰器似乎有点过头了。
  • @DaniyalShahrokhian:你当然可以重用装饰器。此外,我认为您仍然必须明确您正在做的事情(不是通过引用变量本身,但应该清楚发生了什么)。
【解决方案2】:
def foo(a, b, c):
    return call_other(round(a),round(b),round(c))

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

我想如果我理解你想要做什么......(通过阅读 cmets)

【讨论】:

  • 我发布这个问题的主要原因是避免明确引用每个参数。我关心的是一种干净的方法,而不是多次提及函数round()和变量a, b, c
  • 显式优于隐式
【解决方案3】:

这是一个没有副作用的例子:

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

通过此实现,您可以输入任意数量的数组。

【讨论】:

  • 这个解决方案会破坏函数的可读性,因为参数不再有意义。我修改了我的问题以反映这些参数不仅仅是无意义的整数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-09
  • 1970-01-01
  • 2011-03-09
  • 2022-11-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多