Kar*_*tel 5 python iteration list-comprehension
Stack Overflow 上有很多关于这个一般主题的问答,但它们要么质量很差(通常是初学者的调试问题暗示的),要么以其他方式错过了目标(通常是不够通用)。至少有两种极其常见的方法会使幼稚的代码出错,初学者从关于循环的规范中获益更多,而不是从将问题作为拼写错误或关于打印所需内容的规范中获益。所以这是我尝试将所有相关信息放在同一个地方。
假设我有一些简单的代码,可以对一个值进行计算x
并将其分配给y
:
y = x + 1
# Or it could be in a function:
def calc_y(an_x):
return an_x + 1
Run Code Online (Sandbox Code Playgroud)
现在我想重复计算 的许多可能值x
。我知道for
如果我已经有要使用的值列表(或其他序列),我可以使用循环:
xs = [1, 3, 5]
for x in xs:
y = x + 1
Run Code Online (Sandbox Code Playgroud)
while
或者,如果有其他逻辑来计算值序列,我可以使用循环x
:
def next_collatz(value):
if value % 2 == 0:
return value // 2
else:
return 3 * value + 1
def collatz_from_19():
x = 19
while x != 1:
x = next_collatz(x)
Run Code Online (Sandbox Code Playgroud)
问题是:如何收集这些值并在循环后使用它们?我尝试print
在循环内获取值,但它没有给我任何有用的东西:
xs = [1, 3, 5]
for x in xs:
print(x + 1)
Run Code Online (Sandbox Code Playgroud)
结果显示在屏幕上,但我找不到任何方法在代码的下一部分中使用它们。所以我认为我应该尝试将值存储在容器中,例如列表或字典。但是当我尝试这样做时:
xs = [1, 3, 5]
for x in xs:
ys = []
y = x + 1
ys.append(y)
Run Code Online (Sandbox Code Playgroud)
或者
xs = [1, 3, 5]
for x in xs:
ys = {}
y = x + 1
ys[x] = y
Run Code Online (Sandbox Code Playgroud)
在这两种尝试之后,ys
仅包含最后的结果。
解决这个问题有三种常见的方法: 显式使用循环(通常是for
循环,但while
循环也是可能的);通过使用列表理解(或字典理解、集合理解或生成器表达式,根据上下文的特定需要而定);或者使用内置函数map
(其结果可用于显式构造列表、集合或字典)。
在循环之前创建一个列表或字典,并在计算时添加每个值:
def make_list_with_inline_code_and_for():
ys = []
for x in [1, 3, 5]:
ys.append(x + 1)
return ys
def next_collatz(value):
if value % 2 == 0:
return value // 2
else:
return 3 * value + 1
def make_dict_with_function_and_while():
x = 19
ys = {}
while x != 1:
y = next_collatz(x)
ys[x] = y # associate each key with the next number in the Collatz sequence.
x = y # continue calculating the sequence.
return ys
Run Code Online (Sandbox Code Playgroud)
在这两个示例中,循环都被放入函数中,以便标记代码并使其可重用。这些示例的值return
以便调用代码可以使用结果。当然,计算后也可以在同一函数中使用,并且类似的循环也可以在任何函数之外编写。ys
ys
for
当存在现有输入时使用循环,其中每个元素应独立处理。使用while
循环创建输出元素,直到满足某些条件。Python不直接支持运行循环特定次数(预先计算);通常的习惯是制作一个range
适当长度的虚拟对象并使用一个for
循环。
列表推导式提供了从现有值序列创建列表的优雅语法。如果可能的话应该首选它,因为这意味着代码不必关注如何构建列表的细节,从而更容易阅读。它也可以更快,尽管这通常并不重要。
它可以与函数调用或其他计算(“源”元素的任何表达式)一起使用,它看起来像:
xs = [1, 3, 5]
ys = [x + 1 for x in xs]
# or
def calc_y(an_x):
return an_x + 1
ys = [calc_y(x) for x in xs]
Run Code Online (Sandbox Code Playgroud)
请注意,这不会取代while
循环;这里没有有效的语法for
替换while
。一般来说,列表推导式的目的是获取现有值并对每个值进行单独的计算 - 而不是用于涉及“记住”从一次迭代到下一次迭代的任何内容的任何类型的逻辑(尽管这可以解决,特别是在 Python 3.8 和之后)。
类似地,可以使用字典理解来创建字典结果 - 只要在每次迭代中计算键和值即可。根据具体需要,集合推导式(生成 a set
,不包含重复值)和生成器表达式(生成延迟计算的结果;请参阅下面的有关map
生成器表达式)也可能是合适的。
map
这类似于列表理解,但更具体。map
是一个内置函数,可以将函数重复应用于某个输入序列(或多个序列)中的多个不同参数。
获得与之前的代码等效的结果如下所示:
xs = [1, 3, 5]
def calc_y(an_x):
return an_x + 1
ys = list(map(calc_y, xs))
# or
ys = list(map(lambda x: x + 1, xs))
Run Code Online (Sandbox Code Playgroud)
除了需要输入序列(它不会替换while
循环)之外,还需要使用函数或其他可调用的来完成计算,例如上面显示的lambda(其中任何一个,当传递给 时map
,都是所谓的“高阶函数”)。
在 Python 3.x 中,map
是一个类,因此调用它会创建该类的一个实例 - 并且该实例是一种特殊类型的迭代器(不是列表),不能迭代多次。(我们可以使用生成器表达式而不是列表理解得到类似的东西;只需使用()
而不是[]
。)
因此,上面的代码显式地根据映射值创建一个列表。在其他情况下,可能没有必要这样做(即,如果只迭代一次)。另一方面,如果 aset
是必需的,则map
可以直接将对象传递给set
而不用list
同样的方式。要生成字典,map
应该设置为每个输出元素都是一个(key, value)
元组;然后它可以传递给dict
,如下所示:
def dict_from_map_example(letters):
return dict(map(lambda l: (l, l.upper()), letters))
# equivalent using a dict comprehension:
# return {l:l.upper() for l in letters}
Run Code Online (Sandbox Code Playgroud)
一般来说,map
与列表推导式相比,它是有限且不常见的,并且在大多数代码中应该首选列表推导式。然而,它确实提供了一些优点。特别是,它可以避免指定和使用迭代变量的需要:当我们编写 时list(map(calc_y, xs))
,我们不需要编写 anx
来命名 的元素xs
,并且我们不必编写代码来将其传递给calc_y
(如在列表理解等效项中,[calc_y(x) for x in xs]
- 请注意两个x
s)。有些人觉得这更优雅。
归档时间: |
|
查看次数: |
2855 次 |
最近记录: |