Возможно ли иметь назначение в условии?
Например,
if (a=some_func()):
# Use a
Возможно ли иметь назначение в условии?
Например,
if (a=some_func()):
# Use a
Почему бы не попробовать?
>>> def some_func():
... return 2
...
>>> a = 2
>>> if (a = some_func()):
File "<stdin>", line 1
if (a = some_func()):
^
SyntaxError: invalid syntax
>>>
Итак, нет.
Нет, BDFL не понравилась эта функция.
С того места, где я сижу, Гвидо ван Россум, "Доброжелательный диктатор для жизни", изо всех сил старался держать Python настолько простым, насколько это возможно. Мы можем спорить с некоторыми из решений, которые он принял - я бы предпочел, чтобы он сказал "Нет" чаще. Но тот факт, что не был комитет, проектирующий Python, а вместо этого доверенный "консультативный совет", основанный в основном на заслугах, фильтруя через одну дизайнерскую чувствительность, произвел один адский приятный язык, ИМХО.
Python 3.8 принесет PEP572
Аннотация
Это предложение по созданию способа присваивания переменным в выражении с использованием обозначения NAME: = expr. Добавлено новое исключение TargetScopeError, и есть одно изменение в порядке оценки.
https://lwn.net/Articles/757713/
"Беспорядок в PEP 572" был темой заседания на высшем уровне по языку Python 2018 года, которое провел доброжелательный диктатор на всю жизнь (BDFL) Гидо ван Россум. PEP 572 стремится добавить выражения присваивания (или "встроенные присваивания") к языку, но он видел длительное обсуждение нескольких огромных потоков в списке рассылки python-dev - даже после нескольких раундов по идеям python. Эти темы часто были спорными и явно объемными до такой степени, что многие, вероятно, только что отключили их. На саммите Ван Россум представил обзор предложения, которое он, похоже, склонен принять, но он также хотел обсудить, как избежать такого взрыва потока в будущем.
https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library
Примеры из стандартной библиотеки Python
site.py env_base используется только в этих строках, его назначение присваивается if, перемещая его как "заголовок" блока.
Текущий:
env_base = os.environ.get("PYTHONUSERBASE", None) if env_base: return env_base
Улучшенный:
if env_base := os.environ.get("PYTHONUSERBASE", None): return env_base _pydecimal.py
Избегайте вложенных if и удалите один уровень отступа.
Текущий:
if self._is_special: ans = self._check_nans(context=context) if ans: return ans
Улучшенный:
if self._is_special and (ans := self._check_nans(context=context)): return ans
copy.py Код выглядит более регулярно и избегать множественных вложений if. (См. Приложение A для происхождения этого примера.)
Текущий:
reductor = dispatch_table.get(cls) if reductor: rv = reductor(x) else: reductor = getattr(x, "__reduce_ex__", None) if reductor: rv = reductor(4) else: reductor = getattr(x, "__reduce__", None) if reductor: rv = reductor() else: raise Error( "un(deep)copyable object of type %s" % cls)
Улучшенный:
if reductor := dispatch_table.get(cls): rv = reductor(x) elif reductor := getattr(x, "__reduce_ex__", None): rv = reductor(4) elif reductor := getattr(x, "__reduce__", None): rv = reductor() else: raise Error("un(deep)copyable object of type %s" % cls) datetime.py
tz используется только для s + = tz, перемещение его назначения внутри if помогает показать его область видимости.
Текущий:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) tz = self._tzstr() if tz: s += tz return s
Улучшенный:
s = _format_time(self._hour, self._minute, self._second, self._microsecond, timespec) if tz := self._tzstr(): s += tz return s
sysconfig.py Вызов fp.readline() в условии while и вызов .match() в строках if делают код более компактным без
затрудняя понимание.
Текущий:
while True: line = fp.readline() if not line: break m = define_rx.match(line) if m: n, v = m.group(1, 2) try: v = int(v) except ValueError: pass vars[n] = v else: m = undef_rx.match(line) if m: vars[m.group(1)] = 0
Улучшенный:
while line := fp.readline(): if m := define_rx.match(line): n, v = m.group(1, 2) try: v = int(v) except ValueError: pass vars[n] = v elif m := undef_rx.match(line): vars[m.group(1)] = 0
Упрощение понимания списка. Понимание списка может эффективно отображать и фильтровать путем захвата условия:
results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]
Аналогично, подвыражение можно повторно использовать в основном выражении, дав ему имя при первом использовании:
stuff = [[y := f(x), x/y] for x in range(5)]
Обратите внимание, что в обоих случаях переменная y связана в области видимости (то есть на том же уровне, что и результаты или материал).
Захват значений условий Выражения присваивания могут быть эффективно использованы в заголовке оператора if или while:
# Loop-and-a-half while (command := input("> ")) != "quit": print("You entered:", command) # Capturing regular expression match objects # See, for instance, Lib/pydoc.py, which uses a multiline spelling # of this effect if match := re.search(pat, text): print("Found:", match.group(0)) # The same syntax chains nicely into 'elif' statements, unlike the # equivalent using assignment statements. elif match := re.search(otherpat, text): print("Alternate found:", match.group(0)) elif match := re.search(third, text): print("Fallback found:", match.group(0)) # Reading socket data until an empty string is returned while data := sock.recv(8192): print("Received data:", data)
В частности, с циклом while это может устранить необходимость иметь бесконечный цикл, присваивание и условие. Он также создает плавную параллель между циклом, который просто использует вызов функции в качестве своего условия, и циклом, который использует это в качестве своего условия, но также использует фактическое значение.
Вилка Пример из мира UNIX низкого уровня:
if pid := os.fork(): # Parent code else: # Child code
http://docs.python.org/tutorial/datastructures.html
Обратите внимание, что в Python, в отличие от C, присваивание не может происходить внутри выражений. Программисты на C могут ворчать по этому поводу, но это позволяет избежать распространенного класса проблем, встречающихся в программах на C: вводить = в выражении, когда == было задумано.
также см:
http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm
Не прямо, за этот старый рецепт моего - но, как говорится в рецепте, легко построить семантический эквивалент, например. если вам нужно транслитерировать непосредственно из C-кодированного эталонного алгоритма (до рефакторинга на более -диоматический Python, конечно;-). То есть:.
class DataHolder(object):
def __init__(self, value=None): self.value = value
def set(self, value): self.value = value; return value
def get(self): return self.value
data = DataHolder()
while data.set(somefunc()):
a = data.get()
# use a
BTW, очень идиоматическая питоническая форма для вашего конкретного случая, если вы точно знаете, какое значение falsish somefunc
может вернуться, когда оно вернет значение falsish (например, 0
),
for a in iter(somefunc, 0):
# use a
поэтому в этом конкретном случае рефакторинг будет довольно простым; -).
Если возврат может быть любым значением фальшивого значения (0, None
, ''
,...), одна из возможностей:
import itertools
for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
# use a
но вы можете предпочесть простой пользовательский генератор:
def getwhile(func, *a, **k):
while True:
x = func(*a, **k)
if not x: break
yield x
for a in getwhile(somefunc):
# use a
Да, но только с Python 3.8 и выше.
PEP 572 предлагает выражения назначения и уже принят.
Цитируя часть синтаксиса и семантики PEP:
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
process(chunk)
# Reuse a value that expensive to compute
[y := f(x), y**2, y**3]
# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
В вашем конкретном случае вы сможете написать
if a := some_func():
# Use a
Нет. Назначение в Python - это выражение, а не выражение.
Благодаря новой функции Python 3.8 это можно будет сделать из этой версии, хотя и без использования =
но с помощью Ada-подобного оператора присваивания :=
. Пример из документов:
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
Вы можете определить функцию для назначения для вас:
def assign(name, value):
import inspect
frame = inspect.currentframe()
try:
locals_ = frame.f_back.f_locals
finally:
del frame
locals_[name] = value
return value
if assign('test', 0):
print("first", test)
elif assign('xyz', 123):
print("second", xyz)
Одна из причин, по которой присвоения являются незаконными в условиях, заключается в том, что проще совершить ошибку и назначить True или False:
some_variable = 5
# This does not work
# if True = some_variable:
# do_something()
# This only works in Python 2.x
True = some_variable
print True # returns 5
В Python 3 True и False являются ключевыми словами, поэтому больше никакого риска.