Предположим, что функция query
определена так:
def query(how='Here or there'):
print 'Would you like them\n%s?' % how
Затем, когда (неверное) выражение
query(how='With a mouse', who='Daniel', where='Anywhere')
Оценивается, Python автоматически выдает ошибку
Traceback (most recent call last):
File "/tmp/seuss.py", line 4, in <module>
query(how='With a mouse', who='Daniel', where='Anywhere')
TypeError: query() got an unexpected keyword argument 'who'
Теперь предположим, что мне нужно определить функцию reply
, которая принимает ноль или более позиционных аргументов, а также необязательный именованный аргумент prefix
. Итак, все приведенные ниже выражения должны быть действительными, по крайней мере, синтаксически:
reply('in a box', 'with a fox')
reply('In a house', 'With a mouse', preamble='I would not like them\n')
reply(preamble='I do not like them, Sam-i-am.')
Единственный способ, с помощью которого я знаю, реализовать такую функцию, это подписать (*args, **kwargs)
; к сожалению, с этой сигнатурой недопустимые выражения, такие как reply('green eggs', wot='and', wotelse='ham')
, хороши для Python, а это значит, что мы должны выполнить эту проверку ошибок самостоятельно.
1
# check.py
def unexpected_kwargs(kwargs):
unexpected = kwargs.keys()
if len(unexpected) > 0:
import inspect
caller = inspect.getframeinfo(inspect.currentframe(1)).function
raise TypeError("%s() got an unexpected keyword argument '%s'" %
(caller, unexpected[0]))
РЕДАКТИРОВАТЬ: Ради этого вопроса, пожалуйста, возьмите такой факторинг кода обработки ошибок, как данное .
Теперь мы можем реализовать reply(*args, **kwargs)
следующим образом:
import check
def reply(*args, **kwargs):
preamble = kwargs.pop('preamble', 'Not ')
check.unexpected_kwargs(kwargs)
# rest of function definition
... и оценка reply('green eggs', wot='and', wotelse='ham')
результатов в
Traceback (most recent call last):
File "/tmp/seuss.py", line 14, in <module>
reply('green eggs', wot='and', wotelse='ham')
File "/tmp/seuss.py", line 9, in reply
check.unexpected_kwargs(kwargs)
File "/tmp/check.py", line 8, in unexpected_kwargs
(frameinfo.function, unexpected[0]))
TypeError: reply() got an unexpected keyword argument 'wot'
Это похоже на вывод ошибок, показанный ранее, но обратите внимание, что в этом выводе строка с ошибками (query(how='With a mouse', who='Daniel', where='Anywhere')
) была напечатана в самом конце трассировки, тогда как во втором выводе сообщение об ошибке выводится несколькими строками выше. Остальная часть обратной трассировки, после ошибочной строки, представляет собой ненужный шум IMO, который служит только для того, чтобы скрыть ошибку, ответственную за сбой.
Итак, чтобы действительно повторить обработку Python неожиданного ключевого слова, вывод ошибки должен выглядеть следующим образом:
Traceback (most recent call last):
File "/tmp/seuss.py", line 14, in <module>
reply('green eggs', wot='and', wotelse='ham')
TypeError: reply() got an unexpected keyword argument 'wot'
Конечно, один из неверных способов добиться этого будет выглядеть так:
def unexpected_kwargs_NO_GOOD(kwargs):
unexpected = kwargs.keys()
if len(unexpected) > 0:
import inspect
import traceback
import sys
print >> sys.stderr, ('Traceback (most recent call last):\n%s' %
''.join(traceback.format_stack()[:-2])),
frameinfo = inspect.getframeinfo(inspect.currentframe(1))
print >> sys.stderr, ('TypeError: '
"%s() got an unexpected keyword argument '%s'" %
(frameinfo.function, unexpected[0]))
sys.exit(1)
Это приводит к правильному выводу, но не вызывает исключения, которое может перехватить исходный код. Вместо этого он безоговорочно завершает выполнение программы.
Есть ли способ получить желаемый результат и в то же время вызвать правильное исключение?
1 Можно выделить больше обработки аргументов, чем просто обработку ошибок, но, поскольку эта дополнительная функциональность является касательной к вопросу поста, я исключил ее из приведенного выше примера кода.
3 ответа
Вы не можете вызвать исключение в той же точке, в которой Python его вызывает, нет. Просто создайте исключение, когда есть больше ключевых аргументов, чем вы можете обработать.
Это должно быть в порядке; вся информация для отладки неправильного вызова функции все еще присутствует в трассировке.
Если вы уже точно знаете, какие ключевые аргументы вы хотите принять, тогда вы вообще не хотите использовать **kwargs
. Вы просто хотите использовать старые аргументы с ключевыми словами.
Похоже, вы ищете функцию, которая была добавлена в Python 3 (см. PEP 3102) для поддержки аргументов "только для ключевых слов": аргументы, которые никогда не будут автоматически заполнены позиционным аргументом.
>>> def reply(*args, preamble='Not '):
... print(args)
... print(preamble)
...
>>> reply('arg1', 'arg2')
('arg1', 'arg2')
Not
>>> reply('arg1', 'arg2', preamble='yeah')
('arg1', 'arg2')
yeah
>>> reply('arg1', 'arg2', preamble='yeah', wot='wat')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-b16c25a18626> in <module>()
----> 1 reply('arg1', 'arg2', preamble='yeah', wot='wat')
TypeError: reply() got an unexpected keyword argument 'wot
То, что вы хотите, невозможно в Python 2. Обычный шаблон в коде Python 2 просто использует **kwargs
и четко документирует принятые параметры в строке документации . Обычно мы просто игнорируем любые ложные аргументы, они не вызывают ошибку.
Я не одобряю всю эту проверку прототипов и магию аргументов, потому что она нарушает принцип KISS, однако я думаю, что есть способ реализовать (почти) точное поведение, которое вы хотите:
def reply(*args, **kwargs):
try:
my_arg_check(args, kwargs, *other_parameters)
except MyArgCheckFailure as exc:
raise TypeError(exc.my_custom_message)
Похожие вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.