Is there a map without result in python?

2019-01-15 05:44发布

Sometimes, I just want to execute a function for a list of entries -- eg.:

for x in wowList:
   installWow(x, 'installed by me')

Sometimes I need this stuff for module initialization, so I don't want to have a footprint like x in global namespace. One solution would be to just use map together with lambda:

map(lambda x: installWow(x, 'installed by me'), wowList)

But this of course creates a nice list [None, None, ...] so my question is, if there is a similar function without a return-list -- since I just don't need it.

(off course I can also use _x and thus not leaving visible footprint -- but the map-solution looks so neat ...)

14条回答
Animai°情兽
2楼-- · 2019-01-15 06:31

Let me preface this by saying that it seems the original poster was more concerned about namespace clutter than anything else. In that case, you can wrap your working variables in separate function namespace and call it after declaring it, or you can simply remove them from the namespace after you've used them with the "del" builtin command. Or, if you have multiple variables to clean up, def the function with all the temp variables in it, run it, then del it.

Read on if the main concern is optimization:

Three more ways, potentially faster than others described here:

  1. For Python >= 2.7, use collections.deque((installWow(x, 'installed by me') for x in wowList),0) # saves 0 entries while iterating the entire generator, but yes, still has a byproduct of a final object along with a per-item length check internally
  2. If worried about this kind of overhead, install cytoolz. You can use count which still has a byproduct of incrementing a counter but it may be a smaller number of cycles than deque's per-item check, not sure. You can use it instead of any() in the next way:
  3. Replace the generator expression with itertools.imap (when installWow never returns True. Otherwise you may consider itertools.ifilter and itertools.ifilterfalse with None for the predicate): any(itertools.imap(installWow,wowList,itertools.repeat('installed by me')))

But the real problem here is the fact that a function returns something and you do not want it to return anything.. So to resolve this, you have 2 options. One is to refactor your code so installWow takes in the wowList and iterates it internally. Another is rather mindblowing, but you can load the installWow() function into a compiled ast like so:

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:]

You can then do the same for the outer function, and traverse the ast to find the for loop. Then in the body of the for loop, insert the instalWow() function ast's function definition body, matching up the variable names. You can then simply call exec on the ast itself, and provide a namespace dictionary with the right variables filled in. To make sure your tree modifications are correct, you can check what the final source code would look like by running astunparse.

And if that isn't enough you can go to cython and write a .pyx file which will generate and compile a .c file into a library with python bindings. Then, at least the lost cycles won't be spent converting to and from python objects and type-checking everything repeatedly.

查看更多
等我变得足够好
3楼-- · 2019-01-15 06:32

You can use the built-in any function to apply a function without return statement to any item returned by a generator without creating a list. This can be achieved like this:

any(installWow(x, 'installed by me') for x in wowList)

I found this the most concise idom for what you want to achieve.

Internally, the installWow function does return None which evaluates to False in logical operations. any basically applies an or reduction operation to all items returned by the generator, which are all None of course, so it has to iterate over all items returned by the generator. In the end it does return False, but that doesn't need to bother you. The good thing is: no list is created as a side-effect.

Note that this only works as long as your function returns something that evaluates to False, e.g., None or 0. If it does return something that evaluates to True at some point, e.g., 1, it will not be applied to any of the remaining elements in your iterator. To be safe, use this idiom mainly for functions without return statement.

查看更多
混吃等死
4楼-- · 2019-01-15 06:32

You could use a filter and a function that doesn't return a True value. You'd get an empty return list since filter only adds the values which evaluates to true, which I suppose would save you some memory. Something like this:

#!/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

Running it produces this output:

Filter output: ()
Side effect result: 10
查看更多
我只想做你的唯一
5楼-- · 2019-01-15 06:32

Just make installWow return None or make the last statement be pass like so:


def installWow(item, phrase='installed by me'):
  print phrase
  pass

and use this:


list(x for x in wowList if installWow(x))

x won't be set in the global name space and the list returned is [] a singleton

查看更多
时光不老,我们不散
6楼-- · 2019-01-15 06:35

if it is ok to distruct wowList

while wowList: installWow(wowList.pop(), 'installed by me')

if you do want to maintain wowList

wowListR = wowList[:]
while wowListR: installWow(wowListR.pop(), 'installed by me')

and if order matters

wowListR = wowList[:]; wowListR.reverse()
while wowListR: installWow(wowListR.pop(), 'installed by me')

Though as the solution of the puzzle I like the first :)

查看更多
欢心
7楼-- · 2019-01-15 06:36

If you're worried about the need to control the return value (which you need to do to use filter) and prefer a simpler solution than the reduce example above, then consider using reduce directly. Your function will need to take an additional first parameter, but you can ignore it, or use a lambda to discard it:

reduce(lambda _x: installWow(_x, 'installed by me'), wowList, None)
查看更多
登录 后发表回答