Синтаксический сахар Python: псевдонимы функций
Есть ли синтаксис для псевдонимов функций аргументов? Если нет, есть ли предложения PEP? Я не теоретик языков программирования, поэтому мое мнение, вероятно, неосведомлено, но я думаю, что реализация некоторого вида псевдонимов аргументов может быть полезной.
Я делаю некоторые изменения в 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')
2 ответа
Никаких ошибок, вы можете сделать это с помощью декораторов (как показано выше) или из первых рук с помощью 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.
Я не имею в виду, что ваш пример не является синтаксически ясным, просто ненужным.
Нет, такого синтаксического сахара нет.
Вы могли бы использовать **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.