Можем ли мы иметь назначение в условии? - PullRequest
67 голосов
/ 09 апреля 2010

Возможно ли присвоение в условии?

Например,

if (a=some_func()):
    # Use a

Ответы [ 9 ]

91 голосов
/ 09 апреля 2010

Почему бы не попробовать?

>>> def some_func():
...   return 2
... 
>>> a = 2
>>> if (a = some_func()):
  File "<stdin>", line 1
    if (a = some_func()):
          ^
SyntaxError: invalid syntax
>>> 

Итак, нет.

37 голосов
/ 09 апреля 2010

Нет, BDFL не понравилась эта функция.

С, где я сижу, Гвидо ван Россум, «Благотворительное Dictator For Life», боролся трудно держать Python так просто, как это может быть мы можем спорить с некоторыми из решений, которые он сделал. - Я предпочел бы он чаще говорили «Нет», но тот факт, что не был комитет, разрабатывающий Python, а вместо этого был доверенный «консультативный совет», основанный в основном на заслугах, фильтрующих через чувства one дизайнера, породил один чертовски хороший язык, ИМХО.

27 голосов
/ 09 апреля 2010

ОБНОВЛЕНИЕ - Оригинальный ответ в нижней части

Python 3.8 принесет PEP572

Аннотация
Это предложение для создания способа присвоения переменных в выражении, используя обозначение NAME: = expr. Новое исключение, TargetScopeError добавлен, и есть одно изменение в оценке заказ.

https://lwn.net/Articles/757713/

«Беспорядок в PEP 572» был темой саммита Python в 2018 году Сессия во главе с доброжелательным диктатором на всю жизнь (BDFL) Гидо ван Россум. PEP 572 стремится добавить выражения присваивания (или «встроенные присваивания») к языку, но он видел длительное обсуждение в течение нескольких огромные потоки в списке рассылки python-dev - даже после нескольких раундов на питон-идеи. Эти темы часто были спорными и были явно объемный до такой степени, что многие, вероятно, просто отключили их. В На саммите Ван Россум дал обзор функционального предложения, которое он, кажется, склонен принять, но он также хотел обсудить, как чтобы избежать такого взрыва нити в будущем.

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 Код выглядит более регулярно и избегает многократного вложения. (Происхождение этого примера см. В приложении А.)

Ток:

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

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-t-i-use-an-assignment-in-an-expression.htm

16 голосов
/ 09 апреля 2010

Не напрямую, за мой старый рецепт - но, как говорится в рецепте, легко создать семантический эквивалент, например, если вам нужно транслитерировать напрямую из C-кодированного эталонного алгоритма (конечно, перед рефакторингом на более идиоматический Python ;-). I.e.:

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

Кстати, очень идиоматическая Pythonic форма для вашего конкретного случая, если вы точно знаете, какое фальшивое значение somefunc может вернуть, когда оно возвращает фальшивое значение (например, 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
14 голосов
/ 09 октября 2018

Да, но только с 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
4 голосов
/ 09 октября 2018

Благодаря новой функции Python 3.8 можно будет делать такие вещи из этой версии, хотя и не с использованием =, но с ада-подобным оператором присваивания :=. Пример из документов:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match
4 голосов
/ 09 апреля 2010

Нет. Назначение в Python - это утверждение, а не выражение.

2 голосов
/ 10 ноября 2017

Вы можете определить функцию для назначения:

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)
1 голос
/ 11 ноября 2013

Одна из причин, по которой присвоения являются недопустимыми в условиях, заключается в том, что проще допустить ошибку и присвоить значение 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 являются ключевыми словами, поэтому больше нет риска.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...