阅读此博客文章后,您将知道:
- Python中的迭代iteration是如何工作的
- 什么是iterables和iterators,以及如何创建它们
- 什么是iterator protocol
- 什么是lazy evaluation
- 什么是generator functions和generator expressions
Python的for循环
Python没有传统的for
循环。让我们看一下伪代码,了解传统的for循环在许多其他编程语言中的外观。
for (initializer; condition; iterator)
body
- 进入循环之前,初始化程序部分仅执行一次。
- 的条件部分必须是一个布尔表达式。如果该表达式的值为True,则执行下一个循环迭代。
- 该迭代器部分定义了每个迭代之后会发生什么。
现在,让我们看看如何用JavaScript编写传统的for循环。
let numbers = [10, 12, 15, 18, 20];
for (let i = 0; i < numbers.length; i += 1) {
console.log(numbers[i])
}
输出:
10
12
15
18
20
许多其他编程语言都具有这种for循环,但Python没有。但是,Python有一个叫做for loop
的东西,但是它像一个foreach loop一样工作。
numbers = [10, 12, 15, 18, 20]
for number in numbers:
print(number)
输出:
10
12
15
18
20
从上面的示例中,我们可以看到在Python的for
循环中,我们没有以前看到的任何部分。没有初始化,条件或迭代器部分。
可迭代
可迭代对象是一种能够逐个返回其成员 的对象。换句话说,可迭代是您可以在Python中使用循环进行循环的任何内容。for
顺序
序列是一种非常常见类型的迭代。内置序列类型的一些示例是列表,字符串和元组。
numbers = [10, 12, 15, 18, 20]
fruits = ("apple", "pineapple", "blueberry")
message = "I love Python ❤️"
它们支持通过特殊方法(索引)使用整数索引进行有效的元素访问,并定义一种返回序列长度的方法。__getitem()__``__length()__
# Element access using integer indices
print(numbers[0])
print(fruits[2])
print(message[-2])
使用索引访问元素。
输出:
10
blueberry
❤
另外,我们可以对它们使用切片技术。如果您不知道切片的内容,则在我解释时可以查看我以前的文章之一。切片技术的说明在“子集列表”部分中。
# Slicing the sequences
print(numbers[:2])
print(fruits[1:])
print(message[2:])
切片序列。
输出:
[10, 12]
('pineapple', 'blueberry')
love Python ❤️
其他可迭代
Python中的很多东西都是可迭代的,但并非全部都是序列。字典,文件对象,集合和生成器都是可迭代的,但是它们都不是序列。
my_set = {2, 3, 5}
my_dict = {"name": "Ventsislav", "age": 24}
my_file = open("file_name.txt")
squares = (n**2 for n in my_set)
集合,字典,文件和生成器。
Python的for循环不使用索引
让我们考虑一下如何在不使用for
Python循环的情况下循环遍历可迭代对象。我们中有些人可能认为我们可以使用while
循环并生成索引来实现这一目标。
index = 0
numbers = [1, 2, 3, 4, 5]
while index < len(numbers):
print(numbers[index])
index += 1
输出:
1
2
3
4
5
看来这种方法对列表和其他序列对象非常有效。那么非序列对象呢?他们不支持索引编制,因此这种方法对他们不起作用。
index = 0
numbers = {1, 2, 3, 4, 5}
while index < len(numbers):
print(numbers[index])
index += 1
输出:
--------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-22-af1fab82d68f> in <module>()
2 numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
3 while index < len(numbers):
----> 4 print(numbers[index])
5 index += 1
TypeError: 'set' object does not support indexing
嗯,但是Python的for循环如何在这些可迭代对象上工作?我们可以看到它适用于集合。
numbers = {1, 2, 3, 4, 5}
for number in numbers:
print(number)
遍历一组。
输出:
1
2
3
4
5
迭代器
迭代器是代表数据流(stream of data)的对象。您可以通过将iter()
内置函数应用于iterable来创建迭代器对象。
numbers = [10, 12, 15, 18, 20]
fruits = ("apple", "pineapple", "blueberry")
message = "I love Python ❤️"
print(iter(numbers))
print(iter(fruits))
print(iter(message))
输出:
<list_iterator object at 0x000001DBCEC33B70>
<tuple_iterator object at 0x000001DBCEC33B00>
<str_iterator object at 0x000001DBCEC33C18>
您可以使用迭代器来手动遍历它来自的迭代器。将迭代器反复传递给内置函数next()将返回流中的连续项。一旦从迭代器中使用了一个项,它就消失了。当没有更多数据可用时,将引发StopIteration异常。
输出:
values = [10, 20, 30]
iterator = iter(values)
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
输出
10
20
30
--------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-14-fd36f9d8809f> in <module>()
4 print(next(iterator))
5 print(next(iterator))
----> 6 print(next(iterator))
StopIteration:
在后台,Python的for
循环使用迭代器。
了解Python的for循环的工作方式
Source: https://www.incredible-web.com/blog/performance-of-for-loops-with-javascript/
现在,我们知道什么是iterables和iterators,以及如何使用它们。我们可以尝试定义一个不使用循环就遍历可迭代对象的函数for
。
为此,我们需要:
- 从给定的iterable器创建一个iterator
- 从迭代器中获取下一项
- 执行所需的动作
- 如果尝试获取下一个项目时遇到StopIteration ****异常,请停止循环
def custom_for_loop(iterable, action_to_do):
iterator = iter(iterable)
done_looping = False
while not done_looping:
try:
item = next(iterator)
except StopIteration:
done_looping = True
else:
action_to_do(item)
让我们尝试将此函数与一组数字和print
内置函数一起使用。
numbers = {1, 2, 3, 4, 5}
custom_for_loop(numbers, print)
Python中的自定义For循环-用法。
输出:
1
2
3
4
5
我们可以看到,我们定义的函数与不是序列的集合很好地配合使用。这次,我们可以通过任何迭代,它将起作用。在幕后,Python中可迭代的所有形式的循环都以这种方式工作。
迭代器协议(Iterator Protocol)
需要迭代器对象支持以下两种方法,它们共同构成了迭代器协议:
- iterator .__ iter __()
返回迭代器对象本身。这是必需的,以便允许容器(也称为集合)和迭代器与for
andin
语句一起使用。 - iterator .__ next __()
返回容器中的下一项。如果没有更多项目,请引发StopIteration异常。
从上面的方法描述中,我们看到可以遍历迭代器。因此,迭代器也是可迭代的。
请记住,将iter()
函数应用于可迭代对象时,我们会得到一个迭代器。如果我们在迭代器上调用该iter()
函数,它将始终使我们自身返回。
numbers = [100, 200, 300]
iterator1 = iter(numbers)
iterator2 = iter(iterator1)
# Check if they are the same object
print(iterator1 is iterator2)
for number in iterator1:
print(number)
输出:
True
100
200
300
有关迭代器的其他说明
这听起来可能有些混乱。但是,如果您第一次不了解所有内容,请不要担心。让我们回顾一下!
- 一个迭代是你可以遍历。
- 一个迭代是表示对象数据的流。它对可迭代对象进行迭代。
另外,在Python中,迭代器也是可迭代的,它们充当自己的迭代器。
但是,不同之处在于迭代器没有某些可迭代对象具有的功能。它们没有长度,也无法索引。
例子
numbers = [100, 200, 300]
iterator = iter(numbers)
print(len(iterator))
迭代器无长度错误
输出:
--------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-778b5f9befc3> in <module>()
1 numbers = [100, 200, 300]
2 iterator = iter(numbers)
----> 3 print(len(iterator))
TypeError: object of type 'list_iterator' has no len()
numbers = [100, 200, 300]
iterator = iter(numbers)
print(iterator[0])
索引迭代器错误
输出:
--------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-16-64c378cb8a99> in <module>()
1 numbers = [100, 200, 300]
2 iterator = iter(numbers)
----> 3 print(iterator[0])
TypeError: 'list_iterator' object is not subscriptable
Iterators are lazy
迭代器使我们既可以工作又可以创建lazy iterables,这些可迭代对象在我们要求他们提供下一个项目之前不会做任何工作。
来源:https : //opensource.com/article/18/3/loop-better-deeper-look-iteration-python
由于它们的惰性,迭代器可以帮助我们处理无限长的可迭代对象。在某些情况下,我们甚至无法将所有信息存储在内存中,因此我们可以使用迭代器,该迭代器可以在每次询问时提供下一项。迭代器可以为我们节省大量内存和CPU时间。
这种方法称为惰性评估。
迭代器无处不在
我们已经看到了一些带有迭代器的示例。此外,Python有许多内置类,它们是迭代器。例如,enumerate
和reversed
对象是迭代器。
列举例子
fruits = ("apple", "pineapple", "blueberry")
iterator = enumerate(fruits)
print(type(iterator))
print(next(iterator))
输出:
<class 'enumerate'>
(0, 'apple')
Reversed Example
fruits = ("apple", "pineapple", "blueberry")
iterator = reversed(fruits)
print(type(iterator))
print(next(iterator))
输出:
<class 'reversed'>
blueberry
在Python的zip
,map
而且filer
对象也迭代器。
Zip Example
numbers = [1, 2, 3]
squares = [1, 4, 9]
iterator = zip(numbers, squares)
print(type(iterator))
print(next(iterator))
print(next(iterator))
输出:
<class 'zip'>
(1, 1)
(2, 4)
Map Example
numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x**2, numbers)
print(type(squared))
print(next(squared))
print(next(squared))
输出:
<class 'map'>
1
4
Filter Example
numbers = [-1, -2, 3, -4, 5]
positive = filter(lambda x: x > 0, numbers)
print(type(positive))
print(next(positive))
输出:
<class 'filter'>
3
而且,Python中的文件对象也是迭代器。
file = open("example.txt")
print(type(file))
print(next(file))
print(next(file))
print(next(file))
file.close()
输出:
<class '_io.TextIOWrapper'>
This is the first line.
This is the second line.
This is the third line.
我们还可以使用方法迭代Python ** dictionary****的键值对**。items()
my_dict = {"name": "Ventsislav", "age": 24}
iterator = my_dict.items()
print(type(iterator))
for key, item in iterator:
print(key, item)
输出:
<class 'dict_items'>
name Ventsislav
age 24
许多人使用Python解决数据科学问题。在某些情况下,您使用的数据可能非常大。在这种情况下,我们无法将所有数据加载到内存中。
解决方案是将数据分块****加载,然后在每个块上执行所需的操作,丢弃该块并加载下一个数据块。换句话说,我们需要创建一个迭代器。我们可以通过使用pandas中的read_csv
函数来实现。我们只需要指定chunksize即可。
大数据集示例
在此示例中,我们将看到一个名为“ 虹膜种类 ” 的小型数据集的想法,但相同的概念也将适用于非常大的数据集。我更改了列名,您可以在此处找到我的版本。
import pandas as pd
# Initialize an empty dictionary
counts_dict = {}
# Iterate over the file chunk by chunk
for chunk in pd.read_csv("iris.csv", chunksize = 10):
# Iterate over the "species" column in DataFrame
for entry in chunk["species"]:
if entry in counts_dict.keys():
counts_dict[entry] += 1
else:
counts_dict[entry] = 1
# Print the populated dictionary
print(counts_dict)
输出:
{'Iris-setosa': 50, 'Iris-versicolor': 50, 'Iris-virginica': 50}
Python标准库和第三方库中有很多迭代器对象。
通过定义类创建自定义迭代器
在某些情况下,我们可能想创建一个自定义迭代器。我们可以通过定义一个具有init,next和iter方法的类来做到这一点。
让我们尝试创建一个自定义迭代器类,该类生成最小值和最大值之间的数字。
class generate_numbers:
def __init__(self, min_value, max_value):
self.current = min_value
self.high = max_value
def __iter__(self):
return self
def __next__(self):
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
numbers = generate_numbers(40, 50)
print(type(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
输出:
<class '__main__.generate_numbers'>
40
41
42
我们可以看到这可行。但是,使用generator function或generator expression创建自定义迭代器要容易得多。
generator function和generator expression
通常,当我们要创建自定义迭代器时,我们会使用generator function或generator expression。它们使用起来更简单,并且需要更少的代码即可达到相同的结果。
Generator Functions
让我们看看什么是Python文档中的生成器函数。
返回生成器迭代器的函数。它看起来像一个正常功能,除了它包含
[**yield**](https://docs.python.org/3.7/reference/simple_stmts.html#yield)
表达式用于产生一系列值的在一个for循环可用或可以检索一次一个与功能。[**next(**](https://docs.python.org/3.7/library/functions.html#next)**)**
来源:https://docs.python.org/3.7/glossary.html#term-generator
现在,我们可以尝试使用generator function重新创建自定义迭代器。
def generate_numbers(min_value, max_value):
while min_value < max_value:
yield min_value
min_value += 1
numbers = generate_numbers(10, 20)
print(type(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
输出:
<class 'generator'>
10
11
12
yield表达式是将一个generator function从一个普通函数中分离出来的东西。这个表达式帮助我们利用迭代器的惰性。
每个组件都会
[yield](https://docs.python.org/3.7/reference/simple_stmts.html#yield)
临时挂起处理,并记住位置执行状态(包括局部变量和挂起的尝试语句)。当生成器迭代器恢复时,它将在中断的地方继续(与在每次调用时重新启动的函数相反)。
来源:https : //docs.python.org/3.7/glossary.html#term-generator-iterator
generator expression
generator expressions与列表理解非常相似。就像列表理解一样,一般表达式是简洁的。在大多数情况下,它们是用一行代码编写的。
该表达式返回一个迭代器。它看起来像一个普通表达式,后跟一个
[for](https://docs.python.org/3.7/reference/compound_stmts.html#for)
定义循环变量,范围和可选[if](https://docs.python.org/3.7/reference/compound_stmts.html#if)
表达式的表达式。
资料来源:https : //docs.python.org/3.7/glossary.html#term-generator-expression
general formula: (iterable中迭代器变量的输出表达式)
让我们看看如何定义一个简单的生成器表达式。
numbers = [1, 2, 3, 4, 5]
squares = (number**2 for number in numbers)
print(type(squares))
print(next(squares))
print(next(squares))
print(next(squares))
输出:
<class 'generator'>
1
4
9
我们还可以在iterable ****上添加条件表达式。我们可以这样做:
numbers = [1, 2, 3, 4, 5]
squares = (number**2 for number in numbers if number % 2 == 0)
print(type(squares))
print(list(squares))
输出:
<class 'generator'>
[4, 16]
它们可以是可迭代的多个条件表达式,以进行更复杂的过滤。
numbers = [1, 2, 3, 4, 5]
squares = (number**2 for number in numbers if number % 2 == 0 if number % 4 == 0)
print(type(squares))
print(list(squares))
输出:
<class 'generator'>
[16]
另外,我们可以在输出表达式上添加if-else子句,如下所示:
numbers = [1, 2, 3, 4, 5]
result = ("even" if number % 2 == 0 else "odd" for number in numbers)
print(type(result))
print(list(result))
输出:
<class 'generator'>
['odd', 'even', 'odd', 'even', 'odd']
总结
- iterable是可以循环的东西。。
- ** Sequences是可迭代**的非常常见的类型。
- Python中的很多东西都是可迭代的,但并非全部都是序列。
- 一个迭代是表示对象数据的流。它对可迭代对象进行迭代。您可以使用迭代器获取下一个值或对其进行循环。一旦循环遍历一个迭代器,就不再有流值。
- 迭代器使用惰性评估方法。
- Python中许多内置类都是迭代器。
- 甲generator function是它返回一个的函数迭代。
- 甲generator expression是返回的表达式迭代器。
翻译自:https://towardsdatascience.com/python-basics-iteration-and-looping-6ca63b30835c
网友评论