Max ([x for x in something]) vs max (x для x в чем-то): почему существует разница и что это такое?
Я работал над проектом для класса, где мой код не выдавал те же результаты, что и ссылочный код.
Я сравнил свой код с ссылочным кодом по строкам, они появились почти точно так же. Все казалось логически эквивалентным. В конце концов я начал заменять строки и тестирование, пока не нашел нужную строку.
Оказалось, что это что-то вроде этого (EDIT: точный код ниже):
# my version:
max_q = max([x for x in self.getQValues(state)])
# reference version which worked:
max_q = max(x for x in self.getQValues(state))
Теперь это меня озадачило. Я попробовал несколько экспериментов с интерпретатором Python (2.7), выполнив тесты с использованием max
в списках с квадратными скобками и без них. Результаты оказались точно такими же.
Даже путем отладки через PyCharm я не мог найти причин, по которым моя версия не привела к тому же результату, что и эталонная версия. До этого момента я подумал, что у меня неплохой инструмент управления работой списков (и как работает функция max()
), но теперь я не уверен, потому что это такое странное несоответствие.
Что здесь происходит? Почему мой код производит разные результаты, чем код ссылки (в версии 2.7)? Как переход в понимании без скобок отличается от перехода в понимании с помощью скобок?
EDIT 2: точный код:
# works
max_q = max(self.getQValue(nextState, action) for action in legal_actions)
# doesn't work (i.e., provides different results)
max_q = max([self.getQValue(nextState, action) for action in legal_actions])
Я не думаю, что это должно быть помечено как дублирующее - да, другой вопрос касается разницы между объектами понимания и объектами списка, но не почему max()
будет давать разные результаты при задании "некоторого списка, построенного по X понимание, а не только" понимание Х".
Ответы
Ответ 1
Вы пропускаете локальную переменную, которая влияет на более поздний код?
# works
action = 'something important'
max_q = max(self.getQValue(nextState, action) for action in legal_actions)
assert action == 'something important'
# doesn't work (i.e., provides different results)
max_q = max([self.getQValue(nextState, action) for action in legal_actions])
assert action == 'something important' # fails!
Понятия генератора и словаря создают новую область видимости, но до py3, понимания списков нет, для обратной совместимости
Легкий способ тестирования - измените свой код на:
max_q = max([self.getQValue(nextState, action) for action in legal_actions])
max_q = max(self.getQValue(nextState, action) for action in legal_actions)
Предполагая, что self.getQValue
является чистым, то единственным прочным побочным эффектом первой строки будет беспорядок с локальными переменными. Если это сломает его, то причина вашей проблемы.
Ответ 2
Использование []
вокруг понимания списка фактически генерирует список в вашу переменную или в этом случае в вашу функцию max. Без скобок вы создаете объект generator
, который будет передан в функцию max.
results1 = (x for x in range(10))
results2 = [x for x in range(10)]
result3 = max(x for x in range(10))
result4 = max([x for x in range(10)])
print(type(results1)) # <class 'generator'>
print(type(results2)) # <class 'list'>
print(result3) # 9
print(result4) # 9
Насколько я знаю, они должны работать по существу одинаково в пределах функции max.
Ответ 3
Я не знаю, почему у вас разные значения в вашем проекте, но я могу привести вам живой пример, когда это произойдет. Генератор более эффективен, чем список, поэтому у нас будет другое использование памяти. Я использую Python 3.
Здесь функция, которая возвращает текущее использование памяти Python:
import os
import psutil
def memory_usage():
"""Get process virtual memory (vms) usage in MB."""
process = psutil.Process(os.getpid())
memory = process.memory_info()[1] / (1024.0 * 1024.0)
return memory
Попробуйте этот код:
# Generator version:
max_q = max(memory_usage() for i in range(100000))
print(max_q) # 7.03125
Я тестировал код несколько раз, и на моей машине я получаю что-то более 7.
Замените версию генератора версией списка:
# List version:
max_q = max([memory_usage() for i in range(100000)])
print(max_q) # 11.44921875
Я получаю что-то более 11 на моей машине.
Как вы видите, код почти такой же, но вы получите другой результат.
Может быть в вашем проекте getQValue() дает вам разные значения на основе уже рассчитанных. Но эти существующие значения могут быть удалены сборщиком мусора быстрее, если вы используете генератор.