Это нормально для небольших приложений и якобы «одноразовых» сценариев, особенно с улучшением 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% охвату с помощью модульные тесты разумны, но на самом деле не с интеграционными тестами, если принять во внимание комбинаторный взрыв настроек [[для L10N и по многим другим причинам]] и поддерживаемых версий всех зависимости. Таким образом, вы просто не рискуете сломаться, потому что «они будут пойманы в QA» (если вы это сделаете, вы не сможете долго продержаться в среде, которая разрабатывает большие приложения или повторно используемые компоненты;-).
Таким образом, на практике вы никогда не удалите локальную переменную «имя», даже несмотря на то, что специалисты по пользовательскому опыту уже давно заменили это приветствие на более подходящее «Добро пожаловать, Dread Overlord!» (и соответственно их версии L10n'ed). Все потому, что ты пошел на locals()
...
Таким образом, вы накапливаете хлам из-за того, что ограничиваете свою способность поддерживать и редактировать свой код, и, возможно, эта локальная переменная «имя» существует только потому, что она была извлечена из БД или чего-то подобного, поэтому ее сохранение (или какой-то другой местный) вокруг не только хлам, но и снижает вашу производительность. Стоит ли внешнее удобство locals()
этого?-)
Но подождите, есть хуже! Среди множества полезных услуг, которые может сделать вам lint
-подобная программа (например, pylint), заключается в том, чтобы предупредить вас о неиспользуемых локальных переменных (хотелось бы, чтобы это можно было сделать и для неиспользуемых глобальных переменных, но для повторно используемых компонентов это слишком сложно ;-). Таким образом, вы сможете очень быстро и дешево отловить большинство случайных орфографических ошибок, таких как if ...: nmae = ...
, вместо того, чтобы наблюдать за сбоем в модульном тесте и проводить тщательную работу, чтобы выяснить, почему он сломался (вы делаете< /em> есть навязчивые, всепроникающие модульные тесты, которые обнаружат это в конце концов, верно?-) -- lint сообщит вам о неиспользуемой локальной переменной nmae
, и вы немедленно это исправите.
Но если в вашем коде есть blah.format(**locals())
или, что то же самое, blah % locals()
... вы SOL, приятель!-) Как бедняга lint узнает, действительно ли nmae
является неиспользуемой переменной, или она действительно используется кем-то еще? внешняя функция или метод, которому вы передаете locals()
? Он не может - либо он все равно будет предупреждать (вызывая эффект «кричащего волка», который в конечном итоге приводит к тому, что вы игнорируете или отключаете такие предупреждения), либо он никогда не будет предупреждать (с тем же конечным эффектом: никаких предупреждений ;-) .
Сравните это с альтернативой "явное лучше, чем неявное"...:
blah.format(name=name)
Там -- больше не нужно заботиться об обслуживании, производительности и не буду ли я мешать ворсинкам; блаженство! Вы немедленно даете понять всем заинтересованным сторонам (включая lint;-), что именно какие локальные переменные используются и для каких целей.
Я мог бы продолжать, но я думаю, что этот пост уже довольно длинный ;-).
Итак, резюмируя: "γνῶθι σεαυτόν!" Хм, в смысле, "познай себя!". И под «самим собой» я на самом деле подразумеваю «цель и объем вашего кода». Если это что-то вроде 1-го или около того, никогда не будет i18n'd и L10n'd, вряд ли будет нуждаться в будущем обслуживании, никогда не будет повторно использоваться в более широком контексте и т. д., и т. д., тогда используйте locals()
для его маленькое, но аккуратное удобство; если вы знаете иначе или даже если вы не совсем уверены, ошибитесь из-за осторожности и сделайте вещи более явными - потерпите небольшое неудобство, объясняя, что именно вы собираетесь, и наслаждайтесь всеми полученными преимуществами.
Кстати, это всего лишь один из примеров, когда Python стремится поддерживать как «небольшое, одноразовое, исследовательское, возможно, интерактивное» программирование (позволяя и поддерживая рискованные удобства, выходящие далеко за рамки locals()
— подумайте о import *
, eval
, exec
, и несколько других способов, которыми вы можете смешивать пространства имен и рисковать последствиями обслуживания ради удобства), а также «большие, многоразовые, корпоративные» приложения и компоненты. Он может неплохо справляться с обеими задачами, но только если вы «знаете себя» и избегаете использования «удобных» частей, за исключением тех случаев, когда вы абсолютно уверены, что действительно можете себе это позволить. Чаще всего ключевым соображением является «что это делает с моими пространствами имен и осведомленностью об их формировании и использовании компилятором, lint & c, людьми, читающими и сопровождающими, и так далее?».
Помните: «Пространства имен — это отличная идея — давайте сделаем больше таких!» таков вывод Zen of Python... но Python как «язык для взрослых по обоюдному согласию» позволяет вам определять границы того, что это подразумевает, как следствие вашей среды разработки, целей и практики. Используйте эту силу ответственно!-)
person
Alex Martelli
schedule
11.10.2009