Python: использует "..% (var) s.." % locals() хорошая практика?

Я обнаружил этот шаблон (или анти-шаблон), и я очень доволен им.

Я чувствую, что он очень проворный:

def example():
    age = ...
    name = ...
    print "hello %(name)s you are %(age)s years old" % locals()

Иногда я использую своего кузена:

def example2(obj):
    print "The file at %(path)s has %(length)s bytes" % obj.__dict__

Мне не нужно создавать искусственные кортежи и подсчитывать параметры и сохранять совпадающие позиции% s внутри кортежа.

Вам нравится? Do/Используете ли вы его? Да/Нет, пожалуйста, объясните.

Ответ 1

Это нормально для небольших приложений и предположительно "одноразовых" скриптов, особенно с улучшением 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" (если вы это сделаете, вы можете долго не проживать в среде, которая разрабатывает большие приложения или компоненты многократного использования; -).

Таким образом, на практике вы никогда не удалите локальную переменную "name", даже если пользователи People Experience уже давно переключили это приветствие на более подходящий "Welcome, Dread Overlord!". (и соответственно L10n'ed версии). Все, потому что вы пошли за locals()...

Итак, вы накапливаетесь из-за того, что у вас есть возможность сохранить и отредактировать свой код, и, возможно, эта локальная переменная "name" существует только потому, что она была извлечена из БД или тому подобного, поэтому сохраняя это (или некоторые другие локальные) вокруг не просто круто, это также снижает вашу производительность. Удобство поверхности locals() стоит , что?)

Но подождите, еще хуже! Среди многих полезных сервисов программа lint -like (например,

Сравните это с альтернативой "явный лучше, чем неявный"...:

blah.format(name=name)

Там - ничто из беспокойства по обслуживанию, производительности и am-I-hampering-lint не касается; блаженство! Вы сразу же поняли всем, кого это касается (lint included;-) точно, какие локальные переменные используются, и точно для каких целей.

Я мог бы продолжать, но я думаю, что этот пост уже довольно длинный; -).

Итак, суммируя: " γνῶθι σεαυτόν!" Хм, я имею в виду, "знай себя!". И "сам" я на самом деле подразумеваю "цель и объем вашего кода". Если это 1-off-or-thereabout thingy, никогда не будет i18n'd и L10n'd, вряд ли понадобится обслуживание в будущем, никогда не будет использоваться повторно в более широком контексте и т.д. И т.д., Тогда идите и используйте locals() за его небольшое, но аккуратное удобство; если вы знаете иначе, или даже если вы не совсем уверены, ошибайтесь на стороне осторожности и делайте все более явным - испытывайте небольшое неудобство в написании именно того, что вы собираетесь, и наслаждаетесь всеми вытекающими из этого преимуществами.

Кстати, это лишь один из примеров, когда Python стремится поддерживать как "малое, одноразовое, исследовательское, так и интерактивное" программирование (позволяя и поддерживая опасные удобства, которые выходят далеко за пределы locals()). import *, eval, exec и несколько других способов, позволяющих упростить пространство имен и риски для удобства обслуживания), а также "большие, многоразовые, корпоративные" приложения и компоненты. Это может сделать довольно хорошую работу для обоих, но только если вы "знаете себя" и избегаете использования "удобных" частей, за исключением случаев, когда вы абсолютно уверены, что можете на самом деле их купить. Чаще всего это ключевое соображение: "Что это делает с моими пространствами имен и осознанием их формирования и использования компилятором, читателями и читателями, людьми и т.д.?".

Помните: "Пространства имен - одна отличная идея - пусть больше таких!" как Zen Python завершает... но Python, как "язык для соглашающихся взрослых", позволяет вам определять границы того, что это подразумевает, как следствие вашей среды разработки, целей и практики. Используйте эту силу ответственно! -)

Ответ 2

Я думаю, что это отличный шаблон, потому что вы используете встроенные функции, чтобы уменьшить код, который вам нужно написать. Я лично считаю это довольно Pythonic.

Я никогда не пишу код, который мне не нужно писать - меньше кода лучше, чем больше кода, и эта практика использования locals(), например, позволяет мне писать меньше кода, а также очень легко читать и понимать.

Ответ 3

Никогда через миллион лет. Неясно, каков контекст для форматирования: locals может включать почти любую переменную. self.__dict__ не так расплывчато. Совершенно ужасно, чтобы будущие разработчики почесывали головы над тем, что местное, а что не местное.

Это преднамеренная тайна. Почему седла ваша организация с будущими головными болями в обслуживании?

Ответ 4

Что касается "кузена", вместо obj.__dict__, он выглядит намного лучше при форматировании новой строки:

def example2(obj):
    print "The file at {o.path} has {o.length} bytes".format(o=obj)

Я использую это для методов repr, например.

def __repr__(self):
    return "{s.time}/{s.place}/{s.warning}".format(s=self)

Ответ 5

"%(name)s" % <dictionary> или даже лучше, "{name}".format(<parameters>) имеют достоинства

  • является более читаемым, чем "% 0s"
  • не зависит от порядка аргументов
  • не обязательно использовать все аргументы в строке

Я хотел бы использовать str.format(), так как это должен быть способ сделать это в Python 3 (согласно PEP 3101) и уже доступен с 2.6. Однако с помощью locals() вам нужно будет сделать это:

print("hello {name} you are {age} years old".format(**locals()))

Ответ 6

Использование встроенной vars([object]) (документации) может сделать второй взгляд лучше для вас:

def example2(obj):
    print "The file at %(path)s has %(length)s bytes" % vars(obj)

Эффект, конечно же, один и тот же.

Ответ 7

Теперь есть официальный способ сделать это, начиная с Python 3.6.0: отформатированные строковые литералы.

Он работает следующим образом:

f'normal string text {local_variable_name}'

например. вместо них:

"hello %(name)s you are %(age)s years old" % locals()
"hello {name}s you are {age}s years old".format(**locals())

просто выполните это:

f"hello {name}s you are {age}s years old"

Вот официальный пример:

>>> name = "Fred"
>>> f"He said his name is {name}."
'He said his name is Fred.'
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

Ссылка: