【问题标题】:Is it possible to change a function's default parameters in Python?是否可以在 Python 中更改函数的默认参数?
【发布时间】:2013-07-13 00:05:17
【问题描述】:

在 Python 中,是否可以在运行时重新定义函数的默认参数?

我在这里定义了一个带有 3 个参数的函数:

def multiplyNumbers(x,y,z):
    return x*y*z

print(multiplyNumbers(x=2,y=3,z=3))

接下来,我尝试(不成功)为y设置默认参数值,然后我尝试调用不带参数y的函数:

multiplyNumbers.y = 2;
print(multiplyNumbers(x=3, z=3))

但是产生了以下错误,因为y的默认值没有正确设置:

TypeError: multiplyNumbers() missing 1 required positional argument: 'y'

是否可以像我在这里尝试的那样在运行时重新定义函数的默认参数?

【问题讨论】:

  • 在函数also上设置属性y不会改变y的默认参数。

标签: python function


【解决方案1】:

只需使用functools.partial

 multiplyNumbers = functools.partial(multiplyNumbers, y = 42)

这里有个问题:你不能把它称为multiplyNumbers(5, 7, 9);,你应该手动说y=7

如果您需要删除默认参数,我有两种方法:

  1. 在某处存储原始函数

    oldF = f
    f = functools.partial(f, y = 42)
    //work with changed f
    f = oldF //restore
    
  2. 使用partial.func

    f = f.func //go to previous version.
    

【讨论】:

  • @joshlf13,你也可能使用静态语言。
  • 你的意思是可以做类似代码 sn-p 的事情?我想有一个足够强大的类型系统是可能的,但我不知道很多这样的语言。我想很多函数式语言,但我的意思是命令式的(尽管我应该明确地说出来)。
  • 实际上,想想看,这基本上就像柯里化,只是不需要对变量进行排序。
  • @AndersonGreen,我明白了。我的意思是,我不知道如何直接做,但你可以按照oldF = f; f = partials(...); ... ; f = oldF
  • @AndersonGreen,添加了一些信息
【解决方案2】:

从技术上讲,可以按照您的要求去做……但这不是一个好主意。 RiaD 的答案是 Pythonic 的方式来做到这一点。

在 Python 3 中:

>>> def f(x=1, y=2, z=3):
...     print(x, y, z)
>>> f()
1 2 3
>>> f.__defaults__ = (4, 5, 6)
4 5 6

与隐藏在文档中且难以在文档中找到的所有其他内容一样,inspect 模块图表是查找函数属性的最佳位置。

细节在 Python 2 中略有不同,但思路是一样的。 (只需将文档页面左上角的下拉菜单从 3.3 更改为 2.7。)


如果您想知道 Python 如何知道当它只有一个元组时哪些默认值与哪些参数匹配……它只是从末尾倒数(或 **args**kwargs 中的第一个——之后的任何内容而是进入__kwdefaults__ dict)。 f.__defaults = (4, 5) 将默认设置为yz45,默认设置为x。这是因为在默认参数之后不能有非默认参数。


在某些情况下这不起作用,但即使那样,您也可以将其不变地复制到具有不同默认值的新函数中:

>>> f2 = types.FunctionType(f.__code__, f.__globals__, f.__name__,
...                         (4, 5, 6), f.__closure__)

这里,types module 文档并没有真正解释任何内容,但交互式解释器中的 help(types.FunctionType) 显示了您需要的参数。


不能处理的唯一情况是内置函数。但是它们通常没有实际的默认值。相反,他们在 C API 中伪造了类似的东西。

【讨论】:

  • 您提到在运行时更改函数的默认参数“不是一个好主意”。您是否注意到这种方法的任何缺点? (我还不知道)。
  • @AndersonGreen:嗯,有一个明显的缺点是它让你、其他人、linter、IDE、基于反射的工具(如pickle 等)更难理解你的代码。而且您必须完全了解有关 args/params 的规则,以确保您做对了。然后是一个事实,如果不编写可以处理常规函数、奇怪的不可变函数(需要从代码中构造一个新函数)和内置函数(你需要包装,使用partial 或调用它的新函数)。
  • 我们可以在lambda 函数内部做类似f.__defaults__ = (4, 5, 6) 的事情吗? @abarnert
  • @DiptangsuGoswami lambda 不允许赋值,尽管 3.8 中的赋值表达式在技术上可以实现。
  • 很有趣,但是你能在函数定义中修改默认值吗?我问这个的原因是因为我有两个版本的库,一个是用 foo = true 构建的,另一个是用 foo = false 构建的。所以,对于我的函数 f(a, b=1) return a+b;如果 foo = true,我想要 b = 2 的默认值;如果 foo = false,它应该默认为 1。
【解决方案3】:

是的,你可以通过修改函数的func.__defaults__元组来实现

该属性是函数每个参数的默认值的元组。

例如,要使pandas.read_csv 始终使用sep='\t',您可以这样做:

import inspect

import pandas as pd

default_args = inspect.getfullargspec(pd.read_csv).args
default_arg_values = list(pd.read_csv.__defaults__)
default_arg_values[default_args.index("sep")] = '\t'
pd.read_csv.__defaults__ = tuple(default_arg_values)

【讨论】:

    【解决方案4】:

    使用func_defaults

    def myfun(a=3):
        return a
    
    myfun.func_defaults = (4,)
    b = myfun()
    assert b == 4
    

    查看 func_defaults here 的文档

    更新: 看着 RiaD 的回复,我觉得我的字面意思。我不知道您提出这个问题的背景,但总的来说(并遵循Zen of Python)我相信使用部分应用程序是比重新定义函数的默认参数更好的选择

    【讨论】:

    • 我没有设法将其设置为 3 个变量中的第二个。只对我有用。
    • @RiaD:函数默认值必须是最终的参数序列;就像你不能写def foo(x, y=2, z):一样,你也不能把func_defaults/__defaults__改成只覆盖y
    猜你喜欢
    • 2014-07-30
    • 1970-01-01
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 2021-05-04
    • 2011-08-16
    相关资源
    最近更新 更多