3

我想知道是否有一种简单的 Pythonic 方式(可能使用生成器)来对列表中的每个项目运行一个函数并产生一个返回列表?

例子:

def square_it(x):
    return x*x

x_set = [0,1,2,3,4]
squared_set = square_it(x for x in x_set)

我注意到,当我对此进行逐行调试时,传递给函数的对象是生成器。

因此,我收到一个错误: TypeError: unsupported operand type(s) for *: 'generator' and 'generator'

我知道这个生成器表达式创建了一个要传递给函数的生成器,但我想知道是否有一种很酷的方法可以仅通过指定一个可迭代作为参数来完成多次运行该函数?(无需修改函数以期望可迭代)。

在我看来,这种能力对于减少代码行非常有用,因为您不需要创建一个循环来为函数提供乐趣,也不需要创建一个变量来将输出保存在列表中。

谢谢!

4

5 回答 5

5

你想要一个列表理解:

squared_set = [square_it(x) for x in x_set]
于 2013-09-10T16:49:22.530 回答
4

对于这个常见问题,有一个内置函数map() 。

>>> map(square_it, x_set)
[0,1,4,9,16] # On Python 3, a generator is returned.

或者,可以使用生成器表达式,它节省内存但很懒(意味着现在不会计算值,仅在需要时才计算):

>>> (square_it(x) for x in x_set)
    <generator object <genexpr> at ...>

类似地,也可以使用列表推导,它在创建时计算所有值,返回一个列表。

此外,这里是生成器表达式和列表推导的比较。

于 2013-09-10T16:50:18.440 回答
2

您想square_it在生成器内部调用函数,而不是在生成器上调用。

squared_set = (square_it(x) for x in x_set)
于 2013-09-10T16:49:26.997 回答
1

请注意,列表推导返回列表之间存在差异

squared_set = [square_it(x) for x in x_set]

并返回一个您可以对其进行迭代的生成器:

squared_set = (square_it(x) for x in x_set)
于 2013-09-10T19:01:07.320 回答
1

正如其他答案所建议的那样,我认为最好(大多数“pythonic”)使用列表或生成器理解在每个元素上显式调用您的函数。

不过,要真正回答这个问题,您可以使用一个嗅探输入的函数来包装在缩放器上运行的函数,并根据它看到的内容具有不同的行为。例如:

>>> import types
>>> def scaler_over_generator(f):
...     def wrapper(x):
...         if isinstance(x, types.GeneratorType):
...             return [f(i) for i in x]
...         return f(x)
...     return wrapper
>>> def square_it(x):
...     return x * x
>>> square_it_maybe_over = scaler_over_generator(square_it)
>>> square_it_maybe_over(10)
    100
>>> square_it_maybe_over(x for x in range(5))
    [0, 1, 4, 9, 16]

我不会在我的代码中使用这个成语,但可以这样做。

您还可以使用装饰器对其进行编码,如下所示:

>>> @scaler_over_generator
... def square_it(x):
...     return x * x
>>> square_it(x for x in range(5))
    [0, 1, 4, 9, 16]

如果您不想要/不需要原始函数的句柄。

于 2013-09-10T17:22:55.777 回答