Это нормально для небольших приложений и якобы «одноразовых» сценариев, особенно с расширением vars
, упомянутым в @ kaizer.se, и версией .format
, упомянутой в @RedGlyph.
Однако для больших приложений с большим сроком обслуживания и многих сопровождающих эта практика может привести к головным болям при обслуживании, и я думаю, что именно отсюда приходит ответ @ S.Lott. Позвольте мне объяснить некоторые из связанных с этим проблем, поскольку они могут быть неочевидны для тех, у кого нет шрамов от разработки и поддержки больших приложений (или компонентов многократного использования для таких животных).
В «серьезном» приложении у вас не было бы жестко запрограммированной строки формата - или, если бы вы имели, она была бы в некоторой форме, такой как _('Hello {name}.')
, где _
взято из gettext или аналогичные платформы i18n / L10n. Дело в том, что такое приложение (или модули многократного использования, которые могут использоваться в таких приложениях) должно поддерживать интернационализацию (AKA i18n) и локализацию (AKA L10n): вы хотите, чтобы ваше приложение могло излучать «Hello Paul» в определенных ситуациях. страны и культуры, "Hola Paul" в некоторых других, "Ciao Paul" в других, и так далее. Таким образом, строка формата более или менее автоматически заменяется другой во время выполнения, в зависимости от текущих настроек локализации; вместо того, чтобы быть жестко закодированным, это живет в некоторой базе данных. Представьте, что строка формата всегда является переменной, а не строковым литералом.
Итак, то, что у вас есть, по сути
formatstring.format(**locals())
и вы не можете тривиально точно проверить какие локальные имена будут использовать форматирование. Вам придется открыть и просмотреть базу данных L10N, определить строки формата, которые будут использоваться здесь в различных настройках, проверить их все.
Таким образом, на практике вы не знаете , какие локальные имена будут использоваться - что ужасно ограничивает поддержание функции. Вы не осмеливаетесь переименовывать или удалять любые локальные переменные, так как это может ужасно нарушить пользовательский интерфейс для пользователей с некоторыми (для вас) неясными сочетаниями языка, локали и предпочтений
Если у вас есть превосходное интеграционное / регрессионное тестирование, поломка будет обнаружена до бета-релиза - но QA будет кричать на вас, и релиз будет отложен ... и, давайте будем честными, стремясь к 100% охвату с тестами unit вполне разумно, в действительности это не так с тестами интеграция , если учесть комбинаторный взрыв настроек [[для L10N и по многим другим причинам]] и поддерживаемых версий все зависимости. Таким образом, вы просто беспечно не рискуете поломками, потому что «они будут пойманы в QA» (если вы это сделаете, вы не сможете долго жить в среде, которая разрабатывает большие приложения или повторно используемые компоненты; -).
Таким образом, на практике вы никогда не удалите локальную переменную «name», даже если пользователи User Experience уже давно переключили это приветствие на более подходящее «Welcome, Dread Overlord!» (и соответственно их версии L10n). Все потому, что ты пошел на locals()
...
Таким образом, вы накапливаете ненужные данные из-за того, как вы ограничивали свою способность поддерживать и редактировать свой код - и, возможно, эта локальная переменная «name» существует только потому, что она была выбрана из БД или тому подобного, поэтому это (или какое-то другое местное) окружение не просто бесполезно, оно также снижает вашу производительность. Удобство поверхности locals()
стоит , что ? -)
Но подождите, там хуже! Среди многих полезных сервисов программа, подобная lint
(как, например, pylint ), может сделать для вас предупреждение о неиспользуемых локальных переменных (желательно, чтобы это делалось и для неиспользуемых глобальных переменных). , но для многоразовых компонентов это слишком сложно ;-). Таким образом, вы будете ловить большинство случайных орфографических ошибок, таких как if ...: nmae = ...
, очень быстро и дешево, вместо того, чтобы видеть перерыв в модульном тесте и выполнять работу, чтобы выяснить, почему она сломалась (вы делаете имеет навязчивые, всепроникающие юнит-тесты, которые будут поймать это в конце концов, верно? -) - lint расскажет вам о неиспользованной локальной переменной nmae
, и вы немедленно исправите ее.
Но если в вашем коде есть blah.format(**locals())
или, что то же самое, blah % locals()
... вы SOL, приятель! -) Как бедный линт узнает, действительно ли nmae
неиспользованная переменная или на самом деле он используется любой внешней функцией или методом, которому вы передаете locals()
? Он не может - либо он все равно будет предупреждать (вызывая эффект «крик волка», который в конечном итоге приведет к игнорированию или отключению таких предупреждений), либо он никогда не будет предупреждать (с тем же конечным эффектом: без предупреждений ;-) .
Сравните это с альтернативой "явный лучше, чем неявный" ...:
blah.format(name=name)
Там - больше не применимо ни обслуживание, ни эксплуатационные характеристики, ни беспокойство по поводу того, что я делаю; блаженство! Вы немедленно даете понять всем, кого это касается (включая lint ;-), точно , какие локальные переменные используются, и точно для каких целей.
Я мог бы продолжить, но я думаю, что этот пост уже довольно длинный; -).
Итак, подведем итог: " γνῶθι σεαυτόν !" Хм, я имею в виду «познай себя!». И под «самим собой» я имею в виду «цель и область действия вашего кода». Если это что-то вроде 1 или около того, никогда не будет i18n'd и L10n'd, вряд ли будет нуждаться в будущем обслуживании, никогда не будет повторно использовано в более широком контексте и т. Д., И т. Д., Тогда продолжайте и используйте locals()
за его маленькое, но аккуратное удобство; если вы знаете иначе, или даже если вы не совсем уверены, сделайте ошибку осторожно и сделайте вещи более явными - пострадайте от небольшого неудобства из-за точного объяснения того, что вы делаете, и наслаждайтесь всеми вытекающими отсюда преимуществами.
Кстати, это только один из примеров, когда Python стремится поддерживать как «маленькое, одноразовое, исследовательское, возможно, интерактивное» программирование (предоставляя и поддерживая рискованные удобства, которые выходят далеко за пределы locals()
- подумайте о import *
, eval
, exec
и ряд других способов, которыми вы можете создавать удобные пространства имен и риски обслуживания для удобства), а также "большие, многократно используемые, корпоративные" приложения и компоненты. Он может справиться с обеими задачами довольно хорошо, но только если вы «знаете себя» и избегаете использования «удобных» частей, за исключением случаев, когда вы абсолютно уверены, что на самом деле можете себе их позволить. Чаще всего ключевым соображением является «что это делает с моими пространствами имен и осведомленностью об их формировании и использовании компилятором, lint & c, читателями и сопровождающими людьми и т. Д.?».
Помните: «Пространства имен - это одна гудящая отличная идея - давайте сделаем больше таких!» так завершает Zen of Python ... но Python, как «язык для взрослых по согласию», позволяет you определять границы того, что это подразумевает, как следствие среды разработки, целей и практик , Используйте эту силу ответственно! -)