Python3 - 开始python编程(十二)
在上一篇文章中,我们介绍了软件包和虚拟环境。今天,我们将回过头来介绍一些尚未介绍的内置类方法。
在开始之前,我们应该介绍一些基本算法。这些都非常基础,但是为我们入门提供了良好的基础。
让我们从先前介绍的while循环开始。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
my_list = [1, 2, 3, 4, 5]
# Forward loop
index = 0
while index < len(my_list):
print(my_list[index])
index += 1
# Reverse loop
index = len(my_list) - 1
while index >= 0:
print(my_list[index])
index -= 1
# Get the last item for a list
def get_last_item(some_list):
last_index = len(some_list) - 1
return some_list[last_index]
print(get_last_item(my_list))
我将承认有更好的方法可以做到这一点,但这为我们提供了一个简单的示例,为我们为本文的其余部分做准备。
forward looping_和while循环对我们来说并不陌生,但是,我们尚未涵盖_reverse loops。尽管这对您中的某些人来说可能是微不足道的,但我不会假设所有读者都已经了解了循环遍历列表的工作原理。
在反向循环中,我们首先需要获取列表的长度,并将我们的index变量设置为length-1。我们这样做是因为列表基于0,这意味着最后一个可访问的索引将比总数少一个。我们列表中的项目。
而不是像在前向循环中那样将”index”的值与列表的长度进行比较,我们需要确保在循环中”index”的值不小于0。
我们可以执行列表中所需的工作,然后从index
中减去1以获得列表中的上一个条目。一旦“索引”小于0,则while循环退出。
最后,我创建了一个函数,该函数返回给定列表中的最后一项。我们提供的列表都没有关系,它将始终返回最后一个项目。我添加此内容是因为这是我们将需要继续熟悉的概念。
迭代器
有时,我们需要创建包含一系列对象的类,这些对象可以循环通过,同时对循环的数据执行一些恒定的操作。这些对象的范围可以从一系列数字到一系列类。迭代器是提供此功能的类。
您需要熟悉两种类方法:
__iter __(self)
-返回一个迭代器对象__next __(self)
-用于返回序列中的下一项
了解__iter__
和__init__
之间的区别很重要。
当您要在向类提供数据的同时创建类的实例时,使用__init__。这是我们将数据分配给迭代器的地方。
当您想使用序列(列表,有序_dict等)为迭代器循环通过的数据创建种子类时,使用__iter__。
__iter__返回带有__next__方法的对象,如果您在类内部具有自定义的__next__方法,则只需从__iter__方法返回self即可。
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Doubled:
def __init__(self, data):
self.length = len(data)
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index == self.length:
raise StopIteration
result = self.data[self.index] * 2
self.index += 1
return result
my_list = [1, 2, 3, 4, 5]
doubled = Doubled(my_list)
try:
while True:
print(next(doubled))
except StopIteration:
print("Completed")
在这里,我们有一个Doubled类,它具有__init , iter__和__next__方法。
__init__
在类初始化时要求一个参数data
。就我们而言,我们将类设计为将数字列表加倍。为此,我们需要确保将整数或浮点值列表传递给该类。
然后,__init__方法获取数据的长度,因此我们知道何时需要停止遍历传入的列表。它将数据保存到名为data的类属性中,并将当前索引位置设置为0。我们设置了当前索引,因此在创建新的迭代器时,我们始终从0开始。
因为我们在此类中定义了自己的__next__方法,所以我们只需要在__iter__方法内部返回return self即可。
__next__包含用于返回所传递列表中下一个值的逻辑。如果我们要传递字典,它将返回字典中的值,尽管我们需要sp说明如何返回下一个键的值。
在我们的__next__方法中,我们首先检查我们当前的索引位置不等于数据的长度。如果是这样,我们需要引发StopIteration
异常。这与”IndexError”不同,后者在到达列表末尾时会收到,因为我们可能将此迭代器用于其他数据类型。
“StopIteration”特定于我们收到的错误,如果我们需要再次遍历该系列,那么我们要么需要创建另一个迭代器对象,要么想出一种不同的方法来遍历我们的数据。
接下来,我们执行此迭代器的逻辑。因为我们将列表中的每一项都加倍,所以将当前索引的值乘以2保存到结果变量中。之所以这样做,是因为我们需要为下一次运行准备索引,我们将在下一行进行准备。最后,如果一切成功,我们将返回结果。
迭代器的存在使我们的工作更加轻松,同时也确保了逻辑的一致性。但是,它们并不是遍历代码的唯一方法。
生成器
生成器类似于迭代器,除了它们的功能类似于迭代器。关于生成器的整洁之处在于它们允许您的函数暂停直到再次调用。从某种意义上说,它们有自己的__next__
和__iter__
方法。与函数不同,它们使用关键字”yield”返回数据。让我们看一个简单的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ef increment(number):
yield number + 1
yield number + 2
incrementor = increment(4)
try:
print(next(incrementor))
print(next(incrementor))
print(next(incrementor))
except StopIteration:
print("Nothing else...")
为了进行错误处理,我为此添加了一个try / except。
首先,我们创建一个名为”increment”的函数,该函数带有一个参数:”number”。在函数主体中,我们有两个yield
语句;第一个返回传入的数字并加1,第二个返回数字加2。
然后,我们需要将增量函数存储在变量”incrementor”中,为上面的增量函数提供初始值。这些都将存储在变量中,并像类对象一样对待。
从这里,我们可以像迭代器一样使用它,通过将incrementor
传递给next()
函数,我们可以按顺序接收每个yield
语句。这意味着我们将在控制台上看到”5”和”6”。如果像上面一样第三次调用next(incrementor)
,则会收到StopIteration
异常。
使生成器如此有趣的是,我们对它们使用了无限循环。由于yield
会暂停函数的执行,因此我们一次只收到一个结果。这意味着以下代码有效,不会引起问题。
1
2
3
4
5
6
def increment(number=0):
n = number
while True:
yield n + 1
n = n + 1
在这里,我们有一个新的增量器函数,它接受一个数字。 “= 0”表示如果未提供数字,则使用0作为默认值。然后,我们创建一个局部变量”n”,并为其分配“数字”。
接下来,我们有一个无限循环,尽管通常这些循环是不行的,但在这里还是可以接受的,因为yield
会在每次调用时暂停执行。在函数的末尾,我们有n = n + 1,这会增加我们本地的n变量。
由于该函数在传递”yield”时暂停,因此下次我们将递增器传递给”next()”函数时,它将在包含”n = n + 1”的行上恢复。
生成器也可以以接近列表理解的格式编写。唯一的区别是我们在表达式周围使用括号而不是方括号。
1
2
3
4
5
my_list = [1, 2, 3, 4, 5]
generator_object = (i * 2 for i in my_list)
list_comp_object = [i * 2 for i in my_list]
从这里我们可以使用简单的for循环访问generator_object
中的每个项目,就像我们在list_comp_object
中得到的列表一样,或者我们可以使用next(generator_object)
获得下一个值。
迭代器和生成器之间的一个重要区别是,迭代器先计算所有值并将它们存储在内存中,生成器仅将每次运行所需的内容存储在内存中。
生成器的运行速度比列表理解要慢,因此除非遇到内存不足的情况,否则应使用列表理解。嘿,如果需要,很容易进行重构。
生成器可用于递归任务,例如在硬盘驱动器上搜索文件或在网络抓取实用程序中浏览网页。
摘要
迭代器和生成器更多地是一个中间主题,但是一旦使用几次,便开始寻找在任何地方使用它们的方法。了解迭代器和生成器之间的区别对于应用程序的性能至关重要。确保在将它们包括在内之前就已经知道它们之间的区别了。
下一步是什么?
接下来是异步,此主题非常高级,并提供了许多用于同步代码的选项。您不仅可以配置时间,还可以同时运行代码。挂在那里,直到那时,继续练习!