30

有时,我只想为条目列表执行一个函数——例如:

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

有时我需要这些东西来进行模块初始化,所以我不想在全局命名空间中有像 x 这样的足迹。一种解决方案是将 map 与 lambda 一起使用:

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

但这当然会创建一个不错的列表 [None, None, ...] 所以我的问题是,如果有没有返回列表的类似函数 - 因为我只是不需要它。

(当然我也可以使用 _x ,因此不会留下可见的足迹——但地图解决方案看起来很整洁......)

4

16 回答 16

29

您可以制作自己的“每个”功能:


def each(fn, items):
    for item in items:
        fn(item)


# called thus
each(lambda x: installWow(x, 'installed by me'), wowList)

基本上它只是地图,但没有返回结果。通过使用函数,您将确保“item”变量不会泄漏到当前范围内。

于 2009-07-03T17:45:30.493 回答
14

这个怎么样?

for x in wowList:
    installWow(x, 'installed by me')
del x
于 2009-07-03T16:45:26.150 回答
13

您可以使用内置any函数将不带 return 语句的函数应用于生成器返回的任何项目,而无需创建列表。这可以这样实现:

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

我发现这是您想要实现的最简洁的惯用语。

在内部,该installWow函数确实返回,它在逻辑操作None中求值。基本上将归约操作应用于生成器返回的所有项目,当然都是,所以它必须遍历生成器返回的所有项目。最后它确实返回了,但这不需要打扰你。好消息是:没有创建列表作为副作用。FalseanyorNoneFalse

请注意,这仅适用于您的函数返回的结果为 的值False,例如,None或 0。如果它确实返回了True在某个点计算为的值,例如,1它不会应用于迭代器中的任何剩余元素. 为了安全起见,这个习惯用法主要用于没有返回语句的函数。

于 2017-11-09T03:35:49.733 回答
6

你可以试试这个:

filter(lambda x: installWow(x, 'installed by me') and False, wowList)

这样,无论如何,返回结果都是一个空列表。

and False或者,如果您可以强制installWow()始终返回False(或 0 或None其他评估为假的表达式),您也可以放弃。

于 2009-07-03T16:30:22.527 回答
6

每个表达式都会计算出某种结果,因此无论您采用哪种方式,您总能得到一个结果。并且任何此类返回的对象(就像您的列表一样)之后都会被丢弃,因为不再引用它。

澄清一下:python中很少有东西是不返回任何东西的语句。甚至像这样的函数调用

doSomething()

仍然返回一个值,即使它被立即丢弃。python中没有Pascal的函数/过程区分之类的东西。

于 2009-07-03T16:30:39.360 回答
3

您可以使用过滤器和不返回 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
于 2009-07-03T16:32:07.020 回答
3

如果可以破坏 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')

虽然作为谜题的解决方案,我喜欢第一个:)

于 2009-07-03T17:32:31.437 回答
3

我无法抗拒自己将其作为单独的答案发布

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

唯一的扭曲是 installWow 应该返回自身,例如

def installWow(*args):
    print args
    return installWow
于 2009-07-03T17:41:16.283 回答
2

我测试了几种不同的变体,这是我得到的结果。

蟒蛇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

蟒蛇 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使用后的变量即可。

于 2013-04-02T19:21:47.837 回答
1

首先将 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 中受影响的项目数。

于 2009-07-03T18:00:03.337 回答
1

只需让 installWow 返回 None 或让最后一条语句像这样传递:


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

并使用这个:


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

x 不会在全局名称空间中设置,返回的列表是 [] 单例

于 2009-07-04T02:06:00.193 回答
1

如果您担心需要控制返回值(使用filter需要这样做)并且更喜欢比上面的reduce示例更简单的解决方案,那么请考虑直接使用reduce。您的函数将需要额外的第一个参数,但您可以忽略它,或使用 lambda 来丢弃它:

