sds*_*sds 29 python python-3.x
我有一个函数foo
调用math.isclose
:
import math
def foo(..., rtol=None, atol=None):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
...
...
Run Code Online (Sandbox Code Playgroud)
上述失败的math.isclose
,如果我不通过rtol
,并atol
于foo
:
TypeError: must be real number, not NoneType
Run Code Online (Sandbox Code Playgroud)
我不想将系统默认参数值放入我的代码中(如果以后改变的话会怎样?)
到目前为止,这是我想到的:
import math
def foo(..., rtol=None, atol=None):
...
tols = {}
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
if math.isclose(x, y, **tols):
...
...
Run Code Online (Sandbox Code Playgroud)
这看起来很长,很愚蠢,并且dict
在每次调用时都
分配一个foo
(递归调用自身,所以这很重要)。
那么,告诉math.isclose
使用默认公差的最佳方法是什么?
PS。有几个相关的问题。请注意,我并不想知道实际的默认参数math.isclose
-所有我希望它告诉它使用任何他们都是默认的。
gmd*_*mds 11
一种解决方法是可变参数展开:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
Run Code Online (Sandbox Code Playgroud)
这将允许你指定atol
并rtol
作为关键字参数的主要功能foo
,它会再传给不变math.isclose
。
但是,我还要说的是,传递参数以kwargs
某种方式修改函数的行为,而不仅仅是传递给被调用的子函数,这是很习惯的做法。因此,我建议改用一个参数命名,这样很明显它将被解压缩并不变地传递给子函数:
def foo(..., isclose_kwargs={}):
...
if math.isclose(x, y, **isclose_kwargs):
...
Run Code Online (Sandbox Code Playgroud)
您可以在中看到一个等效的模式matplotlib
(例如:plt.subplots
- subplot_kw
和gridspec_kw
,所有其他关键字参数都作为传递给Figure
构造函数**fig_kw
)和seaborn
(示例:FacetGrid
- subplot_kws
,gridspec_kws
)。
当有这特别明显多发,你可能想通过关键字参数,但在其他方面保留默认行为子功能:
def foo(..., f1_kwargs={}, f2_kwargs={}, f3_kwargs={}):
...
f1(**f1_kwargs)
...
f2(**f2_kwargs)
...
f3(**f3_kwargs)
...
Run Code Online (Sandbox Code Playgroud)
警告:
请注意,默认参数仅实例化一次,因此您不应dicts
在函数中修改空值。如果需要,您应该改为使用None
默认参数,并在dict
每次运行该函数时实例化一个新的空值:
def foo(..., isclose_kwargs=None):
if isclose_kwargs is None:
isclose_kwargs = {}
...
if math.isclose(x, y, **isclose_kwargs):
...
Run Code Online (Sandbox Code Playgroud)
我的偏好是避免这种情况,因为它比较简短,因此您不知道自己在做什么,而且一般来说我不喜欢重新绑定变量。但是,这绝对是一个有效的习惯用法,它可以更安全。
Mar*_*ers 11
正确的解决方案是使用与相同的默认设置math.isclose()
。无需对它们进行硬编码,因为您可以使用函数获得当前的默认值:inspect.signature()
import inspect
import math
_isclose_params = inspect.signature(math.isclose).parameters
def foo(..., rtol=_isclose_params['rel_tol'].default, atol=_isclose_params['abs_tol'].default):
# ...
Run Code Online (Sandbox Code Playgroud)
快速演示:
>>> import inspect
>>> import math
>>> params = inspect.signature(math.isclose).parameters
>>> params['rel_tol'].default
1e-09
>>> params['abs_tol'].default
0.0
Run Code Online (Sandbox Code Playgroud)
之所以有效,是因为math.isclose()
使用Argument Clinic工具定义了其参数:
Argument Clinic的最初动机是为CPython内置函数提供内省的“签名”。过去,如果您传入内置函数,自省查询功能将引发异常。有了Argument Clinic,这已成为过去!
在幕后,math.isclose()
签名实际上存储为字符串:
>>> math.isclose.__text_signature__
'($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)'
Run Code Online (Sandbox Code Playgroud)
inspect
签名支持对此进行了解析,以为您提供实际值。
不是所有的C-定义的函数使用参数诊所然而,基本代码被转换上的情况下,逐案。math.isclose()
已转换为Python 3.7.0。
您可以使用__doc__
字符串作为后备,因为在早期版本中,它也包含签名:
>>> import math
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> math.isclose.__doc__.splitlines()[0]
'isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool'
Run Code Online (Sandbox Code Playgroud)
因此,通用的后备广告可能是:
import inspect
def func_defaults(f):
try:
params = inspect.signature(f).parameters
except ValueError:
# parse out the signature from the docstring
doc = f.__doc__
first = doc and doc.splitlines()[0]
if first is None or f.__name__ not in first or '(' not in first:
return {}
sig = inspect._signature_fromstr(inspect.Signature, math.isclose, first)
params = sig.parameters
return {
name: p.default for name, p in params.items()
if p.default is not inspect.Parameter.empty
}
Run Code Online (Sandbox Code Playgroud)
我将其视为仅支持较旧的Python 3.x版本所需的权宜之计。该函数生成一个以参数名称为关键字的字典:
>>> import sys
>>> import math
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> func_defaults(math.isclose)
{'rel_tol': 1e-09, 'abs_tol': 0.0}
Run Code Online (Sandbox Code Playgroud)
注意复制Python默认值的风险很小 ; 除非有错误,否则值不容易更改。因此,另一种选择是将3.5 / 3.6的默认默认值硬编码为后备,并使用3.7及更高版本中提供的签名:
try:
# Get defaults through introspection in newer releases
_isclose_params = inspect.signature(math.isclose).parameters
_isclose_rel_tol = _isclose_params['rel_tol'].default
_isclose_abs_tol = _isclose_params['abs_tol'].default
except ValueError:
# Python 3.5 / 3.6 known defaults
_isclose_rel_tol = 1e-09
_isclose_abs_tol = 0.0
Run Code Online (Sandbox Code Playgroud)
但是请注意,不支持将来的附加参数和默认值的风险更大。至少该方法使您可以添加有关代码期望的参数数量的断言。inspect.signature()
使函数使用其默认参数的方法确实很少,您只有两种选择:
由于这些选项都不是很好的选择,因此我将列出一个详尽的清单,以便您可以将它们全部进行比较。
**kwargs
通过论证使用定义您的方法,**kwargs
并将其传递给math.isclose
:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
Run Code Online (Sandbox Code Playgroud)
缺点:
foo(1, 2, rtol=3)
不起作用)**kwargs
字典def foo(..., rtol=None, atol=None):
...
kwargs = {}
if rtol is not None:
kwargs["rel_tol"] = rtol
if atol is not None:
kwargs["abs_tol"] = atol
if math.isclose(x, y, **kwargs):
Run Code Online (Sandbox Code Playgroud)
缺点:
def foo(..., rtol=1e-09, atol=0.0):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
Run Code Online (Sandbox Code Playgroud)
缺点:
您可以使用该inspect
模块在运行时确定默认值:
import inspect, math
signature = inspect.signature(math.isclose)
DEFAULT_RTOL = signature.parameters['rel_tol'].default
DEFAULT_ATOL = signature.parameters['abs_tol'].default
def foo(..., rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
Run Code Online (Sandbox Code Playgroud)
缺点:
inspect.signature
可能在内置函数或C中定义的其他函数上失败 归档时间: |
|
查看次数: |
1789 次 |
最近记录: |