Heim > Artikel > Backend-Entwicklung > So verwenden Sie Python-Schleifen und Iteratoren
In Python gibt es, wie in den meisten Sprachen, zwei grundlegende Schleifen: while
und for
. while
和for
。
while
循环是非常基本的。
clue = None while clue is None: clue = searchLocation()
clue is None
在这种情况下,只要循环条件的计算结果为True
,就会执行循环的代码。
在 Python 中,我们还有几个有用的关键字:break
立即停止循环,同时continue
跳到循环的下一次迭代。
break
最有用的方面之一是如果我们想要运行相同的代码,直到用户提供有效的输入。
while True: try: age = int(input("Enter your age: ")) except ValueError: print(f"Please enter a valid integer!") else: if age > 0: break
一旦我们遇到该break
语句,我们就退出循环。当然,上面是一个较复杂的例子,但它证明了这一点。你还经常看到while True:
在游戏循环中使用。
注意:如果你曾经使用过任何语言的循环,那么你已经熟悉了无限循环(死循环)。这通常是由while
条件计算结果为True
并且循环中没有break
语句引起的。
来自 Java、C++ 或许多类似的 ALGOL 风格的语言,你可能熟悉三方for
循环:for i := 1; i < 100; i := i + 1
。 我不了解你,但当我第一次遇到这种情况时,它吓坏了我。我现在对它很满意,但它不具备 Python 的优雅简洁。
Python 的for
循环看起来大不相同。与上述伪代码等效的 Python 代码是...
for i in range(1,100): print(i)
range()是 Python 中一个特殊的“函数”,它返回一个序列。(从技术上讲,它根本不是一个函数。)
这是 Python 令人印象深刻的地方——它迭代了一种特殊类型的序列,称为iterable,我们稍后会谈到。
目前,最容易理解的是我们可以迭代一个顺序数据结构,比如一个数组(在 Python 中称为“列表”)。
因此,我们可以这样做...
places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia'] for place in places: print(place) print("...and back!")
我们得到这个...
Nashville Norway Bonaire Zimbabwe Chicago Czechoslovakia ...and back!
Python 在其循环中还有另一个独特的小技巧:else
子句!循环完成后,没有遇到break
语句,就会运行else
。但是,如果手动中断循环,它将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.")
由于“Mali”不在列表中,因此我们看到了“The villain got away again.”的消息。但是,如果我们将值更改villain_at
为Norway
,我们将看到“Villain captured!” ,而看不到了“The villain got away again.”。
Python 没有do...while
循环。如果你正在寻找这样的循环方式,典型的 Python 是使用while True:
带内部break
条件的 ,就像我们之前演示的那样。
Python 有许多保存数据的容器或数据结构。我们不会深入讨论其中的任何一个,但我想快速浏览一下最重要的部分:
list是一个可变序列(其实就是一个数组)。
它是用方括号定义的[ ]
,你可以通过索引访问它的元素。
foo = [2, 4, 2, 3] print(foo[1]) >>> 4 foo[1] = 42 print(foo) >>> [2, 42, 2, 3]<p data-id="p838747a-P9ZA1elg">尽管对它没有严格的技术要求,但典型的约定是列表只包含相同类型的元素(“同质”)。</p> <h4 id="h7" data-id="h7f20189-K6dPB2JE">tuple</h4> <p data-id="p838747a-2knt7nOR">tuple是一个不可变的序列。一旦你定义了它,你在<em>技术上</em>就不能改变它(回想一下之前不变性的含义)。这意味着在定义tuple后,你不能在tuple中添加或删除元素。</p> <p data-id="p838747a-ZzF8EUCk">一个tuple是在括号中定义的<code>( )</code></p> <h4 id="h2" data-id="h7f20189-64jaNOO6">while</h4> <p data-id="p838747a-iPSY8Xgn"><code>while</code>-Schleife ist sehr einfach. </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 assignment
clue is None
In diesem Fall wird die Schleife ausgeführt, solange die Schleifenbedingung als True
-Code ausgewertet wird .
In Python haben wir auch mehrere nützliche Schlüsselwörter: break
stoppt die Schleife sofort, während continue
zur nächsten Iteration springt der Schleife.
Einer der nützlichsten Aspekte von break
ist, wenn wir denselben Code ausführen möchten, bis der Benutzer eine gültige Eingabe macht. 🎜
foo = {2, 4, 2, 3} print(foo) >>> {2, 3, 4} print(foo[1]) >>> TypeError: 'set' object does not support indexing
Sobald wir auf die break
-Anweisung stoßen, verlassen wir die Schleife. Natürlich ist das obige Beispiel ein komplexeres Beispiel, aber es verdeutlicht den Punkt. Sie werden auch oft sehen, dass while True:
in Spielschleifen verwendet wird. 🎜
Hinweis: Wenn Sie jemals Schleifen in einer beliebigen Sprache verwendet haben, sind Sie bereits mit Endlosschleifen (Endlosschleifen)vertraut > . Dies wird normalerweise durch eine while
-Bedingung verursacht, die als True
ausgewertet wird und in der Schleife keine break
-Anweisung vorhanden ist. 🎜
Von Java, C++ oder vielen ähnlichen Sprachen im ALGOL-Stil sind Sie vielleicht vertraut mit der dreiteiligen for
-Schleife: for i := 1; i < Ich weiß nicht, wie es Ihnen geht, aber als ich diese Situation zum ersten Mal erlebte, hat sie mich erschreckt. Ich bin jetzt damit zufrieden, aber es hat nicht die elegante Einfachheit von Python. 🎜<p data-id="p838747a-BvXJlVPb">Pythons <code>for
-Schleife sieht ganz anders aus. Der Python-Code, der dem obigen Pseudocode entspricht, ist... 🎜
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}<p data-id="p838747a-n6zwS0Xl">range() ist eine spezielle „Funktion“ in Python, die eine Sequenz zurückgibt. (Technisch gesehen ist es überhaupt keine Funktion.) 🎜</p> <p data-id="p838747a-orFp4uou">Das ist das Beeindruckende an Python – es iteriert über einen speziellen Sequenztyp namens <strong>iterable</strong>, worüber wir später sprechen werden. 🎜</p> <p data-id="p838747a-SyZnpokO">Bei weitem am einfachsten zu verstehen ist, dass wir über eine sequentielle Datenstruktur iterieren können, beispielsweise ein Array (in Python „Liste“ genannt). 🎜</p> <p data-id="p838747a-T2HEQagz">Also können wir das schaffen... 🎜</p> <pre class="brush:php;toolbar:false">fullname = ('Carmen', 'Sandiego') first, last = fullname print(first) >>> Carmen print(last) >>> Sandiego
Wir bekommen das... 🎜
places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia'] if 'Nashville' in places: print("Music city!")
Python hat einen weiteren einzigartigen kleinen Trick in seinen Schleifen: else
Klausel ! Wenn no nach Abschluss der Schleife auf eine break
-Anweisung stößt, wird else
ausgeführt. Wenn Sie die Schleife jedoch manuell unterbrechen, wird sie von else
vollständig übersprungen. 🎜
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)
Da „Mali“ nicht auf der Liste steht, sehen wir die Meldung „Der Bösewicht ist wieder entkommen.“ Wenn wir jedoch den Wert von villain_at
in Norway
ändern, sehen wir „Bösewicht gefangen!“ anstelle von „Der Bösewicht ist wieder entkommen.“ 🎜
Python verfügt nicht über eine do...while
-Schleife. Wenn Sie nach einer solchen Schleife suchen, verwendet Python normalerweise while True:
mit einer internen break
-Bedingung, wie wir zuvor gezeigt haben. 🎜
Python verfügt über viele Container, die Daten speichern oder Datenstruktur. Wir werden keine davon ausführlich besprechen, aber ich wollte einen kurzen Blick auf die wichtigsten werfen: 🎜
list ist eine variable Sequenz (eigentlich ein Array). 🎜
Es wird mit eckigen Klammern [ ]
definiert und Sie können über den Index auf seine Elemente zugreifen. 🎜
list_iter = iter(dossiers) while True: try: crook = next(list_iter) print(crook) except StopIteration: break
Obwohl es dafür keine strengen technischen Anforderungen gibt, ist die typische Konvention, dass Listen nur Elemente des gleichen Typs („homogen“) enthalten. 🎜
Tupel ist eine unveränderliche Sequenz. Sobald Sie es definiert haben, können Sie es technisch gesehen nicht mehr ändern (erinnern Sie sich an die vorherige Bedeutung von Unveränderlichkeit). Das bedeutet, dass Sie nach der Definition eines Tupels keine Elemente mehr zum Tupel hinzufügen oder daraus entfernen können. 🎜
Ein Tupel wird in Klammern ( )
definiert und Sie können über den Index auf seine Elemente zugreifen. 🎜
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 }🎜Im Gegensatz zu Listen erlaubt die Standardkonvention, dass Tupel Elemente unterschiedlichen Typs („heterogen“) enthalten. 🎜
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
Das obige ist der detaillierte Inhalt vonSo verwenden Sie Python-Schleifen und Iteratoren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!