Я пытаюсь провести рефакторинг проекта, нацеленного на Python 3.6 и pytest. Набор тестов содержит много отладочных операторов, таких как:
print('This is how something looks right now', random_thing.foo.bar.start,
random_thing.foo.bar.middle, random_thing.foo.bar.end)
Идея этих утверждений состоит в том, что если тест начнет давать сбой в будущем, у нас будет некоторый контекст, который поможет нам отследить, что может вызвать проблема. быть. В этом тесте нет необходимости проверять фактические значения прямо сейчас, но как только что-то пойдет не так, наличие этой информации важно для дальнейшей отладки.
Я бы хотел не повторять random_thing.foo.bar.
столько раз. Я мог бы присвоить это временной переменной, но на самом деле код не нуждается в этой переменной, доступной когда-либо после. Я не очень беспокоюсь о производительности, но у меня есть сильное предпочтение держать код "чистым" - и "утечка" этих имен переменных меня теряет. Там есть такая функция на других языках, с которыми я знаком, поэтому мне интересно, как это сделать на Python.
Я свободно владею C ++, где Возможно, я бы просто поместил эту отладочную печать в дополнительную область:
{
const auto& bar = random_thing.foo.bar;
debug << "start: " << bar.start << ", middle: " << bar.middle << ", end: " << bar.end;
}
Учитывая, что в Python нет анонимных блоков, есть ли способ "Pythoni c" избежать этого беспорядка пространства имен? Я на самом деле не ищу мнения или конкурс популярности, но для обзора, основанного на том, как люди, которые делают Python дольше меня, воспринимают эти подходы, поэтому вот несколько вещей, которые я попробовал:
1. Просто добавьте эту чертову переменную и del
потом
Ну, я не люблю многократно делать вещи, которые машина должна делать для меня.
В Python нет новой области видимости с оператором with
, поэтому это оставляет переменную opj
доступной через locals:
>>> import os
>>> import os.path
>>> import contextlib
>>> with contextlib.nullcontext(os.path.join) as opj:
... print(type(opj))
...
<class 'function'>
>>> print(type(opj))
<class 'function'>
3. with
заявление и декоратор заявления Владимира Яковлева let
from contextlib import contextmanager
from inspect import currentframe, getouterframes
@contextmanager
def let(**bindings):
frame = getouterframes(currentframe(), 2)[-1][0] # 2 because first frame in `contextmanager` is the decorator
locals_ = frame.f_locals
original = {var: locals_.get(var) for var in bindings.keys()}
locals_.update(bindings)
yield
locals_.update(original)
Код выглядит потрясающе для меня:
>>> a = 3
>>> b = 4
>>> with let(a=33, b=44):
... print(a, b)
...
(33, 44)
>>> print(a, b)
(3, 4)
Это не undef
a переменная, которая не была определена ранее, но ее легко добавить. Является ли манипулирование стеком таким образом разумной идеей? Мой Python -фу ограничен, поэтому я разрываюсь между тем, чтобы видеть это как супер-крутой и супер-хаки sh. Является ли окончательный результат «разумно Pythoni c»?
4. Обертка вокруг print
с **kwargs
Давайте использовать **kwargs
:
def print_me(format, **kwargs):
print(format.format(**kwargs))
print_me('This is it: {bar.start} {bar.middle} {bar.end}', bar=random_thing.foo.bar)
Это достаточно хорошо, но f-строки могут содержать реальные выражения , например:
foo = 10
print(f'{foo + 1}')
Я хотел бы сохранить эту функциональность. Я понимаю, что str.format
не может на самом деле поддерживать это из-за проблем безопасности передачи пользовательских входных данных.