Python 语法糖:函数 arg 别名
Python syntactic sugar: function arg aliases
是否有别名函数参数的语法?如果没有,是否有任何 PEP 提案?我不是编程语言理论家,所以我的观点可能是无知的,但我认为实现某种函数 arg 别名可能很有用。
我正在对 libcloud 进行一些更改,我的想法可以帮助我在更改 API 时避免破坏他人。
例如,假设我正在重构并想将函数 arg 'foo' 重命名为 'bar':
原文:
def fn(foo):
<code (using 'foo')>
我可以:
def fn(foo, bar=None):
if foo and bar:
raise Exception('Please use foo and bar mutually exclusively.')
bar = foo or bar
<code (using 'bar')>
# But this is undesirable because it changes the method signature to allow
# a new parameter slot.
fn('hello world', 'goodbye world')
我未提炼的语法糖想法:
def fn(bar|foo|baz):
# Callers can use foo, bar, or baz, but only the leftmost arg name
# is used in the method code block. In this case, it would be bar.
# The python runtime would enforce mutual exclusion between foo,
# bar, and baz.
<code (using 'bar')>
# Valid uses:
fn(foo='hello world')
fn(bar='hello world')
fn(baz='hello world')
fn('hello world')
# Invalid uses (would raise some exception):
fn(foo='hello world', bar='goodbye world')
fn('hello world', baz='goodbye world')
不,没有这样的语法糖。
您可以使用 **kwargs
来捕获额外的关键字参数并在其中查找已弃用的名称(如果不存在则引发异常)。您甚至可以使用装饰器将其自动化。
from functools import wraps
def renamed_argument(old_name, new_name):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if old_name in kwargs:
if new_name in kwargs:
raise ValueError(
"Can't use both the old name {} and new name {}. "
"The new name is preferred.".format(old_name, new_name))
kwargs[new_name] = kwargs.pop(old_name)
return func(*args, **kwargs)
return wrapper
return decorator
@renamed_argument('bar', 'foo')
def fn(foo=None):
<method code>
演示:
>>> @renamed_argument('bar', 'foo')
... def fn(foo=None):
... return foo
...
>>> fn() # default None returned
>>> fn(foo='spam')
'spam'
>>> fn(bar='spam')
'spam'
>>> fn(foo='eggs', bar='spam')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in wrapper
ValueError: Can't use both the old name bar and new name foo. The new name is preferred.
也许没有,您可以使用装饰器(如上所示)或直接使用 kwargs 来做到这一点:
def fn (**kw):
arg = kw.get("foo") or kw.get("bar") or kw.get("baz")
if arg==None: raise TypeError, "foo nor bar nor baz given az argument"
print arg
Here the order of precedence is: "if foo exists, arg is foo. if it doesn't but bar exists, arg is bar, if neither foo nor bar exists, the arg is baz. If baz doesn't, i.e. all 3 are missing, arg is None.
Of course, you may check whether either one exists and force the mutual exclusion, but I don't see why would you need such a thing.
You are clever and you will never pass them in together. Even if you do, some will be ignored.
您也可以使用可调用对象来实现,如果需要,甚至可以在运行时将您的语法和行为引入解释器。
但在我看来,将此作为有效的 Python 语法引入并不 Pythonic 并且永远不会发生,即使有 PEP。
如您所见,您可以做您想做的事而不会弄脏 Python 语法。
我并不是说你的例子在语法上不清晰,只是没有必要。
是否有别名函数参数的语法?如果没有,是否有任何 PEP 提案?我不是编程语言理论家,所以我的观点可能是无知的,但我认为实现某种函数 arg 别名可能很有用。
我正在对 libcloud 进行一些更改,我的想法可以帮助我在更改 API 时避免破坏他人。
例如,假设我正在重构并想将函数 arg 'foo' 重命名为 'bar':
原文:
def fn(foo):
<code (using 'foo')>
我可以:
def fn(foo, bar=None):
if foo and bar:
raise Exception('Please use foo and bar mutually exclusively.')
bar = foo or bar
<code (using 'bar')>
# But this is undesirable because it changes the method signature to allow
# a new parameter slot.
fn('hello world', 'goodbye world')
我未提炼的语法糖想法:
def fn(bar|foo|baz):
# Callers can use foo, bar, or baz, but only the leftmost arg name
# is used in the method code block. In this case, it would be bar.
# The python runtime would enforce mutual exclusion between foo,
# bar, and baz.
<code (using 'bar')>
# Valid uses:
fn(foo='hello world')
fn(bar='hello world')
fn(baz='hello world')
fn('hello world')
# Invalid uses (would raise some exception):
fn(foo='hello world', bar='goodbye world')
fn('hello world', baz='goodbye world')
不,没有这样的语法糖。
您可以使用 **kwargs
来捕获额外的关键字参数并在其中查找已弃用的名称(如果不存在则引发异常)。您甚至可以使用装饰器将其自动化。
from functools import wraps
def renamed_argument(old_name, new_name):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if old_name in kwargs:
if new_name in kwargs:
raise ValueError(
"Can't use both the old name {} and new name {}. "
"The new name is preferred.".format(old_name, new_name))
kwargs[new_name] = kwargs.pop(old_name)
return func(*args, **kwargs)
return wrapper
return decorator
@renamed_argument('bar', 'foo')
def fn(foo=None):
<method code>
演示:
>>> @renamed_argument('bar', 'foo')
... def fn(foo=None):
... return foo
...
>>> fn() # default None returned
>>> fn(foo='spam')
'spam'
>>> fn(bar='spam')
'spam'
>>> fn(foo='eggs', bar='spam')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in wrapper
ValueError: Can't use both the old name bar and new name foo. The new name is preferred.
也许没有,您可以使用装饰器(如上所示)或直接使用 kwargs 来做到这一点:
def fn (**kw):
arg = kw.get("foo") or kw.get("bar") or kw.get("baz")
if arg==None: raise TypeError, "foo nor bar nor baz given az argument"
print arg
Here the order of precedence is: "if foo exists, arg is foo. if it doesn't but bar exists, arg is bar, if neither foo nor bar exists, the arg is baz. If baz doesn't, i.e. all 3 are missing, arg is None.
Of course, you may check whether either one exists and force the mutual exclusion, but I don't see why would you need such a thing.
You are clever and you will never pass them in together. Even if you do, some will be ignored.
您也可以使用可调用对象来实现,如果需要,甚至可以在运行时将您的语法和行为引入解释器。
但在我看来,将此作为有效的 Python 语法引入并不 Pythonic 并且永远不会发生,即使有 PEP。
如您所见,您可以做您想做的事而不会弄脏 Python 语法。
我并不是说你的例子在语法上不清晰,只是没有必要。