이전 장에서 우리는 특히 대규모 데이터 세트를 처리해야 할 때 훌륭한 도구인 반복기에 대해 배웠습니다. 그러나 Python에서 자신만의 반복자를 구축하는 것은 약간 번거롭고 시간이 많이 걸립니다. 반복자 프로토콜(__iter__() 및 __next__() 메서드)을 구현하는 새 클래스를 정의해야 합니다. 이 클래스에서는 변수의 내부 상태를 직접 관리하고 업데이트해야 합니다. 또한 __next__() 메서드에 반환할 값이 없으면 StopIteration 예외가 발생해야 합니다.
이를 구현하는 더 좋은 방법이 있나요? 대답은 '예'입니다! 이것은 Python의 생성기 솔루션입니다. 그것을 살펴보자.
자신만의 반복자를 더 효율적으로 구축하려면 Python에서 이에 대한 우아한 솔루션을 갖는 것이 좋습니다. Python에서 제공하는 생성기(Generator)를 사용하여 반복자를 쉽게 만들 수 있습니다. Generator를 사용하면 반복자처럼 동작하는 함수를 선언할 수 있습니다. 즉, for 루프에서 사용할 수 있습니다. 간단히 말해서, 생성기는 반복자 객체를 반환하는 함수입니다. 따라서 이것은 반복자를 만드는 간단한 방법이기도 합니다. 반복자를 생성할 때 필요한 모든 작업(반복 프로토콜 및 내부 상태 등)에 대해 생각할 필요가 없습니다. 생성기가 모든 작업을 처리하기 때문입니다.
다음으로, 한 단계 더 나아가 Python에서 제너레이터가 작동하는 방식과 정의 방법을 쉽게 배워보겠습니다.
이전 섹션에서 언급했듯이 생성기는 Python에서 특별한 유형의 함수입니다. 이 함수는 단일 값이 아니라 반복자 개체를 반환합니다. 생성기 함수에서 반환 값은 return 문 대신 Yield 문을 사용합니다. 간단한 생성기 함수는 아래와 같이 정의됩니다.
Code List Fragment-01
위 목록에서는 생성기 함수를 정의합니다. 이 함수는 return 키워드 대신 Yield 문을 실행합니다. Yield 문은 이 함수를 생성기로 만듭니다. 이 함수를 호출하면 반복자 객체가 반환(생성)됩니다. 생성기 호출을 살펴보겠습니다.
Code List Snippet-02
생성기 호출은 일반적으로 객체를 생성하고 생성기 함수를 호출하고 이를 변수에 할당하는 것과 유사합니다.
프로그램 실행 결과는 다음과 같습니다.
Yielding First Item A Yielding Second Item B Yielding Last Item C
애플리케이션 생성기 코드에서 firstGenerator() 함수를 호출합니다. 이는 생성기이며 반복자 개체를 반환합니다. 이 반복자의 이름을 myIter로 지정합니다. 그런 다음 이 반복자 객체에 대해 next() 함수를 호출합니다. 각 next() 호출에서 반복자는 각각의 순서에 따라 Yield 문을 실행하고 항목을 반환합니다.
규칙에 따라 이 생성기 함수에는 return 키워드가 포함되어서는 안 됩니다. 그렇게 하면 return 문이 함수를 종료하고 반복자 요구 사항이 충족되지 않기 때문입니다.
이제 for 루프를 사용하여 보다 실용적인 생성기를 정의해 보겠습니다. 이 예에서는 0부터 시작하여 주어진 최대 한도까지 일련의 숫자를 연속적으로 생성하는 생성기를 정의합니다.
코드 목록은 다음과 같습니다.
Code List Snippet-03
프로그램 실행 결과는 다음과 유사합니다.
0 1 2 3
위 목록에서는 정수를 생성하는 생성기 함수를 정의합니다. 0부터 주어진 숫자까지. 보시다시피, 항복 문은 for 루프 안에 있습니다. n 값은 연속적인 next() 호출에 자동으로 저장됩니다.
한 가지 주의할 점은 생성기를 정의할 때 반환 값은 반드시 항복 문이어야 한다는 것입니다. 이는 반환 문이 생성기에 나타날 수 없다는 의미는 아닙니다. None이 아닌 값을 반환하는 return 문은 일반적으로 호출자가 처리할 수 있도록 StopIteration 예외에 추가 정보를 추가하기 위해 생성기의 끝에 배치됩니다. 예는 다음과 같습니다.
Code List Fragment-04
다음은 예외 처리 없이 프로그램을 실행한 출력 결과이며, 이는 다음과 유사합니다.
99 100 Traceback (most recent call last): File "……", line 11, in <module> print(next(g)) StopIteration: 不支持大于100的数字生成!
프로그램에 예외가 발생하는 경우( try-Exception), 표시된 결과가 더 간결해졌습니다. 직접 실행해 보세요.
如果一个函数至少包含一个yield语句,那么它就是生成器函数。如果需要,还可以包含其他yield或return语句。yield和return关键字都将从函数中返回一些东西。
return和yield关键字之间的差异对于生成器来说非常重要。return语句会完全终止函数,而yield语句会暂停函数,保存它的所有状态,然后在后续的调用中继续执行。
我们调用生成器函数的方式和调用普通函数一样。但在执行过程中,生成器在遇到yield关键字时暂停。它将迭代器流的当前值发送到调用环境,并等待下一次调用。同时,它在内部保存局部变量及其状态。
以下是生成器函数与普通函数不同的关键点:
我们用一个简单的例子来演示普通函数和生成器函数之间的区别。在这个例子中,我们要计算前n个正整数的和。为此,我们将定义一个函数,该函数给出前n个正数的列表。我们将以两种方式实现这个函数,一个普通函数和一个生成器函数。
普通函数代码如下:
代码清单片段-05
运行程序输出结果类似如下:
49999995000000 Elapsed Time in seconds: 1.2067763805389404
在代码清单中,我们定义一个普通函数,它返回前n个正整数的列表。当我们调用这个函数时,它需要一段时间来完成执行,因为它创建的列表非常庞大。它还使用了大量内存来完成此任务。
现在让我们为相同的操作定义一个生成器函数来实现,代码清单如下:
代码清单片段-06
运行程序结果类似如下:
49999995000000 (生成器模式)Elapsed Time in seconds: 1.0013225078582764
正如在生成器清单中所见,生成器在更短的时间内完成相同的任务,并且使用更少的内存资源。因为生成器是一个一个地生成项,而不是返回完整的列表。
性能改进的主要原因(当我们使用生成器时)是值的惰性生成。这种按需值生成的方式,会降低内存使用量。生成器的另一个优点是,你不需要等到所有元素都生成后才开始使用它们。
有时候,我们需要简单的生成器来执行代码中相对简单的任务。这正是生成器表达式(Generator Expression)用武之地。可以使用生成器表达式轻松地动态创建简单的生成器。
生成器表达式类似于Python中的lambda函数。但要记住,lambda是匿名函数,它允许我们动态地创建单行函数。就像lambda函数一样,生成器表达式创建的是匿名生成器函数。
生成器表达式的语法看起来像一个列表推导式。不同之处在于,我们在生成器表达式中使用圆括号而不是方括号。请看示例:
运行结果类似如下:
49999995000000 (生成器模式)Elapsed Time in seconds: 1.0013225078582764
在上述清单中,我们在生成器表达式的帮助下定义了一个简单的生成器。下面是语法:cubes_gen = (i**3 for i in nums)。你可以在输出中看到生成器对象。正如所已经知的,为了能够在生成器中获取项,我们要么显式调用next()方法,要么使用for循环遍历生成器。接下来就打印cubes_gen对象中的项:
运行程序,遍历出的元素项结果是否和列表推导式一样。
我们再看一个例子。来定义一个生成器,将字符串中的字母转换为大写字母。然后调用next()方法打印前两个字母。代码示例如下:
运行输出结果如下:
M A
生成器是非常棒的工具,特别是当需要在相对有限的内存中处理大型数据时。以下是在Python中使用生成器的一些主要好处:
1)内存效率:
假设有一个返回结果非常大序列的普通函数。例如,一个包含数百万项的列表。你必须等待这个函数完成所有的执行,并将整个列表返回给你。就时间和内存资源而言,这显然是低效的。另一方面,如果你使用生成器函数,它将一个一个地返回项,你将有机会继续执行下一行代码。而不需要等待函数执行列表中的所有项。因为生成器一次只给你一项。
2)延迟计算:
生成器提供了延迟(惰性)计算求值的功能。延迟计算是在真正需要值时计算值,而不是在实例化时计算值。假设你有一个大数据集要计算,延迟计算允许你在整个数据集仍在计算生成中可立即开始使用数据。因为如果使用生成器,则不需要整个数据集。
3)易实现和可读性:
生成器非常容易实现,并且提供了好的代码可读性。记住,如果你使用生成器,你不需要担心__iter__()和__next__()方法。你所需要的只是函数中一个简单的yield语句。
4)处理无限流:
当你需要表示无限的数据流时,生成器是非常棒的工具。例如,一个无限计数器。理论上,你不能在内存中存储无限流的,因为你无法确定存储无限流需要多少的内存大小。这是生成器真正发挥作用的地方,因为它一次只产生一项,它可以表示无限的数据流。它不需要将所有的数据流存储在内存中。
主要介绍了生成器相关知识,用于更好的自定义迭代器。内容包括何为生成器?如何自定义生成器以及和普通函数的关键区别?如何实现生成器表达式?并总结了生成器的有点。通过这篇文章,相信你能更轻松高效的掌握Python常规的生成器方方面面。
위 내용은 Python 프로그래밍: 생성기와 표현식을 얻는 방법은 무엇입니까? 와서 요리해 보세요!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!