Есть ли карта без результата в python?
Иногда я просто хочу выполнить функцию для списка записей – например:
for x in wowList: installWow(x, 'installed by me')
Иногда мне нужен этот материал для инициализации модуля, поэтому я не хочу иметь такой размер, как x в глобальном пространстве имен. Одним из решений было бы просто использовать карту вместе с лямбдой:
map(lambda x: installWow(x, 'installed by me'), wowList)
Но это, конечно, создает хороший список [None, None, …], так что мой вопрос в том, есть ли подобная функция без возвращаемого списка – так как мне это просто не нужно.
(конечно, я тоже могу использовать _x и, таким образом, не оставлять видимых следов, но картографическое решение выглядит так аккуратно …)
12 Solutions collect form web for “Есть ли карта без результата в python?”
Вы можете использовать фильтр и функцию, которая не возвращает значение True. Вы получите пустой список возврата, так как фильтр добавляет только значения, которые оцениваются как true, что, я полагаю, спасет вам некоторую память. Что-то вроде этого:
#!/usr/bin/env python y = 0 def myfunction(x): global y y += x input = (1, 2, 3, 4) print "Filter output: %s" % repr(filter(myfunction, input)) print "Side effect result: %d" % y
Выполнение этого результата производит:
Filter output: () Side effect result: 10
Вы можете создать свою собственную «каждую» функцию:
def each(fn, items): for item in items: fn(item) # called thus each(lambda x: installWow(x, 'installed by me'), wowList)
def each(fn, items): for item in items: fn(item) # called thus each(lambda x: installWow(x, 'installed by me'), wowList)
В основном это просто карта, но без возвращаемых результатов. Используя функцию, вы убедитесь, что переменная «item» не течет в текущую область.
Как насчет этого?
for x in wowList: installWow(x, 'installed by me') del x
Каждое выражение оценивает что-то, поэтому вы всегда получаете результат, независимо от того, как вы это делаете. И любой такой возвращенный объект (точно так же, как ваш список) впоследствии будет выброшен, потому что больше нет ссылки на него.
Чтобы прояснить: очень немного вещей в python – это утверждения, которые ничего не возвращают. Даже вызов функции
doSomething()
все равно возвращает значение, даже если оно сразу же отбрасывается. В python нет никакой функции, как функция / процедура Pascal.
Вы можете попробовать следующее:
filter(lambda x: installWow(x, 'installed by me') and False, wowList)
Таким образом, результат возврата – это пустой список, несмотря ни на что.
Или вы можете просто отказаться от and False
если вы можете заставить installWow()
всегда возвращать False
(или 0 или None
или другое выражение, которое оценивает false).
если это нормально, чтобы разбить wowList
while wowList: installWow(wowList.pop(), 'installed by me')
если вы хотите сохранить wowList
wowListR = wowList[:] while wowListR: installWow(wowListR.pop(), 'installed by me')
и если вопросы порядка
wowListR = wowList[:]; wowListR.reverse() while wowListR: installWow(wowListR.pop(), 'installed by me')
Хотя в качестве решения головоломки мне нравится первый π
Я не могу удержаться, чтобы опубликовать его как отдельный ответ
reduce(lambda x,y: x(y, 'installed by me') , wowList, installWow)
только твист устанавливается. Вернемся, например,
def installWow(*args): print args return installWow
сначала перепишем цикл for как выражение генератора, который не выделяет никакой памяти.
(installWow(x, 'installed by me') for x in wowList )
Но это выражение на самом деле ничего не делает, не найдя способ его уничтожить. Таким образом, мы можем переписать это, чтобы дать что-то определенное, а не полагаться на возможно None
результата installWow
.
( [1, installWow(x, 'installed by me')][0] for x in wowList )
который создает список, но возвращает только константу 1. это можно удобно использовать с reduce
reduce(sum, ( [1, installWow(x, 'installed by me')][0] for x in wowList ))
Который удобно возвращает количество элементов в wowList, которые были затронуты.
Просто сделайте installWow return None или сделайте последнее утверждение таким:
def installWow(item, phrase='installed by me'): print phrase pass
и используйте это:
list(x for x in wowList if installWow(x))
x не будет задано в глобальном пространстве имен, а возвращаемый список – [] a singleton
Если вы беспокоитесь о необходимости контролировать возвращаемое значение (которое вам нужно использовать для использования фильтра ) и предпочитаете более простое решение, чем приведенный выше пример сокращения , тогда рассмотрите возможность использования сокращения напрямую. Ваша функция должна будет взять дополнительный первый параметр, но вы можете игнорировать его или использовать лямбда, чтобы отбросить его:
reduce(lambda _x: installWow(_x, 'installed by me'), wowList, None)
Позвольте мне предисловие к этому, сказав, что, кажется, оригинальный плакат больше беспокоился о беспорядке пространства имен, чем что-либо еще. В этом случае вы можете обернуть свои рабочие переменные в отдельное пространство имен функций и вызвать его после объявления, или вы можете просто удалить их из пространства имен после того, как вы использовали их с помощью команды del. Или, если у вас есть несколько переменных для очистки, определите функцию со всеми переменными temp в ней, запустите ее, а затем del.
Читайте дальше, если основной проблемой является оптимизация:
Еще три пути, потенциально быстрее, чем другие, описанные здесь:
- Для Python> = 2.7 используйте collections.deque ((installWow (x, 'установлен мной) для x в wowList), 0) # сохраняет 0 записей при повторении всего генератора, но да, все еще имеет побочный результат финального объект вместе с внутренней проверкой длины элемента
- Если вас беспокоит такой накладные расходы, установите cytoolz . Вы можете использовать счетчик, который по-прежнему имеет побочный результат приращения счетчика, но это может быть меньшее количество циклов, чем проверка на предмет, но не уверен. Вы можете использовать его вместо any () следующим образом:
- Замените выражение генератора на itertools.imap (когда installWow никогда не возвращает True. В противном случае вы можете рассмотреть itertools.ifilter и itertools.ifilterfalse с None для предиката): any (itertools.imap (installWow, wowList, itertools.repeat ('установлен меня')))
Но настоящая проблема заключается в том, что функция возвращает что-то, и вы не хотите, чтобы она возвращала что-либо. Поэтому, чтобы решить эту проблему, у вас есть 2 варианта. Один из них заключается в том, чтобы реорганизовать ваш код, поэтому installWow принимает в wowList и выполняет итерацию внутри него. Другой – скорее раздумья, но вы можете загрузить функцию installWow () в скомпилированный аст, например:
lines,lineno=inspect.getsourcelines(func) # func here is installWow without the parens return ast.parse(join(l[4:] for l in lines if l)) # assumes the installWow function is part of a class in a module file.. For a module-level function you would not need the l[4:]
Затем вы можете сделать то же самое для внешней функции и пройти по аш, чтобы найти цикл for. Затем в теле цикла for вставьте элемент определения функции instalWow () функции ast, сопоставляя имена переменных. Затем вы можете просто вызвать exec на самом астрале и предоставить словарь пространств имен с нужными переменными. Чтобы убедиться, что ваши изменения в дереве верны, вы можете проверить, как выглядит конечный исходный код, запустив astunparse .
И если этого недостаточно, вы можете перейти на cython и написать файл .pyx, который будет генерировать и компилировать файл .c в библиотеку с привязками python. Затем, по крайней мере, потерянные циклы не будут потрачены на преобразование в объекты python и обратно, а также проверку типов все раз.
Я проверил несколько разных вариантов, и вот результаты, которые я получил.
Python 2:
>>> timeit.timeit('for x in xrange(100): L.append(x)', 'L = []') 14.9432640076 >>> timeit.timeit('[x for x in xrange(100) if L.append(x) and False]', 'L = []') 16.7011508942 >>> timeit.timeit('next((x for x in xrange(100) if L.append(x) and False), None)', 'L = []') 15.5235641003 >>> timeit.timeit('any(L.append(x) and False for x in xrange(100))', 'L = []') 20.9048290253 >>> timeit.timeit('filter(lambda x: L.append(x) and False, xrange(100))', 'L = []') 27.8524758816
Python 3:
>>> timeit.timeit('for x in range(100): L.append(x)', 'L = []') 13.719769178002025 >>> timeit.timeit('[x for x in range(100) if L.append(x) and False]', 'L = []') 15.041426660001889 >>> timeit.timeit('next((x for x in range(100) if L.append(x) and False), None)', 'L = []') 15.448063717998593 >>> timeit.timeit('any(L.append(x) and False for x in range(100))', 'L = []') 22.087335471998813 >>> timeit.timeit('next(filter(lambda x: L.append(x) and False, range(100)), None)', 'L = []') 36.72446593800123
Обратите внимание, что значения времени не так точны (например, относительная производительность первых трех вариантов варьировалась от запуска до запуска). Мой вывод состоит в том, что вы должны просто использовать цикл, он более читабельный и выполняет, по крайней мере, так же, как и альтернативы. Если вы хотите избежать загрязнения пространства имен, просто del
переменную после ее использования.