Liệt kê vs lặp python

Khi tìm hiểu cách sử dụng các kiểu dữ liệu có nhiều phần tử như array, list, v. v. trong các ngôn ngữ lập trình hiện đại, chúng ta thường gặp các từ khóa như Iterable, Iterator, Enumerator… Dù rằng các khái niệm do các từ khóa này đưa ra không phải là phức tạp, nhưng đôi khi chúng sẽ gây ra “ . Vì vậy, chúng ta sẽ tìm hiểu các khái niệm này một cách chi tiết trong bài viết này

Chúng ta sẽ bắt đầu với một vấn đề đầu vào môn học. Nếu được yêu cầu thiết lập một đoạn mã để duyệt tuần tự động thông qua mọi phần tử trong một tập hợp nhiều phẩn tử, bạn sẽ làm thế nào?

Với Python, đây là công việc rất đơn giản, chúng ta sẽ sử dụng vòng lặp như sau

1

2

3

ngày = ["Thứ Hai", ngày = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}

>>> i = 0

>>> while i > iterator = iter[>> iterator2 = iter[>> iterator is iterator2

Thật

Điều này dẫn đến một số kết quả khá thú vị mà chúng ta sẽ không thảo luận trong bài viết này. Chúng ta sẽ trở lại vấn đề này trong một bài viết tương lai

Iterator Giao thức

Giao thức iterator thật ra chỉ là một cách gọi hoa mỹ cho một khái niệm đơn giản là cách làm việc của iterable trong Python

Từ góc độ của Python, chúng ta có thể định nghĩa iterable và iterator theo cách làm như sau

Iterable object

  • Có thể truyền cho phương thức iter[] để trả về một iterator tương ứng

Iterator các đối tượng

  • Có thể truyền cho phương thức next[] để trả về phần tử tiếp theo trong nhóm hoặc ngoại lệ StopIteration nếu không có phần tử nào
  • Trả về chính nó khi được truyền cho phương thức iter[]

Định nghĩa này cũng đúng theo chiều ngược lại, có nghĩa là

  • Bất kỳ đối tượng nào có thể được truyền theo phương thức iter[] mà không gây ra lỗi sẽ là một lần lặp
  • Bất kỳ đối tượng nào có thể được truyền theo phương thức next[] mà không gây ra lỗi [ngoại lệ StopIteration] sẽ là một iterator
  • Bất kỳ đối tượng nào được trả về chính nó khi được truyền cho phương thức iter[] cũng là một iterator
Vòng lặp bằng Iterator

Đến đây, với những gì chúng ta đã biết về iterable và iterator, chúng ta có thể tạo ra một kiểu vòng lặp tương tự như for nhưng lại không sử dụng đến vòng lặp for as trong ví dụ dưới đây

Vòng next[]2 dưới đây sẽ duyệt các tuần tự các phần tử trong một đối tượng có thể lặp lại và ra từng phần tử

1

2

3

4

5

6

7

8

9

def print_member[có thể lặp lại]:

    iterator = iter[iterable]

    trong khi Đúng.

        thử.

            mục = tiếp theo[iterator]

        ngoại trừ Dừng lặp lại.

            break  # Iterator đã cạn kiệt. dừng vòng lặp

        khác.

            in[mục]

Chúng ta có thể gọi hàm này với bất kỳ đối tượng iterable nào và duyệt qua các phần tử chứa trong nó

1

2

3

4

>>> print_member[{1> từ lớp học nhập Phòng học

>>> từ trình tạo lớp học nhập ClassRoomIterator

>>> lớp học = Phòng học[>> lớp học. add_student[['Tom', 'Susan', 'Harry', 'Flare']]

>>> cho học sinh trong lớp học . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . :

.. .      in[sinh viên]>>

Kết quả đúng như chúng ta mong đợi. loop iterable forwill duyệt qua từng phần tử trong đối tượng iter[]1 và in ra tên của từng phần tử đó [cũng là tên của các học sinh trong lớp] theo đúng nghĩa của iterable

Máy phát điện là gì?

Như vậy chúng ta đã hiểu được các định nghĩa về iterable, iterator và cách thức hoạt động của vòng lặp cho và có thể yên tâm khi sử dụng các đối tượng này rồi phải không? . Bây giờ bạn hãy thử cấu hình điều gì sẽ xảy ra nếu bạn sử dụng vòng lặp để đọc một tệp văn bản theo định dạng csv vào một danh sách như trong ví dụ sau

Read file csv theo từng dòng

1

2

3

4

5

6

7

csv_gen = csv_reader["a_csv_file. csv"]

row_count = 0

 

cho hàng trong csv_gen.

    row_count += 1

 

in[f"Số hàng là {row_count}"]

With the method csv_reader was known as after

Phương thức csv_reader[]

1

2

3

4

def csv_reader[file_name]:

    tệp = mở[file_name]

    kết quả = tệp. đọc[]. tách["\n"]

    trả về kết quả

Phương thức này sẽ đọc tệp và trả về một danh sách với mỗi phần tử là một dòng trong tệp. Nó sẽ làm việc tốt với các tập tin nhỏ. Tuy nhiên, khi chạy đoạn mã hành động này với một tệp có kích thước rất lớn, bạn sẽ thấy kết quả như sau

1

2

3

4

5

6

7

8

Truy nguyên [hầu hết cuộc gọi gần đây last]:

  Tệp "naive_read. py", dòng 22, in

    chính[]

  Tệp "naive_read. py", dòng 13, in main

    csv_gen = csv_reader["a_csv_file.csv"]

  Tệp "naive_read. py ", dòng 6, in csv_reader

    kết quả = tệp. đã đọc[]. tách["\n"]

Lỗi bộ nhớ

Điều gì đã xảy ra? . Tuy nhiên, khi next[]8 được gọi, nó sẽ đọc tất cả nội dung của tệp cùng lúc để đưa vào danh sách kết quả dẫn đến lỗi không đủ bộ nhớ

Để giải quyết vấn đề này, Python cung cấp một giải pháp khá đơn giản, chúng ta chỉ cần sửa đổi lại phương thức next[]9 như sau

1

2

3

def csv_reader[file_name]:

    cho hàng trong mở[file_name, "r"]:

        sản lượng hàng

Với sự thay đổi này, chúng ta mở tệp, lần lượt đọc các dòng trong tệp và mỗi khi đọc một dòng, chúng ta “nhường lại” [yield] dòng đó. Khi chạy đoạn mã mới này, chúng tôi sẽ nhận được kết quả tương tự như sau và không có lỗi phát sinh

1

Hàng số lượng 84286214

Như vậy chính xác là chúng ta đã làm gì với sự thay đổi trên? . Phiên bản mới của hàm sẽ mở tệp, tuần tự đi qua các dòng trong tệp và trả về mỗi lần một dòng thay vì tất cả các dòng cùng lúc

Hàm tạo được giới thiệu từ PEP 255. Các hàm thuộc loại này có tác dụng như các trình vòng lặp nhưng có thể được xây dựng với cú pháp đơn giản hơn nhiều so với các trình vòng lặp. Và cũng giống như các iterator, các giá trị do trình tạo hàm trả về sẽ được đánh giá theo phương pháp đánh giá trì hoãn [đánh giá lười biếng]. Đánh giá lười biếng là một kỹ thuật cho phép các giá trị trả về của các hàm chỉ được tạo ra khi chúng được sử dụng đến chứ không phải ở thời điểm hàm được gọi. Một lợi thế của kỹ thuật này là iterator chỉ trả về mỗi lần một phần tử và không lưu các phần tử này vào bộ nhớ trong quá trình duyệt. Điều này cho phép iterator duyệt qua các nhóm phần tử có số lượng rất lớn mà không bị ràng buộc về giới hạn bộ nhớ. Với Python, phương pháp này cho phép các iterator hoạt động ngay cả với các nhóm phần tử có độ dài không giới hạn

Các phương pháp tạo ra generator

Để tạo ra các trình tạo, chúng ta có hai phương pháp

  • Sử dụng trình tạo hàm. Để tạo ra một trình tạo hàm, chúng ta chỉ cần thay thế từ khóa iter[]0 của hàm đó bằng từ khóa iter[]1 như ví dụ ở phần trên. Ví dụ như hàm reverse_str[] để trả về một chuỗi theo công thức đảo ngược trong ví dụ dưới đây

1

2

3

4

def reverse_string[inp_str]:

    độ dài = len[inp_str]

    cho i trong phạm vi[length - 1,-1,-1]:

        sản lượng inp_str[i]

  • Sử dụng trình tạo biểu thức [biểu thức trình tạo]. Các trình tạo biểu thức được giới thiệu từ PEP 289. Các biểu thức này sẽ tạo ra các hàm ẩn danh [hàm tạo ẩn danh] – là các hàm được sử dụng mà không cần định nghĩa trước – tương tự như các hàm ẩn danh được tạo ra từ các biểu thức lambda. Cú pháp của các trình tạo biểu thức cũng gần giống như cú pháp của việc hiểu danh sách trong Python nhưng sử dụng dấu trích vòng [[]] thay vì dấu trích khung [[]] như trong ví dụ sau

1

2

3

4

5

6

7

8

9

10

11

12

13

14

'''Khởi tạo danh sách '''

my_list = [1, 3, 6, 10]

 

'''

Sử dụng hiểu danh sách để tạo một danh sách mới với các phần tử là bình phương của các phần tử trong my_list

đầu ra. [1, 9, 36, 100]

'''

[x**2 for x in my_list]

 

'''

Tương tự như trên nhưng sử dụng trình tạo biểu thức

Output:

'''

[x**2 for x in my_list]

Lợi ích của việc sử dụng máy phát điện
  • Build build. vì vậy với các trình vòng lặp, mã để tạo ra các trình tạo một cách đơn giản và chính xác hơn mặc dù có cùng mục đích. Ví dụ như mã để tạo ra một dãy số dư thừa của 2 như trong ví dụ sau

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# Use Iterator

lớp PowerOfTwo.

    def __init__[self, max = 0]:

        bản thân. tối đa = tối đa

 

    def __iter__[self]:

        bản thân. n = 0

        trả về chính mình

 

    def __next__[self]:

        nếu chính mình. n > bản thân. tối đa.

            nâng cao Dừng lại

 

        kết quả = 2 ** self.n

        bản thân. n += 1

        trả về kết quả

 

# Sử dụng Trình tạo

def power_of_two_generator[max = 0]:

    n = 0

    trong khi n

Chủ Đề