Возможна ли уступка в состоянии?
Например,
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
>>>
Так что нет.
Обновление: это возможно (с другим синтаксисом) в Python 3.8
x = x + 1требует дополнительного времени поиска , когда x += 1был несколько быстрее, но я уверен , что он даже не нравится делать , что много. :-)
Python 3.8 принесет PEP572
Аннотация
Это предложение по созданию способа присвоения переменных в выражении с использованием обозначения NAME: = expr. Добавлено новое исключение TargetScopeError и одно изменение в порядке оценки.
https://lwn.net/Articles/757713/
«Беспорядок в PEP 572» был темой сессии Python Language Summit 2018, которую провел благотворительный пожизненный диктатор (BDFL) Гвидо ван Россум. PEP 572 стремится добавить к языку выражения присваивания (или «встроенные присваивания»), но он видел продолжительное обсуждение множества огромных потоков в списке рассылки python-dev - даже после нескольких раундов на python-idea. Эти темы часто были спорными и были явно объемными до такой степени, что многие, вероятно, просто игнорировали их. На саммите Ван Россум сделал обзор предложения по функциям, который он, кажется, склонен принять, но он также хотел обсудить, как избежать такого рода всплесков потоков в будущем.
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 anscopy.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.pytz используется только для 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 ssysconfig.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
if (foo = 'bar'), намереваясь проверить ценность foo.
=
Нет, BDFL не понравилась эта функция.
С того места, где я сижу, Гвидо ван Россум, «Доброжелательный диктатор на всю жизнь», изо всех сил старался сохранить Python настолько простым, насколько это возможно. Мы можем спорить с некоторыми из принятых им решений - я бы предпочел, чтобы он сказал: «Нет Но тот факт, что не было комитета, разрабатывающего Python, а вместо этого доверенный «консультативный совет», основанный в основном на заслугах и фильтрующий чувства одного дизайнера, создал чертовски хороший язык, ИМХО.
if letкогда у меня есть цепочка if elif, но мне нужно хранить и использовать значение условия в каждом случае.
Не напрямую, в соответствии с моим старым рецептом, но, как говорится в рецепте, семантический эквивалент легко построить, например, если вам нужно выполнить транслитерацию непосредственно из ссылочного алгоритма с кодировкой 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
Кстати, очень идиоматическая питоническая форма для вашего конкретного случая, если вы точно знаете, какое ложное значение somefuncможет вернуть, когда оно действительно возвращает ложное значение (например 0), является
for a in iter(somefunc, 0):
# use a
так что в этом конкретном случае рефакторинг будет довольно простым ;-).
Если возвращение может быть любой вид falsish значения (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's 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 - ключевые слова, так что больше никакого риска.
== Trueвсе равно выбирают правильную сторону.
Оператор присваивания, также неофициально известный как оператор моржа, был создан 28 февраля 2018 г. в PEP572 .
Для полноты картины я опубликую соответствующие части, чтобы вы могли сравнить различия между 3.7 и 3.8:
3.7
---
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
test: or_test ['if' or_test 'else' test] | lambdef
test_nocond: or_test | lambdef_nocond
lambdef: 'lambda' [varargslist] ':' test
lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
3.8
---
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
namedexpr_test: test [':=' test] <---- WALRUS OPERATOR!!!
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*