>  기사  >  백엔드 개발  >  Python 루프 및 반복자를 사용하는 방법

Python 루프 및 반복자를 사용하는 방법

王林
王林앞으로
2023-05-20 15:04:11922검색

루프 개요

Python에는 대부분의 언어와 마찬가지로 whilefor라는 두 가지 기본 루프가 있습니다. whilefor

while

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语句引起的。

for

来自 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 = [&#39;Nashville&#39;, &#39;Norway&#39;, &#39;Bonaire&#39;, &#39;Zimbabwe&#39;, &#39;Chicago&#39;, &#39;Czechoslovakia&#39;]
for place in places:
    print(place)

print("...and back!")

我们得到这个...

Nashville
Norway
Bonaire
Zimbabwe
Chicago
Czechoslovakia
...and back!
for...else

Python 在其循环中还有另一个独特的小技巧:else子句!循环完成后,没有遇到break语句,就会运行else。但是,如果手动中断循环,它将else完全跳过。

places = [&#39;Nashville&#39;, &#39;Norway&#39;, &#39;Bonaire&#39;, &#39;Zimbabwe&#39;, &#39;Chicago&#39;, &#39;Czechoslovakia&#39;]
villain_at = &#39;Mali&#39;

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_atNorway,我们将看到“Villain captured!” ,而看不到了“The villain got away again.”。

存在do...whiel吗?

Python 没有do...while循环。如果你正在寻找这样的循环方式,典型的 Python 是使用while True:带内部break条件的 ,就像我们之前演示的那样。

数据结构(容器)

Python 有许多保存数据的容器或数据结构。我们不会深入讨论其中的任何一个,但我想快速浏览一下最重要的部分:

list

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> 루프는 매우 기본적입니다. </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

단서가 없음이 경우 루프 조건이 True 코드로 평가되는 한 루프가 실행됩니다. .

Python에는 몇 가지 유용한 키워드도 있습니다. break는 루프를 즉시 중지하고 continue는 다음 반복으로 점프합니다. 루프의.

break의 가장 유용한 측면 중 하나는 사용자가 유효한 입력을 제공할 때까지 동일한 코드를 실행하려는 경우입니다. 🎜

foo = {2, 4, 2, 3}

print(foo)
>>> {2, 3, 4}

print(foo[1])
>>> TypeError: 'set' object does not support indexing

break 문을 만나면 루프를 종료합니다. 물론 위의 예는 더 복잡한 예이지만 요점을 보여줍니다. 게임 루프에서 while True:가 사용되는 경우도 자주 볼 수 있습니다. 🎜

참고: 어떤 언어에서든 루프를 사용해 본 적이 있다면 이미 무한 루프(무한 루프)에 익숙할 것입니다. > . 이는 일반적으로 True로 평가되는 while 조건에 의해 발생하며 루프에 break 문이 없습니다. 🎜

for

Java, C++ 또는 여러 유사한 ALGOL 스타일 언어에 익숙할 수 있습니다. 3자 for 루프: for i := 1 &lt 100; 당신은 어떨지 모르겠지만 처음 이런 상황에 직면했을 때 나는 놀랐습니다. 지금은 만족하지만 Python의 우아하고 단순함은 없습니다. 🎜<p data-id="p838747a-BvXJlVPb">Python의 <code>for 루프는 매우 다르게 보입니다. 위 의사 코드에 해당하는 Python 코드는... 🎜

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}

range()는 시퀀스를 반환하는 Python의 특수 "함수"입니다. (기술적으로는 전혀 함수가 아닙니다.) 🎜

이것이 Python의 인상적인 점입니다. Python은 iterable이라는 특별한 유형의 시퀀스를 반복합니다. 이에 대해서는 나중에 이야기하겠습니다. 🎜

가장 이해하기 쉬운 것은 배열(Python에서는 "목록"이라고 함)과 같은 순차적 데이터 구조를 반복할 수 있다는 것입니다. 🎜

그래서 우리는 이것을 할 수 있습니다... 🎜

fullname = ('Carmen', 'Sandiego')
first, last = fullname
print(first)
>>> Carmen
print(last)
>>> Sandiego

우리는 이것을 얻습니다... 🎜

places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia']

if 'Nashville' in places:
    print("Music city!")
for...else

Python의 루프에는 또 다른 독특한 작은 트릭이 있습니다: else 절 ! 루프가 완료된 후 no에서 break 문을 만나면 else가 실행됩니다. 그러나 루프를 수동으로 중단하면 else는 루프를 완전히 건너뜁니다. 🎜

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)

