In Python, like most languages, there are two basic loops: while
and for
.
while
Loops are very basic.
clue = None while clue is None: clue = searchLocation()
clue is None
In this case, the loop's code will be executed as long as the loop condition evaluates to True
.
In Python, we also have several useful keywords: break
Stop the loop immediately and continue
jump to the next one iteration of the loop.
#break
One of the most useful aspects is if we want to run the same code until the user provides valid input.
while True: try: age = int(input("Enter your age: ")) except ValueError: print(f"Please enter a valid integer!") else: if age > 0: break
Once we encounter the break
statement, we exit the loop. Of course, the above is a more complex example, but it demonstrates the point. You also often see while True:
used in game loops.
Note: If you have ever used loops in any language, then you are already familiar with infinite loops (infinite loops). This is usually caused by a while
condition evaluating to True
and no break
statement in the loop.
Coming from Java, C, or many similar ALGOL-style languages, you may be familiar with the three-party for
loop: for i := 1; i < ; 100; i := i 1
. I don't know about you, but when I first encountered this situation it freaked me out. I'm happy with it now, but it doesn't have the elegant simplicity of Python.
Python's for
loop looks very different. The Python code equivalent to the above pseudocode is...
for i in range(1,100): print(i)
range() is a special "function" in Python that returns a sequence. (Technically, it's not a function at all.)
This is the impressive thing about Python - it iterates over a special type of sequence called iterable, We'll get to that later.
By far the easiest to understand is that we can iterate over a sequential data structure, such as an array (called a "list" in Python).
So we can do this...
places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia'] for place in places: print(place) print("...and back!")
We get this...
Nashville Norway Bonaire Zimbabwe Chicago Czechoslovakia ...and back!
Python in its loop There is another unique little trick: the else
clause! After the loop is completed, if no encounters a break
statement, else
will be run. However, if you interrupt the loop manually, it else
skips completely.
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.")
Since "Mali" is not on the list, we see the message "The villain got away again." However, if we change the value villain_at
to Norway
, we will see "Villain captured!" but not "The villain got away again.".
Python does not have a do...while
loop. If you're looking for a way to loop like this, typical Python is to use while True:
with an internal break
condition, like we demonstrated earlier.
Python has many containers or data structures that hold data. We won't discuss any of them in depth, but I want to take a quick look at the most important parts:
list is a mutable sequence (actually an array).
It is defined with square brackets [ ]
, and you can access its elements by index.
foo = [2, 4, 2, 3] print(foo[1]) >>> 4 foo[1] = 42 print(foo) >>> [2, 42, 2, 3]
Although there are no strict technical requirements for it, the typical convention is that lists only contain elements of the same type ("homogeneous").
tuple is an immutable sequence. Once you define it, you technicallycan't change it (recall the previous meaning of immutability). This means that after defining a tuple, you cannot add or remove elements from the tuple.
A tuple is defined in brackets ( )
, and you can access its elements by index.
foo = (2, 4, 2, 3) print(foo[1]) >>> 4 foo[1] = 42 >>> TypeError: 'tuple' object does not support item assignment
Unlike lists, the standard convention allows tuples to contain elements of different types ("heterogeneous").
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
The above is the detailed content of How to use Python loops and iterators. For more information, please follow other related articles on the PHP Chinese website!