Tại sao bạn nên sử dụng Python Generator

2148

Generator đã là một phần quan trọng của Python kể từ khi được giới thiệu với PEP 255.

Generator function cho phép bạn tuyên bố một function hoạt động giống như một iterator.

Chúng cho phép các lập trình tạo ra một iterator một cách nhanh chóng và dễ dàng.

Iterator là gì, bạn có thể yêu cầu?

Iterator là một đối tượng có thể lặp lại (looped). Nó được sử dụng để phản ánh một container dữ liệu để làm cho nó hoạt động giống như một đối tượng iterable. Có thể bạn đã sử dụng một vài đối tượng có thể lặp lại mỗi ngày: các string, list và từ điển để đặt tên cho một số.

Một iterator được định nghĩa bởi một class thực hiện các Iterator Protocol. Giao thức này chỉ ra hai phương pháp trong class: __iter__ và __next__.

Quay lại. Tại sao bạn thậm chí muốn tạo ra iterators?

Tiết kiệm không gian bộ nhớ

Các iterator không tính toán giá trị của mỗi mục khi khởi tạo. Chúng chỉ tính toán nó khi bạn yêu cầu nó. Đây được gọi là lazy evaluation.

Lazy evaluation thực sự hữu ích khi bạn có một bộ dữ liệu cực lớn để tính toán. Nó cho phép bạn sử dụng dữ liệu ngay lập tức, trong khi cả bộ dữ liệu đang được tính.

Giả sử chúng ta muốn nhận được tất cả các số nguyên tố nhỏ hơn một số cực đại.

Trước tiên chúng ta xác định hàm kiểm tra nếu một số là số nguyên tố:

def check_prime(number):
    for divisor in range(2, int(number ** 0.5) + 1):
        if number % divisor == 0:
            return False
    return True

Sau đó, chúng ta định nghĩa class bao gồm phương thức __iter__  và __next__ :

class Primes:
    def __init__(self, max):
        self.max = max
        self.number = 1
    def __iter__(self):
        return self
    def __next__(self):
        self.number += 1
        if self.number >= self.max:
            raise StopIteration
        elif check_prime(self.number):
            return self.number
        else:
            return self.__next__()

Primes được khởi tạo với giá trị lớn nhất. Nếu số nguyên lớn tiếp theo lớn hơn mức max, iterator sẽ tăng một ngoại lệ StopIteration, kết thúc Iterator.

Khi chúng ta yêu cầu các element tiếp theo trong Iterator, nó sẽ tăng number một đơn vị và kiểm tra xem đó có là một số nguyên tố. Nếu không, nó sẽ gọi __next__ một lần nữa cho đến khi number đó là số nguyên tố. Một khi đạt được, Iterator trả về số.

Bằng cách sử dụng một Iterator, chúng ta không tạo ra một danh sách các số nguyên tố trong bộ nhớ của chúng ta. Thay vào đó, chúng tôi sẽ tạo ra số nguyên tố tiếp theo mỗi khi được yêu cầu.

Hãy thử nó:

primes = Primes(100000000000)
print(primes)
for x in primes:
    print(x)
---------
<__main__.Primes object at 0x1021834a8>
2
3
5
7
11
...

Mỗi lần lặp của đối tượng Primes gọi __next__ để tạo ra số nguyên tố tiếp theo.

Iterator chỉ có thể được lặp lại qua một lần. Nếu bạn cố lặp lại primes một lần nữa, sẽ không có giá trị nào được trả về và điều này hoạt động giống như một danh sách trống rỗng.

Bây giờ chúng ta đã biết iterator là gì và làm thế nào để tạo ra chúng, tiếp theo là về generator.

Generator

Nhớ lại rằng generator function cho phép chúng ta tạo ra các iterator theo một cách đơn giản hơn.

Generator giới thiệu câu lệnh yield cho Python. Nó hoạt động giống như return vì nó trả về một giá trị.

Vậy là chúng ta đã tiết kiệm được trạng thái của function. Trong lần tiếp theo function được gọi, vẫn tiếp tục từ nơi dừng lại, với cùng các giá trị biến mà nó đã có trước khi yielding.

Nếu chúng ta chuyển  Primes iterator thành generator, nó sẽ giống như sau

def Primes(max):
    number = 1
    while number < max:
        number += 1
        if check_prime(number):
            yield number
primes = Primes(100000000000)
print(primes)
for x in primes:
    print(x)
---------
<generator object Primes at 0x10214de08>
2
3
5
7
11
...

Bây giờ đó là pythonic! Liệu ta có thể làm tốt hơn không?

Vậy, chúng ta có thể sử dụng Generator Expressions, được giới thiệu với PEP 289.

Đây là danh sách comprehension equivalent của generator. Nó hoạt động chính xác giống như comprehension, nhưng biểu thức được đặt trong() và trái ngược với [].

Các biểu thức sau đây có thể thay thế generator function ở trên:

primes = (i for i in range(2, 100000000000) if check_prime(i))
print(primes)
for x in primes:
    print(x)
---------
<generator object <genexpr> at 0x101868e08>
2
3
5
7
11
...

Đây là khả năng của generator bằng Python.

Tóm lại

  • Generator cho phép bạn tạo các iterator theo cách rất pythonic.
  • Iterator cho phép lazy evaluation, chỉ tạo ra element tiếp theo của một đối tượng có thể lặp lại khi được yêu cầu. Điều này hữu ích đối với bộ dữ liệu cực lớn.
  • Iterator và generator chỉ có thể lặp lại được một lần.
  • Generator Function tốt hơn Iterator.
  • Generator Expressions tốt hơn các Iterator (đối với các trường hợp đơn giản).

Nguồn: TopDev via Freecodecamp

Báo cáo bài viết vi phạm bản quyền>>