Как присвоить переменной значение в условии IF и вернуть её?
Я столкнулся с проблемой в Python. У меня есть функция, которая определяет, является ли переданное значение "большим" (больше 4), и возвращает соответственно 'apple' или 'orange':
def isBig(x):
if x > 4:
return 'apple'
else:
return 'orange'
Я протестировал два варианта вызова этой функции. Первый вариант работает:
if isBig(y): return isBig(y)
Однако второй вариант не работает:
if fruit = isBig(y): return fruit
В чём дело? Почему второй вариант не работает? Я хотел бы написать код в одну строку, но при этом избежать повторного вызова функции.
Как я могу реализовать это в одну строку, не вызывая функцию дважды?
5 ответ(ов)
Я вижу, кто-то уже указал на мой старый рецепт "assign and set" из «Кулинарной книги», который сводится в своей самой простой версии к:
class Holder(object):
def set(self, value):
self.value = value
return value
def get(self):
return self.value
h = Holder()
...
if h.set(isBig(y)): return h.get()
Однако это задумывалось в основном для облегчения транслитерации между Python и языками, где присваивание поддерживается непосредственно в if
или while
. Если у вас есть "сотни" таких проверок с возвратом в каскаде, то значительно лучше сделать что-то совершенно иное:
hundreds = isBig, isSmall, isJuicy, isBlah, ...
for predicate in hundreds:
result = predicate(y)
if result: return result
или даже что-то вроде
return next(x for x in (f(y) for f in hundreds) if x)
если приемлемо получить исключение StopIteration
, если ни один предикат не выполнен, или
return next((x for x in (f(y) for f in hundreds) if x), None)
если None
является правильным значением возврата, когда ни один предикат не выполнен, и так далее.
Почти всегда использование (или даже желание использовать;-) трюк с Holder
является «дизайнерским запахом», который подсказывает, что стоит искать другой, более питонический подход. Единственный случай, когда использование Holder
оправдано, это именно тот специальный случай, для которого я его и придумал, а именно, когда вы хотите сохранить тесную связь между кодом на Python и каким-то не-Python (вы транслитерируете эталонный алгоритм на Python и хотите, чтобы он работал сначала, прежде чем реорганизовать его в более питоническую форму, или вы пишете Python как прототип, который будет транслитерирован в C++, C#, Java и т.д., как только он будет эффективно работать).
Одна строчка не работает, потому что в Python присваивание (fruit = isBig(y)
) — это оператор, а не выражение. В таких языках, как C, C++, Perl и многих других, присваивание является выражением, и его можно использовать в if
, while
или где угодно, но не в Python. Создатели Python посчитали, что это слишком легко злоупотребить для написания "умного" кода (как вы пытаетесь сделать).
Кроме того, ваш пример выглядит довольно глупо. Функция isBig()
всегда будет возвращать true
, поскольку единственная строка, которая считается ложной — это пустая строка (""
), так что ваш оператор if
в этом случае бесполезен. Я предполагаю, что это просто упрощение того, что вы пытаетесь сделать. Просто сделайте так:
tmp = isBig(y)
if tmp: return tmp
Действительно ли это так сильно хуже?
Вы можете использовать генератор:
def ensure(x):
if x: yield x
for fruit in ensure(isBig(y)):
return fruit
Этот код создает генератор с помощью функции ensure
, которая возвращает значение x
, если оно истинно. В цикле for
вы проходите по элементам, возвращаемым генератором, и после того как найдете подходящий фрукт, возвращаете его. Таким образом, если isBig(y)
возвращает True
, fruit
получит соответствующее значение.
Что-то, что не было указано в предыдущих комментариях, заключается в том, что обычно стоит использовать оператор := с круглыми скобками "()".
Для присвоения значения используйте := и оборачивайте выражение в круглые скобки "()":
if (x := myIndex(y)): return x
Это может работать и без скобок, если возвращаемое значение - True или False, но для любого другого типа возвращаемого значения рекомендуется использовать скобки, иначе присваиваемое значение будет True или False.
Как уже упоминалось, это работает в Python 3.8.
Проблема заключается в том, что операция присваивания не может быть оценена как имеющая булевое значение. Оператор if
требует возможности оценки выражения в булевом контексте. Например, если вы попытаетесь выполнить следующий код:
>>> fruit = 'apple'
>>> bool(fruit = 'apple')
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/jem/<ipython console> in <module>()
TypeError: 'fruit' is an invalid keyword argument for this function
>>> bool('a')
True
Вы получите ошибку TypeError
, потому что в выражении bool(fruit = 'apple')
происходит попытка использовать оператор присваивания =
в контексте, где ожидается булевое значение. Правильный способ проверки значения переменной fruit на равенство строке 'apple' будет выглядеть так:
if fruit == 'apple':
# выполняем действие, если fruit - это 'apple'
Таким образом, вместо присваивания используйте оператор сравнения ==
, чтобы избежать данной ошибки.
Однострочное выражение if-then-else
Стилизация многострочных условий в операторе 'if'? [закрыто]
Как протестировать несколько переменных на равенство одному значению?
if else в списковом включении
Как создать "пустое условие if" в Python