Чи можна мати призначення в стані?
Для екс.
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
>>>
Отже, ні.
x = x + 1вимагає додаткового часу пошуку , коли x += 1був трохи швидше, але я впевнений , що він навіть не подобається робити , що багато. :-)
Python 3.8 додасть PEP572
Анотація
Це пропозиція щодо створення способу присвоєння змінним у виразі за допомогою позначення НАЗВА: = вираження. Додано новий виняток, 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 anscopy.py Код виглядає більш регулярним і уникає декількох вкладених if. (Див. Додаток А для походження цього прикладу.)
Струм:
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, переміщуючи його призначення всередину, якщо допомагає показати його область дії.
Струм:
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 це може усунути необхідність мати нескінченний цикл, призначення та умову. Він також створює плавну паралель між циклом, який просто використовує виклик функції як свою умову, і таким, який використовує це як свій стан, але також використовує фактичне значення.
Fork Приклад із низькорівневого світу 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, а натомість довіреної" дорадчої ради ", що базується переважно на заслугах, фільтруючи чуттєвість одного дизайнера, породило чудову приємну мову, IMHO.
if letколи у мене є ланцюжок if elif, але мені потрібно зберігати та використовувати значення умови в кожному випадку.
Не безпосередньо, за цим моїм старим рецептом - але, як зазначає рецепт, легко створити семантичний еквівалент, наприклад, якщо вам потрібно транслітерувати безпосередньо з алгоритму, що кодується на С (перед переформатуванням на більш ідіоматичний 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
До речі, дуже ідіоматична пітонічна форма для вашого конкретного випадку, якщо ви точно знаєте, яке значення falsish 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 можна буде робити подібні дії з цієї версії, хоча і не використовуючи =оператор присвоєння, схожий на Ада :=. Приклад з документації:
# 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)*