reduce(lambda _x: installWow(_x, 'installed by me'), wowList, None)
于 2013-12-30T18:26:45.633 回答
1

让我先说一下,最初的发帖人似乎比其他任何事情都更关心命名空间的混乱。在这种情况下,您可以将工作变量包装在单独的函数命名空间中并在声明后调用它,或者您可以在使用“del”内置命令后将它们从命名空间中删除。或者,如果您要清理多个变量,请定义包含所有临时变量的函数,运行它,然后删除它。

如果主要关注的是优化,请继续阅读:

另外三种方式,可能比此处描述的其他方式更快:

  1. 对于 Python >= 2.7,使用 collections.deque((installWow(x, 'installed by me') for x in wowList),0) # 在迭代整个生成器时保存 0 个条目,但是是的,仍然有一个 final 的副产品对象以及内部每个项目的长度检查
  2. 如果担心这种开销,请安装cytoolz。您可以使用count,它仍然具有递增计数器的副产品,但它可能比 deque 的每个项目检查的周期数更少,不确定。您可以通过以下方式使用它而不是 any():
  3. 用 itertools.imap 替换生成器表达式(当 installWow 永远不会返回 True。否则,您可以考虑使用 None 作为谓词的 itertools.ifilter 和 itertools.ifilterfalse): any(itertools.imap(installWow,wowList,itertools.repeat('installed by我')))

但这里真正的问题是函数返回一些东西而你不希望它返回任何东西。所以要解决这个问题,你有两个选择。一种是重构您的代码,以便 installWow 接收 wowList 并在内部对其进行迭代。另一个相当令人兴奋,但您可以将 installWow() 函数加载到已编译的 ast 中,如下所示:

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

然后,您可以对外部函数执行相同的操作,并遍历 ast 以找到 for 循环。然后在 for 循环体中,插入 instalWow() 函数 ast 的函数定义体,匹配变量名。然后,您可以简单地在 ast 本身上调用 exec,并提供一个名称空间字典,其中填充了正确的变量。为了确保您的树修改是正确的,您可以通过运行astunparse检查最终源代码的样子。

如果这还不够,您可以去 cython 并编写一个 .pyx 文件,该文件将生成一个 .c 文件并将其编译到具有 python 绑定的库中。然后,至少丢失的循环不会花费在与 python 对象之间进行转换和重复检查所有内容上。

于 2016-05-23T19:42:50.627 回答
1

一个简单的 DIY,其唯一目的是遍历生成器表达式:

def do(genexpr):
    for _ in genexpr:
        pass

然后使用:

do(installWow(x, 'installed by me') for x in wowList)
于 2020-04-07T15:00:19.577 回答
0

有人需要回答——

这里更pythonic的方法是不用担心污染命名空间,并使用__all__来定义公共变量。

myModule/__init__.py:
     __all__ = ['func1', 'func2']

     for x in range(10): 
         print 'init {}'.format(x)

     def privateHelper1(x):
         return '{}:{}'.format(x,x)

     def func1(): 
         print privateHelper1('func1')

     def func2(): 
         print privateHelper1('func1')

然后

python -c "import myModule; help(myModule);"

init 0
init 1
init 2
init 3
init 4
init 5
init 6
init 7
init 8
init 9
Help on package mm:

NAME
    myModule

FILE
    h:\myModule\__init__.py

PACKAGE CONTENTS


FUNCTIONS
    func1()

   func2()

DATA
   __all__ = ['func1', 'func2']
于 2018-12-19T15:39:04.937 回答
0

在 python 3 中,有一些方法可以使用没有返回的函数(只需在 jupyter 中使用分号 ot 省略单元格的输出):

[*map(print, MY_LIST)]; # form 1 - unpack the map generator to a list

any(map(print, MY_LIST)); # form 2 - force execution with any

list(map(print, MY_LIST)); # form 3 - collect list from generator
于 2022-02-28T21:01:52.927 回答