"Mali"가 목록에 없기 때문에 "악당이 또 도망쳤습니다."라는 메시지가 표시됩니다. 하지만 villain_at의 값을 Norway로 변경하면 "Thevillain gotaway again" 대신 "Villain Capture!"가 표시됩니다. 🎜

...휠이 존재하나요?

Python에는 do...while 루프가 없습니다. 이러한 루프를 찾고 있다면 일반적인 Python은 앞서 설명했듯이 내부 break 조건과 함께 while True:를 사용하는 것입니다. 🎜

데이터 구조(컨테이너) 🎜

Python에는 데이터를 저장하는 컨테이너가 많이 있습니다. 또는 데이터 구조. 그 중 어떤 것도 깊이 논의하지는 않겠지만 가장 중요한 사항을 간단히 살펴보고 싶었습니다. 🎜

목록

목록은 가변 시퀀스(실제로는 배열)입니다. 🎜

대괄호 [ ]로 정의되며 색인을 통해 해당 요소에 액세스할 수 있습니다. 🎜

list_iter = iter(dossiers)
while True:
    try:
        crook = next(list_iter)
        print(crook)
    except StopIteration:
        break

엄격한 기술 요구 사항은 없지만 일반적인 관례는 목록에 동일한 유형("동종")의 요소만 포함된다는 것입니다. 🎜

튜플

튜플은 불변 시퀀스입니다. 일단 정의하면 기술적으로 변경할 수 없습니다(이전에 불변성의 의미를 기억하세요). 즉, 튜플을 정의한 후에는 튜플에서 요소를 추가하거나 제거할 수 없습니다. 🎜

튜플은 대괄호 ( ) 안에 정의되어 있으며 해당 요소에 인덱스로 액세스할 수 있습니다. 🎜

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
    }
🎜목록과 달리 표준 규칙에서는 튜플이 다양한 유형("이기종")의 요소를 포함할 수 있도록 허용합니다. 🎜

set

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)。一个对象是可散列的,如果:

  1. 它定义了方法__hash__(),该方法将哈希值(hash)作为整数返回。(见下文)

  2. 它定义了__eq__()比较两个对象的方法。

对于同一个对象(值),一个有效的散列值(hash)应该总是相同的,并且它应该是合理的唯一的,因此另一个对象返回相同的散列是不常见的。(两个或多个具有相同哈希值的对象称为哈希冲突,它们仍然会发生。)

dict

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是无序的!虽然你可以在技术上使用集合来执行此操作,但你无法确定将什么值分配给什么变量。不保证按顺序进行分配,集合的值的分配顺序通常是偶然的!

in

Python 提供了一个关键字in ,用于检查是否在容器中找到了特定元素。

places = ['Nashville', 'Norway', 'Bonaire', 'Zimbabwe', 'Chicago', 'Czechoslovakia']

if 'Nashville' in places:
    print("Music city!")

这适用于许多容器,包括列表、元组、集合,甚至是字典键(但不是字典值)。

如果你希望你的自定义类之一支持in运算符,你只需要定义__contains__(self, item)方法,它应该返回Trueor 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循环时,它会做三件事:

  1. 调用iter(dossiers),依次执行dossiers.__iter__()。这将返回一个我们将调用的迭代器对象list_iter。这个迭代器对象将被循环使用。

  2. 对于循环的每次迭代,它都会调用next(list_iter),执行list_iter.__next__()并将返回的值分配给crook

  3. 如果迭代器抛出了特殊异常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

위 내용은 Python 루프 및 반복자를 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제