Rumah > Artikel > pembangunan bahagian belakang > Cara menggunakan gelung Python dan iterator
Dalam Python, seperti kebanyakan bahasa, terdapat dua gelung asas: while
dan for
. Gelung
while
adalah sangat asas.
clue = None while clue is None: clue = searchLocation()
clue is None
Dalam kes ini, kod untuk gelung akan dilaksanakan selagi keadaan gelung bernilai True
.
Dalam Python, kami juga mempunyai beberapa kata kunci yang berguna: break
Hentikan gelung serta-merta sambil continue
melompat ke lelaran seterusnya bagi gelung satu lelaran. Salah satu aspek yang paling berguna bagi
break
ialah jika kita mahu menjalankan kod yang sama sehingga pengguna memberikan input yang sah.
while True: try: age = int(input("Enter your age: ")) except ValueError: print(f"Please enter a valid integer!") else: if age > 0: break
Sebaik sahaja kami menemui kenyataan break
ini, kami keluar dari gelung. Sudah tentu, di atas adalah contoh yang lebih kompleks, tetapi ia menunjukkan maksudnya. Anda juga sering melihat while True:
digunakan dalam gelung permainan.
Nota: Jika anda pernah menggunakan gelung dalam mana-mana bahasa, anda sudah biasa dengan gelung tak terhingga (gelung tak terhingga). Ini biasanya disebabkan oleh keadaan while
yang menilai kepada True
dan tiada pernyataan break
dalam gelung.
Daripada Java, C++ atau banyak bahasa gaya ALGOL yang serupa, anda mungkin biasa dengan gelung for
tiga hala: for i := 1; i < 100; i := i + 1
. Saya tidak tahu tentang anda, tetapi apabila saya mula-mula menghadapi situasi ini, saya terkejut. Saya gembira dengannya sekarang, tetapi ia tidak mempunyai kesederhanaan elegan Python.
Gelung for
Python kelihatan sangat berbeza. Kod Python yang setara dengan pseudokod di atas ialah...
for i in range(1,100): print(i)
range() ialah "fungsi" khas dalam Python yang mengembalikan jujukan. (Secara teknikal, ia bukan satu fungsi sama sekali.)
Ini adalah perkara yang mengagumkan tentang Python - ia berulang pada jenis jujukan khas yang dipanggil iterable, Kita akan mengetahuinya kemudian .
Setakat ini, yang paling mudah difahami ialah kita boleh mengulang struktur data berjujukan, seperti tatasusunan (dipanggil "senarai" dalam Python).
Jadi kita boleh buat ini...
places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia'] for place in places: print(place) print("...and back!")
Kita dapat ini...
Nashville Norway Bonaire Zimbabwe Chicago Czechoslovakia ...and back!
Python dalam gelungnya Di sana ialah satu lagi helah kecil yang unik: klausa else
! Selepas gelung selesai, jika tidak menemui pernyataan , break
akan dijalankan. Walau bagaimanapun, jika anda mengganggu gelung secara manual, ia else
melangkau sepenuhnya. else
places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia'] villain_at = 'Mali' for place in places: if place == villain_at: print("Villain captured!") break else: print("The villain got away again.")Memandangkan "Mali" tiada dalam senarai, kami melihat mesej "Penjahat melarikan diri lagi." Walau bagaimanapun, jika kita menukar nilai daripada
kepada villain_at
, kita akan melihat "Penjahat ditangkap!" Norway
gelung. Jika anda sedang mencari cara untuk menggelung seperti ini, Python biasa ialah menggunakan do...while
dengan while True:
dalaman bersyarat, seperti yang kami tunjukkan sebelum ini. break
bekas atau struktur data yang menyimpan data. Kami tidak akan membincangkan mana-mana daripada mereka secara mendalam, tetapi saya ingin melihat dengan pantas bahagian yang paling penting:
senaraisenarai ialah jujukan boleh ubah (sebenarnya tatasusunan). Ia ditakrifkan dengan kurungan segi empat sama dan anda boleh mengakses elemennya mengikut indeks. [ ]
foo = [2, 4, 2, 3] print(foo[1]) >>> 4 foo[1] = 42 print(foo) >>> [2, 42, 2, 3]</code></p>Walaupun tiada keperluan teknikal yang ketat untuknya, konvensyen biasa ialah senarai hanya mengandungi unsur-unsur jenis yang sama ("homogen"). <p data-id="p838747a-P9ZA1elg"></p>tuple<h4 id="h7" data-id="h7f20189-K6dPB2JE"></h4>tuple ialah jujukan yang tidak berubah. Sebaik sahaja anda mentakrifkannya, anda <p data-id="p838747a-2knt7nOR">secara teknikal<em> tidak boleh mengubahnya (ingat semula maksud ketakbolehubahan sebelumnya). Ini bermakna selepas mentakrifkan tupel, anda tidak boleh menambah atau mengalih keluar elemen daripada tupel. </em></p>Tuple ditakrifkan dalam kurungan <p data-id="p838747a-ZzF8EUCk"> dan anda boleh mengakses elemennya mengikut indeks. <code>( )</code></p> <pre class="brush:php;toolbar:false">foo = (2, 4, 2, 3) print(foo[1]) >>> 4 foo[1] = 42 >>> TypeError: 'tuple' object does not support item assignmentTidak seperti senarai, konvensyen standard membenarkan tupel mengandungi unsur jenis yang berbeza ("heterogen").
set是一个无序的可变集合,保证没有重复。记住“无序”很重要:不能保证单个元素的顺序!
一个集合在花括号中定义{ }
,但如果你想要一个空集合,你可以使用foo = set()
, 或者foo = {}
创建一个空的dict
. 你不能通过索引访问它的元素,因为它是无序的。
foo = {2, 4, 2, 3} print(foo) >>> {2, 3, 4} print(foo[1]) >>> TypeError: 'set' object does not support indexing
对于要添加到集合中的对象,它也必须是可散列的(hash)。一个对象是可散列的,如果:
它定义了方法__hash__(),该方法将哈希值(hash)作为整数返回。(见下文)
它定义了__eq__()比较两个对象的方法。
对于同一个对象(值),一个有效的散列值(hash)应该总是相同的,并且它应该是合理的唯一的,因此另一个对象返回相同的散列是不常见的。(两个或多个具有相同哈希值的对象称为哈希冲突,它们仍然会发生。)
dict(字典)是键值数据结构。
它在花括号中定义{ }
,:
用于分隔键和值。它是无序的,所以你不能通过索引访问它的元素;但是你可以通过[ ]
加键值访问元素。
foo = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4} print(foo['b']) >>> 2 foo['b'] = 42 print(foo) >>> {'a': 1, 'b': 42, 'c': 3, 'd': 4}
只有可散列的对象可以用作字典键。(有关set
哈希性的更多信息,请参阅官网的部分。)
除了基础之外,Python 还提供了额外的容器/数据结构。可以在内置模块collections中找到它们。
有一个重要的 Python 语法我们还没有讨论过,但很快就会派上用场。我们可以将容器中的每个元素分配给一个变量!这称为拆包。
当然,我们需要确切地知道我们要拆包多少才能结束,否则我们会得到一个ValueError
的异常。
让我们看一个使用tuple元组的基本示例。
fullname = ('Carmen', 'Sandiego') first, last = fullname print(first) >>> Carmen print(last) >>> Sandiego
看第二行代码,我们可以列出多个要分配的变量,用逗号分隔。Python 将拆分等号右侧的容器,将每个值按从左到右的顺序分配给一个变量。
注意:记住,set
是无序的!虽然你可以在技术上使用集合来执行此操作,但你无法确定将什么值分配给什么变量。不保证按顺序进行分配,集合的值的分配顺序通常是偶然的!
Python 提供了一个关键字in
,用于检查是否在容器中找到了特定元素。
places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia'] if 'Nashville' in places: print("Music city!")
这适用于许多容器,包括列表、元组、集合,甚至是字典键(但不是字典值)。
如果你希望你的自定义类之一支持in
运算符,你只需要定义__contains__(self, item)
方法,它应该返回True
or False
。
Python 的循环是配合我之前提到的迭代器一起使用。前面提到的数据结构都是是可以使用迭代器迭代的对象。
好的,让我们从头开始。Python 容器对象,例如 list
,也是一个可迭代对象,因为它的__iter__()
方法,返回一个迭代器对象。
方法__next__()
也是一个迭代器,在容器迭代器的情况下,返回下一项。即使是无序的容器,例如set()
,也可以使用迭代器进行遍历。
当__next__()
不能返回任何其他内容时,它会抛出一个名为StopIteration的特殊异常。这可以使用try...except
捕获异常。
让我们再看一下for
遍历 list
的循环,例如...
dossiers = ['The Contessa', 'Double Trouble', 'Eartha Brute', 'Kneemoi', 'Patty Larceny', 'RoboCrook', 'Sarah Nade', 'Top Grunge', 'Vic the Slick', 'Wonder Rat'] for crook in dossiers: print(crook)
dossiers
是一个list
对象,它是一个可迭代的对象。当 Python 到达for
循环时,它会做三件事:
调用iter(dossiers)
,依次执行dossiers.__iter__()
。这将返回一个我们将调用的迭代器对象list_iter
。这个迭代器对象将被循环使用。
对于循环的每次迭代,它都会调用next(list_iter)
,执行list_iter.__next__()
并将返回的值分配给crook
。
如果迭代器抛出了特殊异常StopIteration
,则循环结束,退出。
while True:
如果我在循环中重写该逻辑可能会更容易理解......
list_iter = iter(dossiers) while True: try: crook = next(list_iter) print(crook) except StopIteration: break
如果你尝试这两个循环,你会发现它们做的事情完全相同!
了解__iter__()
,__next__()
和StopIteration
异常的工作原理后,你现在可以使自己的类可迭代!
注意:虽然将迭代器类与可迭代类分开定义都可以,但你不一定必须这样做!只要这两种方法都在你的类中定义,并且__next__()
行为适当,你就可以定义__iter__()
为return self
.
值得注意的是迭代器本身是可迭代的:它们有一个__iter__()
方法返回self
。
假设我们有一本想要使用的字典......
locations = { 'Parade Ground': None, 'Ste.-Catherine Street': None, 'Pont Victoria': None, 'Underground City': None, 'Mont Royal Park': None, 'Fine Arts Museum': None, 'Humor Hall of Fame': 'The Warrant', 'Lachine Canal': 'The Loot', 'Montreal Jazz Festival': None, 'Olympic Stadium': None, 'St. Lawrence River': 'The Crook', 'Old Montréal': None, 'McGill University': None, 'Chalet Lookout': None, 'Île Notre-Dame': None }
如果我们只想查看其中的每个项目,我们只需使用for
循环。所以,这应该有效,对吧?
for location in locations: print(location)
哎呀!这只向我们展示了键,而不是值。这并不是我们想要的,不是吗?
dict.__iter__()
返回一个dict_keyiterator
对象,该对象执行其类名的操作:它遍历键,但不遍历值。
要同时获取键和值,我们需要调用locations.items()
返回dict_items
对象。dict_items.iter()
返回 dict_itemiterator
,它将字典中的每个键值对作为元组返回。
旧版说明:如果你使用的是 Python 2,则应改为调用locations.iteritems()
。
还记得刚才,当我们谈到拆包的时候吗?我们将每一对键值作为一个元组并拆分成成两个变量。
for key, value in locations.items(): print(f'{key} => {value}')
打印出以下内容:
Parade Ground => None Ste.-Catherine Street => None Pont Victoria => None Underground City => None Mont Royal Park => None Fine Arts Museum => None Humor Hall of Fame => The Warrant Lachine Canal => The Loot Montreal Jazz Festival => None Olympic Stadium => None St. Lawrence River => The Crook Old Montréal => None McGill University => None Chalet Lookout => None Île Notre-Dame => None
现在我们可以处理数据了。例如,我想在另一个字典中记录重要信息。
information = {} for location, result in locations.items(): if result is not None: information[result] = location # Win the game! print(information['The Loot']) print(information['The Warrant']) print(information['The Crook']) print("Vic the Slick....in jaaaaaaaaail!")
这将找到 Loot、Warrant 和 Crook,并按正确顺序列出它们:
Lachine Canal Humor Hall of Fame St. Lawrence River Vic the Slick....in jaaaaaaaaail!
我之前已经提到你可以制作自己的迭代器和迭代器,但现在来实现它!
想象一下,我们想方便保留一个代理列表,以便我们始终可以通过代理编号来识别它们。但是,有些代理是我们不能谈论的。我们可以通过将代理 ID 和名称存储在字典中,然后维护分类代理列表来轻松完成此操作。
注意:请记住,在我们对类的讨论中,Python 中实际上没有私有变量这样的东西。如果你真的打算保密,请使用行业标准的加密和安全实践,或者至少不要将你的 API 暴露给任何 VILE 操作员。;)
对于初学者,这是该类的基本结构:
class AgentRoster: def __init__(self): self._agents = {} self._classified = [] def add_agent(self, name, number, classified=False): self._agents[number] = name if classified: self._classified.append(name) def validate_number(self, number): try: name = self._agents[number] except KeyError: return False else: return True def lookup_agent(self, number): try: name = self._agents[number] except KeyError: name = "<NO KNOWN AGENT>" else: if name in self._classified: name = "<CLASSIFIED>" return name
我们可以继续测试一下,只是为了后续:
roster = AgentRoster() roster.add_agent("Ann Tickwitee", 2539634) roster.add_agent("Ivan Idea", 1324595) roster.add_agent("Rock Solid", 1385723) roster.add_agent("Chase Devineaux", 1495263, True) print(roster.validate_number(2539634)) >>> True print(roster.validate_number(9583253)) >>> False print(roster.lookup_agent(1324595)) >>> Ivan Idea print(roster.lookup_agent(9583253)) >>> <NO KNOWN AGENT> print(roster.lookup_agent(1495263)) >>> <CLASSIFIED>
太好了,这完全符合预期!现在,如果我们希望能够遍历整个字典怎么办。
但是,我们不想直接访问roster._agents
字典,因为这将忽略这个类的整个“分类”方面。我们如何处理?
正如我之前提到的,我们可以让这个类也作为它自己的迭代器,这意味着它有一个__next__()
方法。在这种情况下,我们只会 return self
。但是,这里是超简单Python教程,所以让我们跳过烦人步骤,简化内容,实际创建一个单独的迭代器类。
在这个例子中,我实际上将字典变成了一个元组列表,这将允许我使用索引。(请记住,字典是无序的。)我还将计算出有多少代理未分类。当然,所有这些逻辑都属于该__init__()
方法:
class AgentRoster_Iterator: def __init__(self, container): self._roster = list(container._agents.items()) self._classified = container._classified self._max = len(self._roster) - len(self._classified) self._index = 0
要成为迭代器,类必须有__next__()
方法;这是唯一的要求!请记住,一旦我们没有更多数据要返回,该方法就需要抛出StopException
异常。
我将定义AgentRoster_Iterator
的__next__()
方法如下:
class AgentRoster_Iterator: # ...snip... def __next__(self): if self._index == self._max: raise StopIteration else: r = self._roster[self._index] self._index += 1 return r
现在我们返回到AgentRoster
类,我们需要在其中添加一个__iter__()
返回迭代器对象的方法。
class AgentRoster: # ...snip... def __iter__(self): return AgentRoster_Iterator(self)
只需要一点点操作,现在我们的AgentRoster
类的行为与循环的预期完全一样!这段代码如下...
roster = AgentRoster() roster.add_agent("Ann Tickwitee", 2539634) roster.add_agent("Ivan Idea", 1324595) roster.add_agent("Rock Solid", 1385723) roster.add_agent("Chase Devineaux", 1495263, True) for number, name in roster: print(f'{name}, id #{number}')
产生的结果如下...
Ann Tickwitee, id #2539634 Ivan Idea, id #1324595 Rock Solid, id #1385723
Atas ialah kandungan terperinci Cara menggunakan gelung Python dan iterator. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!