Питонский способ переписать назначение в операторе if
Есть ли питон предпочтительный способ сделать это, что я бы сделал в C++:
for s in str:
if r = regex.match(s):
print r.groups()
Мне очень нравится этот синтаксис, потому что он намного чище, чем временные переменные везде. Единственный не слишком сложный способ
for s in str:
r = regex.match(s)
if r:
print r.groups()
Думаю, я жалуюсь на довольно педантичный вопрос. Я просто скучаю по прежнему синтаксису.
7 ответов
Как насчет
for r in [regex.match(s) for s in str]:
if r:
print r.groups()
или чуть более функционально
for r in filter(None, map(regex.match, str)):
print r.groups()
Возможно, это немного странно, но использование атрибутов объекта функции для хранения последнего результата позволяет вам сделать что-то вроде этого:
def fn(regex, s):
fn.match = regex.match(s) # save result
return fn.match
for s in strings:
if fn(regex, s):
print fn.match.groups()
Или более обобщенно:
def cache(value):
cache.value = value
return value
for s in strings:
if cache(regex.match(s)):
print cache.value.groups()
Обратите внимание, что хотя сохраненное "значение" может быть набором из нескольких вещей, этот подход ограничен удержанием только одного такого за раз, поэтому может потребоваться более одной функции для обработки ситуаций, когда необходимо сохранить несколько значений одновременно Например, во вложенных вызовах функций, циклах или других потоках. Таким образом, в соответствии с принципом СУХОЙ, вместо того, чтобы писать каждую, заводская функция может помочь:
def Cache():
def cache(value):
cache.value = value
return value
return cache
cache1 = Cache()
for s in strings:
if cache1(regex.match(s)):
# use another at same time
cache2 = Cache()
if cache2(somethingelse) != cache1.value:
process(cache2.value)
print cache1.value.groups()
...
Всякий раз, когда я обнаруживаю, что моя логика цикла становится сложной, я делаю то же, что и с любой другой частью логики: я извлекаю ее в функцию. В Python это сделать намного проще, чем в некоторых других языках.
Итак, извлеките код, который просто генерирует интересующие элементы:
def matching(strings, regex):
for s in strings:
r = regex.match(s)
if r: yield r
и затем, когда вы захотите его использовать, сам цикл настолько прост, насколько это возможно:
for r in matching(strings, regex):
print r.groups()
Нет питонского способа сделать что-то не питоническое. Это так по какой-то причине, потому что 1, разрешение операторов в условной части оператора if сделает грамматику довольно уродливой, например, если вы разрешите операторы присваивания в условиях if, почему бы не разрешить операторы if? как бы ты это написал? В языках, подобных C, такой проблемы нет, потому что в них нет операторов присваивания. Они обходятся только с помощью выражений присваивания и выражений выражений.
Вторая причина заключается в том, как
if foo = bar:
pass
выглядит очень похоже на
if foo == bar:
pass
даже если вы достаточно умны, чтобы набрать правильный, и даже если большинство членов вашей команды достаточно проницательны, чтобы заметить это, вы уверены, что тот, на кого вы смотрите сейчас, это именно то, что должно быть там? для нового разработчика вполне разумно видеть это и просто исправлять это (так или иначе), и теперь это определенно неправильно.
Это может быть слишком упрощенным ответом, но вы могли бы рассмотреть это:
for s in str:
if regex.match(s):
print regex.match(s).groups()
Есть рецепт для создания выражения присваивания, но он очень хакерский. Ваш первый вариант не компилируется, поэтому ваш второй вариант - путь.
## {{{ http://code.activestate.com/recipes/202234/ (r2)
import sys
def set(**kw):
assert len(kw)==1
a = sys._getframe(1)
a.f_locals.update(kw)
return kw.values()[0]
#
# sample
#
A=range(10)
while set(x=A.pop()):
print x
## end of http://code.activestate.com/recipes/202234/ }}}
Как вы можете видеть, производственный код не должен касаться этого взлома десятифутовой палкой в двойной упаковке.
Еще один ответ - использовать рецепт "Назначить и проверить", чтобы разрешить назначение и тестирование в одном утверждении, опубликованном в 1-м выпуске поваренной книги Python от O'Reilly Media за июль 2002 года, а также в Интернете в Activestate. Это объектно-ориентированный, суть которого заключается в следующем:
# from http://code.activestate.com/recipes/66061
class DataHolder:
def __init__(self, value=None):
self.value = value
def set(self, value):
self.value = value
return value
def get(self):
return self.value
При желании это можно немного изменить, добавив __call__()
метод, показанный ниже, чтобы предоставить альтернативный способ извлечения значений экземпляров, что, хотя и менее явно, кажется вполне логичным для объекта "DataHolder", когда он вызывается, я думаю.
def __call__(self):
return self.value
Позвольте вашему примеру быть переписанным:
r = DataHolder()
for s in strings:
if r.set(regex.match(s))
print r.get().groups()
# or
print r().groups()
Как также отмечалось в оригинальном рецепте, если вы часто его используете, добавьте класс и / или его экземпляр к __builtin__
Модуль сделать его глобально доступным очень заманчиво, несмотря на возможные недостатки:
import __builtin__
__builtin__.DataHolder = DataHolder
__builtin__.data = DataHolder()
Как я уже упоминал в своем другом ответе на этот вопрос, необходимо отметить, что этот подход ограничен удержанием только одного результата / значения за раз, поэтому для обработки ситуаций, в которых требуется одновременное сохранение нескольких значений, требуется более одного экземпляра. например, во вложенных вызовах функций, циклах или других потоках. Это не значит, что вы должны использовать его или другой ответ, просто потребуется больше усилий.