0

Как присвоить переменной значение в условии IF и вернуть её?

14

Я столкнулся с проблемой в 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 ответ(ов)

0

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

0

Одна строчка не работает, потому что в Python присваивание (fruit = isBig(y)) — это оператор, а не выражение. В таких языках, как C, C++, Perl и многих других, присваивание является выражением, и его можно использовать в if, while или где угодно, но не в Python. Создатели Python посчитали, что это слишком легко злоупотребить для написания "умного" кода (как вы пытаетесь сделать).

Кроме того, ваш пример выглядит довольно глупо. Функция isBig() всегда будет возвращать true, поскольку единственная строка, которая считается ложной — это пустая строка (""), так что ваш оператор if в этом случае бесполезен. Я предполагаю, что это просто упрощение того, что вы пытаетесь сделать. Просто сделайте так:

tmp = isBig(y)
if tmp: return tmp

Действительно ли это так сильно хуже?

0

Вы можете использовать генератор:

def ensure(x):
    if x: yield x

for fruit in ensure(isBig(y)):
    return fruit

Этот код создает генератор с помощью функции ensure, которая возвращает значение x, если оно истинно. В цикле for вы проходите по элементам, возвращаемым генератором, и после того как найдете подходящий фрукт, возвращаете его. Таким образом, если isBig(y) возвращает True, fruit получит соответствующее значение.

0

Что-то, что не было указано в предыдущих комментариях, заключается в том, что обычно стоит использовать оператор := с круглыми скобками "()".

Для присвоения значения используйте := и оборачивайте выражение в круглые скобки "()":

if (x := myIndex(y)): return x

Это может работать и без скобок, если возвращаемое значение - True или False, но для любого другого типа возвращаемого значения рекомендуется использовать скобки, иначе присваиваемое значение будет True или False.

Как уже упоминалось, это работает в Python 3.8.

0

Проблема заключается в том, что операция присваивания не может быть оценена как имеющая булевое значение. Оператор 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'

Таким образом, вместо присваивания используйте оператор сравнения ==, чтобы избежать данной ошибки.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь