Điều khiển lặp trong python

bảo đảm. “Chào Minh Hoàng. ”. bảo gà. “Chào Bá Lộc. ”. Mẹ Út bảo. “Chào Minh Hoàng”. bảo gà. “Chào Mẹ Út” 🤣

Hôm nay anh sẽ cầm gương lên, dũng cảm xông pha ra trận chém con thú hai đầu, một cái đầu màu hồng tên là “Iterator“, một cái đầu màu xanh tên là “Generator“

Để mình kể bạn nghe, đây là câu chuyện có thật, từ chiếc ti vi, kênh Cartoon Network, chương trình hoạt hình “Biệt đội Titan xuất kích”. Hai bạn quái vật này bị nhốt trong một cái hộp thần bí, Raven đã phiêu lưu các bạn không mở được nó ra vì nó rất nguy hiểm, nhưng StarFire đã không có hứng thú tò mò và mở ra. Hai bạn này siêu dễ thương luôn, cùng vui chơi với các bạn nhỏ, nhưng khi hai bạn đó được StarFire Không Nhụy mà hôn cho một cái thì lập tức hai bạn biến hình thành một con quái vật có hai đầu đi khám phá thành phố. Sau đó, StarFire đã nói ra những lời đau lòng đến mức từ con quái vật hai đầu đến với hai bạn càng ngày càng thu bé lại và biến thành hai con nhỏ yêu buồn nôn nôn vì không được yêu thương nữa.

Câu chuyện vậy đó, khi mình sáng tác bài này tự sướng mình cũng thấy trùng hợp ghê, hai bạn “Iterator” và “Generator” này cũng dễ thương như vậy, nhưng cũng có thể biến thành quái vật khi mình chủ quan về các bạn . Còn mình mà đã hiểu á, thì sẽ biến lại thành hai bạn tiểu yêu xinh dễ cưng thôi

Cho nên là hôm nay, mình cùng quyết tâm chinh phục con quái thú hai đầu này nha. 👻 Ý quên, còn gặp cả sư phụ của bọn nhỏ nữa đấy

Bài blog này thuộc series “Khám phá Đại Bản Doanh Trăn”

(Hình ảnh được cung cấp bởi artemtation từ Pixabay)

Open start

Trong nội dung bài “Các công cụ điều khiển luồng dữ liệu”, mình đã biết cách lặp qua các phần tử của các kiểu dữ liệu là tập hợp nhiều phần tử trong đó, như chuỗi, danh sách, dict rồi, dùng cho phần_tử . Và mình cũng đã học cách sử dụng enumerate() để có thể vừa lặp vừa sử dụng chỉ mục số của phần tử, ví dụ

>>> a = ["Thanh", "has", "a", "blog"]
>>> for i, s in enumerate(a):
...     print("chuoi hien tai la:", s)
...     if i < len(a) - 1:
...             print("chuoi tiep theo la:", a[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: Thanh
chuoi tiep theo la: has
chuoi hien tai la: has
chuoi tiep theo la: a
chuoi hien tai la: a
chuoi tiep theo la: blog
chuoi hien tai la: blog
day la chuoi cuoi cung!
>>>

“a” is a list. Nếu a là một bộ thì sao nhỉ? . (Tìm hiểu thêm về các loại dữ liệu ở bài “Cấu trúc dữ liệu trong Python” nhé)

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>

Ôi nó bị sao thế nhỉ, lỗi “TypeError. ‘set’ object is not subscriptable” có nghĩa là kiểu dữ liệu “set” không hỗ trợ truy cập phần tử theo chỉ mục

Hiểu nôm na thì nếu a là list, nó có thể truy cập đến phần tử đầu tiên bằng chỉ số là 0 với a[0], còn b là set thì nó không có chỉ số index như vậy nên b[0] sẽ báo lỗi


Thế còn kiểu dữ liệu dict thì sao nhỉ?

>>> a = {"name": "Thanh", "age": 29}
>>> for i, key in enumerate(a):
...     print("tu khoa: ", key)
...     print(f"gia tri cua tu khoa {key} la: {a[key]}")
...     print(f"gia tri cua a tại index {i} la: {a[i]}")
...
tu khoa:  name
gia tri cua tu khoa name la: Thanh
Traceback (most recent call last):
  File "", line 4, in <module>
KeyError: 0
>>>

Úi, vậy là kiểu dữ liệu dict cũng không truy cập được theo chỉ mục index được

Do đó, trong Python the same as data type may be contain many element(string, list, set, dict,…) but you are back share doing two group

🥰 Nhóm dữ liệu tuần tự(sequence). chuỗi, danh sách, … cho phép mình truy cập qua các phần tử trong bộ sưu tập bằng chỉ mục, hay gọi là chỉ mục số, các bạn chỉ số này có chỉ mục được đánh dấu từ 0 đến len – 1(chiều dài của nó trừ

🥰 Nhóm dữ liệu tập hợp(bộ sưu tập). set, dict, … không truy cập được theo chỉ mục


Rồi sao nữa 😊 Hehe, rồi thì luật sinh ra là để lách luật đó mấy bạn 😅

Không muốn lướt luật tức thời là muốn duyệt có trình tự mấy kiểu tập hợp dữ liệu như set, dict trên, thì mình cần hiểu bản chất và cơ chế của luật này cái đã. Đây chính là sư phụ của hai bạn nhỏ yêu trên, tạm gọi là sư phụ iterable

Có thể lặp lại

Thực ra gối giờ mình từng gặp nhiều bạn là iterable rồi đó 👉 a và b của những ví dụ trên đều là iterable

Một đối tượng là iterable nghĩa là nó có thể lặp lại, hiểu nôm na là if a is iterable thì

👉 could itqua a been, tức là có thể viết *“for x in a” *

👉 call iter(a), will return iter

👉 a có phương thức __iter__ nếu cũng trả về một iterator hoặc đôi khi A có phương thức \getitem\ thuộc nhóm dữ liệu tuần tử có thể truy cập phần tử theo chỉ số chỉ số đã nói ở trên

Xem ví dụ iterable is list

>>> a = ["Thanh", "has", "a", "blog"]]
>>> a = ["BeautyOnCode", "blog"]
>>> for s in a:
...     print(s)
...
BeautyOnCode
blog
>>> iter(a)
<list_iterator object at 0x10b5dbd60>
>>> a.__dir__()
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__add__', '__mul__', '__rmul__', '__contains__', '__iadd__', '__imul__', '__new__', '__reversed__', '__sizeof__', 'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse', 'sort', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
>>> a.__iter__()
<list_iterator object at 0x10b5dbd60>
>>> a.__getitem__(0)
'BeautyOnCode'
>>>

Continue by b is a dict nè

>>> b = {"name": "Thanh", "age": 29}
>>> for x in b:
...     print(x)
...
name
age
>>> iter(b)
<dict_keyiterator object at 0x10b61cc20>
>>> b.__dir__()
['__repr__', '__hash__', '__getattribute__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__iter__', '__init__', '__len__', '__getitem__', '__setitem__', '__delitem__', '__contains__', '__new__', '__sizeof__', 'get', 'setdefault', 'pop', 'popitem', 'keys', 'items', 'values', 'update', 'fromkeys', 'clear', 'copy', '__reversed__', '__doc__', '__str__', '__setattr__', '__delattr__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__dir__', '__class__']
>>> b.__iter__()
<dict_keyiterator object at 0x10b61cc20>
>>> b.__getitem__(0)
Traceback (most recent call last):
  File "", line 1, in <module>
KeyError: 0

Bạn đã thấy sự khác nhau chưa, vậy điểm chung duy nhất của các iterable là có nhiều phần tử và cho phép mình bật qua chúng nó đó.

Tuy nhiên, số lượng phần tử trong iterable có thể là hữu hạn hoặc vô hạn, theo ví dụ trên thì chúng có số lượng hữu hạn. Giờ thì cũng đi qua một ví dụ với số lượng phần tử vô hạn nhé

Điều khiển lặp trong python

Trong ví dụ này, mình sử dụng hàm đếm từ itertools để tạo một bộ đếm là bội số của 3

Khi thực hiện vòng lặp này qua bộ đếm này với vòng lặp cho và trong các phần tử ra thì nó sẽ chạy mãi mãi cho đến khi mình tiếp tục chương trình hoặc mình phải thêm điều kiện dừng cho vòng lặp là khi n > 200 thì thoát khỏi

Với số lượng phần tử vô hạn như vậy thì mình không thể chuyển các bạn ấy về kiểu danh sách được. Không kể số lượng phần tử quá lớn sẽ gây hại cho bộ nhớ và hiệu suất của chương trình

Hehe, thực ra to test rồi, và nó kill luôn PI và tự thoát ra luôn 😅

>>> list(multiples_of_three)
[1]    5953 killed     python

Vì những nguyên nhân đó, sư phụ Iterable đã nhận một đệ tử đầu tiên, chính là bạn tiểu yêu màu hồng, bạn mà sau này biến thành quái thú màu hồng trong con quái thú hai đầu đó, bạn ấy tên là Iterator

Cùng xem bạn Iterator giúp giải quyết vấn đề trên ra sao nhé

Trình lặp

Thực hiện iterator là một khái niệm trong lĩnh vực khoa học máy tính đấy nhé. This is output body of Iterator from Wikipedia

Trong lập trình máy tính, iterator là một đối tượng cho phép lập trình viên duyệt qua một vùng chứa, đặc biệt là các danh sách

Bản dịch tạm thời là. Trong lĩnh vực khoa học máy tính, iterator là một đối tượng cho phép các nhà thiết lập có thể duyệt qua một vùng chứa dữ liệu, như danh sách

Còn lại trong Python, trình vòng lặp được định nghĩa trong Python wiki là

iterator là đối tượng có phương thức __next__, và phương thức này sẽ trả về phần tử tiếp theo của đối tượng, nếu đối tượng không còn phần tử nào để lặp lại thì nó sẽ báo lỗi StopIteration


Bạn đã thấy iterator lần nào chưa nhỉ?

Thực ra mình đã thấy bạn ấy khi mình gọi iter(a) ở ví dụ trên đấy, cùng xem mình gọi __next__ thì bạn ấy sẽ trả ra gì nhé

>>> a = ["BeautyOnCode", "blog"]
>>> x = iter(a)
>>> x
<list_iterator object at 0x10b5dbd60>
>>> x.__next__()
'BeautyOnCode'
>>> x.__next__()
'blog'
>>> x.__next__()
Traceback (most recent call last):
  File "", line 1, in <module>
StopIteration
>>>

Ồ hay chưa, mình có thể đi qua các phần tử trong a bằng phương thức __next__, thay vì dùng cho nè. Khi đến phần tử cuối cùng rồi thì nó sẽ báo lỗi StopIteration để báo cho mình biết hết đồ để đi tiếp rồi nghen 🤣

Thêm nữa, iterator cũng là iterable đó, cùng xem tớ thích nó với cho nè, và cả gọi iter() cho x thì nó trả về chính nó luôn

>>> a = ["BeautyOnCode", "blog"]
>>> x = iter(a)
>>> for s in x:
...     print(s)
...
BeautyOnCode
blog
>>> iter(x)
<list_iterator object at 0x10b5a84f0>
>>> x
<list_iterator object at 0x10b5a84f0>
>>>

Vì vậy, để lặp qua một lần lặp vô hạn, như cái ví dụ bội số của 3 ở trên, mình cần biến nó thành iterator bằng hàm iter(), rồi sau đó có thể sử dụng __next__() hoặc next() để đi qua . Nhưng vì đang nói đến iterable vô hạn nên iterable không bao giờ có ngoại lệ StopIteration luôn đó

Cùng xem cách mình đã bật qua “multiplesofthree” sử dụng iterator thay cho nhé

Điều khiển lặp trong python

Thật vị thú không đúng, mình không sử dụng cho mà vẫn lặp qua các đối tượng của một iterable đấy

Và bật mí với các bạn, đây cũng chính là cơ chế lặp được sử dụng nhiều trong Python đấy, cụ thể là cho vòng lặp cho nè, rồi xác định nhiều giá trị trong tuple nè, rồi hiểu danh sách (ví dụ cho bạn nào

Trước khi đi tiếp mình xin bùm bùm cho bạn đỡ rối nha

Mình đã tìm hiểu về. Có thể lặp lại, Iterator

😊 Iterable was verify by 3 way

  1. used used with for

  2. iter(X) not báo lỗi

  3. has method __iter__

😊 Iterator được xác định bằng

  1. Y = iter(X), với X là iterable thì Y là iterator

  2. next(Y) sẽ trả về giá trị phần tử tiếp theo hoặc ngoại lệ StopIteration, thì Y là iterator

  3. iter(Y) will return Y, then Y is iterator(tức iterator cũng chính là iterable)

Nhìn tóm tắt vào bạn thử cấu hình xem nếu mình muốn tạo một iterable cho riêng mình thì mình cần phải định nghĩa những gì để Python hiểu nó là một iterable nhỉ?

To see any, chắc chắn là nó cần có một phương thức là __iter__, và phương thức này cần phải trả về một iterator của bạn

Cùng mình cố gắng định nghĩa một lần lặp lại của riêng bạn nhé


________số 8

Ở trên, lớp BlogPost có thể tạo ra các đối tượng có thể lặp lại đó, cùng thử trải nghiệm nha

Điều khiển lặp trong python

Mình đang thực hiện và giải thích trên video này nè

Ui, mãi mê mệt với hai bạn này mà mình sắp đến thời gian rồi, nhanh nhanh đi tiếp chú tiểu yêu tiếp theo cần phải chinh phục, đó là chú màu xanh máy phát điện

Máy phát điện

This is output body of Generator from Wikipedia

Trong khoa học máy tính, trình tạo là một thói quen có thể được sử dụng để kiểm soát hành vi lặp lại của một vòng lặp. Tất cả các trình tạo cũng là trình vòng lặp

Bản dịch tạm thời là. Trong lĩnh vực khoa học máy tính, máy phát điện là một bộ quy trình mà những người có thể sử dụng để kiểm tra hành động của một vòng lặp. And all generator also iterators

Còn lại trong Python, trình tạo được định nghĩa trong Python wiki là

trình tạo hàm hay gọi là trình tạo hàm, cho phép bạn tạo ra một hàm hoạt động tương tự như một trình lặp, tức là nó cũng có thể lặp lại và có thể sử dụng với vòng lặp cho

Use generator in any fields

Mình là một bạn nhỏ hay thắc mắc, mình cũng rất tò mò lý do tại sao lại cần có hàm tạo nhỉ?

Bạn đoán thử cùng mình xem sao?

Mình nghĩ là nếu tạo iterator khoai như ở trên, thì buồn thật đấy, khi nào cũng viết phương thức __iter__ rồi nó phải trả về iterator, rồi muốn trả về iterator thì lại phải đi định nghĩa phương thức __next__ và viết cái logic khác . Thật tình mà nói thì cũng hơi khó ghét đó nha 🥲

Chắc là mấy chú Python thấy thế thì nghĩ ra một cách, hay là mình cho đám nhỏ viết một hàm thôi, và hàm đó hoạt động như một iterator, còn những thứ lằng nhằng kia để các chú lo, phải không?

Hihi, đoán sao mà nó trúng rồi nha, chính xác là generator giúp mình tạo iterator một cách dễ dàng hơn nhiều. Và thêm nữa, trình tạo sẽ thường được sử dụng cho các trường hợp cần xem xét về hiệu suất của chương trình, ví dụ như khi mình làm việc với số lượng siêu lớn, hay làm việc với các tệp có dung lượng lớn cần xử lý

Vì sao?

Nếu bạn gặp những trường hợp này, thì generator chính là chân ái của đời bạn đó, nhớ nhé 😘

Ví dụ cho cái

đề bài. tạo một danh sách các số từ 0 đến n, sau đó tính tổng của chúng. Please try to try with n = 1000000000 nhé

Nào, giờ mình sẽ đi cùng mọi người giải bài toán bằng ba cách nhé

Cách 1. sử dụng một danh sách để lưu các số

def list_n_list(n):
    num, nums = 0, []
    while num < n:
        nums.append(num)
        num += 1
        return nums

sum(list_n_list(1000000000))

Và mình đang chạy thử xem nó tốn bao nhiêu thời gian

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
0

Rồi, đoạn mã này khá đơn giản phải không, logic rất dễ hiểu, nhưng nó đang tạo một danh sách với tất cả các phần tử từ 0 đến n, rồi cộng lại. Rõ ràng là phương án này nhìn thì đơn giản, nhưng rất khó tiếp nhận trong trường hợp n là số siêu lớn, vì làm sao mà mình lưu hết cả 1000000000… phần tử trong bộ nhớ được

Với n = 1000000000000000, chương trình sẽ đứng sau một nỗ lực hồi sinh hiu hiu, thương ghê

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
1

cách 2. sử dụng iterator

Đầu tiên, mình sẽ tiến gần cách số 2 với bạn iterator trước nha, vì bạn này cũng giúp mình không lưu cả dãy như ở trên, và vẫn có thể xử lý vấn đề này ha

Ôkê, tại bạn ni hơi cực nên mình lại nhắc tí là mình tính làm gì nha. Đầu tiên là mình cần tạo một lớp có phương thức __iter__ sau đó phương thức này trả ra iterator, ở đây là mình muốn gom hết vào một chỗ luôn, vì mình hiểu là iterator cũng là iterable nên mình sẽ trở lại chính mình ở đây. Sau đó mình sẽ tạo tiếp một phương thức __next__ để chính nó là iterator

Cùng xem mã nha

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
2

Còn đây là thời gian chạy của mã đoạn với iterator nhé

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
3

Và tất nhiên là đoạn mã trên có thể hoạt động ổn định hơn rồi

Tuy nhiên có vài vấn đề mà mình từng đoán trước đây như là

– Nhiều mã quá, bao gồm lớp rồi phương thức,…

– Và logic khá rắc rối, trừ khi bạn hiểu sâu về iterator, iterable còn không thì nhìn vào đã hoa mắt rồi

Chưa hết, nếu dùng đoạn mã này quay lại thì nhiều nơi sẽ làm cho đoạn mã dài hơn đấy. Vì thế, Python đã hỗ trợ trình tạo của mình, bạn này được giới thiệu từ PEP255

Cách 3. sử dụng người anh hùng máy phát điện, chân ái khi làm việc với số lượng lớn và dữ liệu lớn

Cùng viết lại mã trên máy phát điện nhé. Bạn sẽ ngạc nhiên vì độ thanh lịch của nó đấy

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
4

And your time this time run is

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
5

*Lưu ý nhỏ là thời gian chỉ mang tính chất so sánh khách quan thôi nha, vì nó còn phụ thuộc vào máy của tớ nữa ý. *

Cách tạo máy phát điện

Cách 1. Sử dụng trình tạo hàm

Đây chính là cách mà ví dụ ở trên dùng đấy

Để tạo hàm tạo thì mình tạo hàm như bình thường và thay vì dùng return để trả về giá trị thì mình dùng yield để trả về giá trị

cách 2. Sử dụng trình tạo biểu thức

Ngoài ra, còn có thể sử dụng trình tạo biểu thức để tạo nữa, biểu thức này tương tự như cách hiểu danh sách đó, mà thay dấu [] bằng dấu () thôi

>>> b = {"Thanh", "has", "a", "blog"}
>>> for i, s in enumerate(b):
...     print("chuoi hien tai la:", s)
...     if i < len(b) - 1:
...             print("chuoi tiep theo la:", b[i+1])
...     else:
...             print("day la chuoi cuoi cung!")
...
chuoi hien tai la: blog
Traceback (most recent call last):
  File "", line 4, in <module>
TypeError: 'set' object is not subscriptable
>>>
6

Vì hôm nay mình đã cùng gặp qua sư phụ iterable và chiến đấu với hai bạn nhỏ yêu iterator và generator rồi. Bạn quái thú hai đầu này dù có sự trợ giúp sức lực của sư phụ iterable nữa nhưng vẫn đầu hàng trước sự cố gắng của bọn mình và cả ba đều biến thành các bạn yêu nhỏ xinh Điều khiển rồi đó. 🥳