Mat*_*ias 68 python function optional-parameters
Python中是否有一种简单的方法可以检查可选参数的值是否来自其默认值,还是因为用户已在函数调用中明确设置它?
eca*_*mur 49
并不是的.标准方法是使用用户不希望传递的默认值,object例如:
DEFAULT = object()
def foo(param=DEFAULT):
if param is DEFAULT:
...
Run Code Online (Sandbox Code Playgroud)
通常,您可以将其None用作默认值,如果它作为用户想要传递的值没有意义.
另一种方法是使用kwargs:
def foo(**kwargs):
if 'param' in kwargs:
param = kwargs['param']
else:
...
Run Code Online (Sandbox Code Playgroud)
然而,这过于冗长,使您的功能更难以使用,因为其文档不会自动包含param参数.
Ste*_*ano 26
很多答案都有完整信息的一小部分,所以我想把它们和我最喜欢的模式放在一起。
mutable类型如果默认值是一个可变对象,那么你很幸运:你可以利用 Python 的默认参数在函数定义时计算一次的事实(在最后一节的答案末尾有更多关于这个的信息)
这意味着您可以轻松地比较默认的可变值,is以查看它是作为参数传递还是默认保留,如下面的函数或方法示例所示:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Run Code Online (Sandbox Code Playgroud)
和
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Run Code Online (Sandbox Code Playgroud)
现在,如果你的默认值应该是一个immutable值(并且记住,即使是字符串也是不可变的!),它就不那么优雅了,因为你不能照原样利用这个技巧,但是你仍然可以做一些事情,仍然利用可变类型; 基本上,您在函数签名中放置了一个可变的“假”默认值,并在函数体中放置了所需的“真实”默认值。
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
# whatever I want to do with the value
print(value)
Run Code Online (Sandbox Code Playgroud)
如果你真正的默认值是None,那感觉特别有趣,但它None是不可变的,所以......你仍然需要明确地使用一个可变的作为函数默认参数,并在代码中切换到 None 。
Default类用于不可变的默认值或者,类似于@cz 的建议,如果 python 文档不够 :-) ,您可以在两者之间添加一个对象以使 API 更加明确(无需阅读文档);used_proxy_ 默认类实例是可变的,并将包含您要使用的真实默认值。
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
Run Code Online (Sandbox Code Playgroud)
现在:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
Run Code Online (Sandbox Code Playgroud)
以上也适用于Default(None).
显然,上面的模式看起来比它们应该的更丑,因为所有print这些都只是为了展示它们是如何工作的。否则,我发现它们足够简洁且可重复。
您可以编写一个装饰器以__call__更简化的方式添加@dmg 建议的模式,但这仍然必须在函数定义本身中使用奇怪的技巧 - 您需要拆分value,value_default如果您的代码需要区分它们,所以我没有看到太多的优势,我不会写这个例子:-)
关于#1 python 的更多信息!,为了你自己的快乐而滥用上面。您可以通过执行以下操作来查看由于定义时的评估而发生的情况:
def testme(default=[]):
print(id(default))
Run Code Online (Sandbox Code Playgroud)
您可以根据需要运行testme()多次,您将始终看到对相同默认实例的引用(因此基本上您的默认值是不可变的 :-) )。
请记住,在 Python 中只有 3 种可变的内置类型:set, list, dict; 其他一切 - 甚至字符串!- 是不可变的。
Ell*_*ioh 14
以下函数装饰器生成explicit_checker一组显式给出的所有参数的参数名称.它将结果作为额外参数(explicit_params)添加到函数中.只是'a' in explicit_params检查参数a是否明确给出.
def explicit_checker(f):
varnames = f.func_code.co_varnames
def wrapper(*a, **kw):
kw['explicit_params'] = set(list(varnames[:len(a)]) + kw.keys())
return f(*a, **kw)
return wrapper
@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
print a, b, c, explicit_params
if 'b' in explicit_params:
pass # Do whatever you want
my_function(1)
my_function(1, 0)
my_function(1, c=1)
Run Code Online (Sandbox Code Playgroud)
小智 5
我有时使用通用唯一字符串(如UUID).
import uuid
DEFAULT = uuid.uuid4()
def foo(arg=DEFAULT):
if arg is DEFAULT:
# it was not passed in
else:
# it was passed in
Run Code Online (Sandbox Code Playgroud)
这样,没有用户甚至可以猜测默认值,如果他们尝试了,所以我可以非常自信,当我看到该值时arg,它没有传入.
我已经多次看到这种模式(例如 library unittest, py-flags, jinja):
class Default:
def __repr__( self ):
return "DEFAULT"
DEFAULT = Default()
Run Code Online (Sandbox Code Playgroud)
...或等效的单线...:
DEFAULT = type( 'Default', (), { '__repr__': lambda x: 'DEFAULT' } )()
Run Code Online (Sandbox Code Playgroud)
与 不同的是DEFAULT = object(),这有助于类型检查并在发生错误时提供信息——通常在错误消息中使用字符串表示 ( "DEFAULT") 或类名 ( "Default")。
小智 5
@Ellioh 的答案在 python 2 中有效。在 python 3 中,以下代码应该有效:
import inspect
from functools import wraps
def explicit_checker(f):
varnames = inspect.getfullargspec(f)[0]
@wraps(f)
def wrapper(*a, **kw):
kw['explicit_params'] = set(list(varnames[:len(a)]) + list(kw.keys()))
return f(*a, **kw)
return wrapper
@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
print a, b, c, explicit_params
if 'b' in explicit_params:
pass # Do whatever you want
Run Code Online (Sandbox Code Playgroud)
这种方法可以保持参数名称和默认值(而不是 **kwargs)具有更好的可读性。
| 归档时间: |
|
| 查看次数: |
50988 次 |
| 最近记录: |