Khám phá các hàm dựng sẵn và đối tượng có thể lặp lại trong Python

Các hàm dựng sẵn quan trọng

1. `zip()`

Hàm `zip()` dùng để kết hợp các phần tử tương ứng từ nhiều đối tượng có thể lặp lại (như danh sách, tuple) lại với nhau, tạo thành các tuple. Sau đó, nó trả về một đối tượng zip chứa các tuple này.

Khi chuyển đổi đối tượng zip thành danh sách bằng `list()`, bạn sẽ thấy mỗi phần tử trong danh sách là một tuple chứa các phần tử tương ứng từ các đối tượng đầu vào. Nếu số lượng phần tử của các đối tượng đầu vào không đồng đều, các phần tử dư ra sẽ không được bao gồm trong kết quả.


danh_sach_1 = [1, 2, 3]
danh_sach_2 = ['a', 'b', 'c']
ket_hop = zip(danh_sach_1, danh_sach_2)
print(list(ket_hop))
# Kết quả: [(1, 'a'), (2, 'b'), (3, 'c')]

danh_sach_3 = [1, 2]
danh_sach_4 = ['x', 'y', 'z']
ket_hop_le = zip(danh_sach_3, danh_sach_4)
print(list(ket_hop_le))
# Kết quả: [(1, 'x'), (2, 'y')]
        

2. `filter()`

Hàm `filter()` được sử dụng để lọc các phần tử khỏi một chuỗi (sequence) dựa trên một điều kiện xác định. Nó trả về một đối tượng filter chứa các phần tử thỏa mãn điều kiện đó. Để nhận được danh sách, bạn cần chuyển đổi đối tượng filter này.


def la_so_chan(n):
    return n % 2 == 0

so_nguyen = [1, 2, 3, 4, 5, 6]
so_chan = filter(la_so_chan, so_nguyen)
print(list(so_chan))
# Kết quả: [2, 4, 6]
        

3. `sorted()`

Hàm `sorted()` dùng để sắp xếp các phần tử trong một đối tượng có thể lặp lại (iterable) như danh sách, tuple, hoặc các khóa của từ điển. Theo mặc định, nó sắp xếp theo thứ tự tăng dần. Bạn có thể cung cấp tham số `reverse=True` để sắp xếp theo thứ tự giảm dần.


danh_sach_unsorted = [3, 1, 4, 1, 5, 9, 2]
da_sap_xep = sorted(danh_sach_unsorted)
print(da_sap_xep)
# Kết quả: [1, 1, 2, 3, 4, 5, 9]

da_sap_xep_giam = sorted(danh_sach_unsorted, reverse=True)
print(da_sap_xep_giam)
# Kết quả: [9, 5, 4, 3, 2, 1, 1]
        

Các hàm dựng sẵn phổ biến khác

  1. `abs(x)`

    Trả về giá trị tuyệt đối của một số.

    print(abs(-10))  # Kết quả: 10
  2. `all(iterable)`

    Trả về True nếu tất cả các phần tử trong đối tượng có thể lặp lại đều có giá trị boolean là True (hoặc nếu đối tượng rỗng). Ngược lại trả về False.

    print(all([1, 2, 3]))       # Kết quả: True
    print(all([1, 0, 3]))       # Kết quả: False
    print(all([]))              # Kết quả: True
  3. `any(iterable)`

    Trả về True nếu có ít nhất một phần tử trong đối tượng có thể lặp lại có giá trị boolean là True. Nếu tất cả đều là False hoặc đối tượng rỗng, trả về False.

    print(any([0, False, None]))  # Kết quả: False
    print(any([0, 1, None]))      # Kết quả: True
  4. Chuyển đổi hệ đếm

    • bin(x): Chuyển đổi số nguyên sang dạng nhị phân (chuỗi bắt đầu bằng '0b').
    • oct(x): Chuyển đổi số nguyên sang dạng bát phân (chuỗi bắt đầu bằng '0o').
    • hex(x): Chuyển đổi số nguyên sang dạng thập lục phân (chuỗi bắt đầu bằng '0x').
    • int(x, base=10): Chuyển đổi chuỗi hoặc số sang dạng số nguyên thập phân.
    print(bin(10))   # Kết quả: '0b1010'
    print(oct(10))   # Kết quả: '0o12'
    print(hex(10))   # Kết quả: '0xa'
    print(int('1010', 2)) # Kết quả: 10
  5. `bytes(source, encoding)`

    Chuyển đổi một chuỗi hoặc một đối tượng tương thích khác thành kiểu bytes.

    chuoi = "hello"
    bytes_obj = bytes(chuoi, 'utf-8')
    print(bytes_obj)  # Kết quả: b'hello'
  6. `callable(object)`

    Kiểm tra xem một đối tượng có thể được gọi (thực thi như một hàm) hay không. Trả về True nếu có thể gọi, ngược lại trả về False.

    def ham_test(): pass
    class ClassTest:
        def __call__(self): pass
    
    print(callable(ham_test))     # Kết quả: True
    print(callable(ClassTest()))  # Kết quả: True
    print(callable(10))           # Kết quả: False
  7. Chuyển đổi số và ký tự (ASCII)

    • chr(i): Trả về ký tự tương ứng với mã ASCII i.
    • ord(c): Trả về mã ASCII của ký tự c.
    print(chr(65))  # Kết quả: 'A'
    print(ord('A')) # Kết quả: 65
  8. `dir(object)`

    Trả về một danh sách các tên thuộc tính và phương thức của đối tượng.

    print(dir('hello')) # Liệt kê các phương thức của chuỗi
  9. `divmod(a, b)`

    Trả về một cặp số (tuple) chứa thương số và số dư của phép chia a cho b.

    thuong, du = divmod(10, 3)
    print(f"Thương: {thuong}, Dư: {du}")  # Kết quả: Thương: 3, Dư: 1
  10. `enumerate(iterable, start=0)`

    Trả về một đối tượng enumerate, bao gồm cặp chỉ số (index) và giá trị cho từng phần tử trong đối tượng có thể lặp lại. Tham số start xác định chỉ số bắt đầu.

    chuoi = 'Python'
    for index, char in enumerate(chuoi):
        print(f"Chỉ số: {index}, Ký tự: {char}")
    # Kết quả:
    # Chỉ số: 0, Ký tự: P
    # Chỉ số: 1, Ký tự: y
    # ...
    
    for index, char in enumerate(chuoi, start=1):
        print(f"Vị trí: {index}, Ký tự: {char}")
    # Kết quả:
    # Vị trí: 1, Ký tự: P
    # Vị trí: 2, Ký tự: y
    # ...
  11. `eval(expression, globals=None, locals=None)` và `exec(object, globals=None, locals=None)`

    eval() dùng để đánh giá một biểu thức chuỗi và trả về kết quả. Chuỗi chỉ nên chứa một biểu thức hợp lệ.

    exec() dùng để thực thi các câu lệnh Python được biểu diễn dưới dạng chuỗi, có thể bao gồm logic phức tạp hoặc nhiều câu lệnh.

    gia_tri_bieu_thuc = eval("5 * 10 + 2")
    print(gia_tri_bieu_thuc)  # Kết quả: 52
    
    ma_python = """
    x = 10
    y = 20
    print(x + y)
    """
    exec(ma_python)  # Kết quả: 30
  12. `hash(object)`

    Trả về giá trị băm (hash) của một đối tượng. Giá trị băm là một số nguyên được sử dụng để so sánh nhanh các mục trong bảng băm (hash table).

    print(hash('hello'))
  13. `id(object)`

    Trả về định danh duy nhất (identity) của một đối tượng trong bộ nhớ. Định danh này là một số nguyên.

    a = [1, 2, 3]
    print(id(a))
  14. `input(prompt=None)`

    Nhận đầu vào từ người dùng thông qua bàn phím. Tham số prompt là chuỗi hiển thị trước khi chờ nhập liệu.

    ten = input("Nhập tên của bạn: ")
    print(f"Chào bạn, {ten}!")
  15. `isinstance(object, classinfo)`

    Kiểm tra xem một đối tượng có phải là một thể hiện (instance) của một lớp hoặc một tuple các lớp hay không. Trả về True hoặc False.

    so = 10
    chuoi = "Python"
    print(isinstance(so, int))      # Kết quả: True
    print(isinstance(chuoi, str))   # Kết quả: True
    print(isinstance(so, (str, list))) # Kết quả: False
  16. `map(function, iterable, ...)` ; `max(iterable, ...)` ; `min(iterable, ...)`

    • map(): Áp dụng một hàm cho tất cả các phần tử của một hoặc nhiều đối tượng có thể lặp lại.
    • max(): Trả về phần tử lớn nhất trong một đối tượng có thể lặp lại hoặc trong các đối số được cung cấp.
    • min(): Trả về phần tử nhỏ nhất.
    def binh_phuong(n):
        return n * n
    
    numbers = [1, 2, 3, 4]
    ket_qua_map = map(binh_phuong, numbers)
    print(list(ket_qua_map))  # Kết quả: [1, 4, 9, 16]
    
    print(max(numbers))      # Kết quả: 4
    print(min(numbers))      # Kết quả: 1
  17. `open(file, mode='r', ...)`

    Mở một tệp và trả về một đối tượng tệp tương ứng. Tham số mode xác định cách thức mở tệp (ví dụ: 'r' để đọc, 'w' để ghi, 'a' để nối thêm).

    # Đọc nội dung tệp
    with open('example.txt', 'r') as f:
        content = f.read()
        print(content)
    
    # Ghi nội dung vào tệp
    with open('output.txt', 'w') as f:
        f.write("Đây là dòng mới.\n")
  18. `pow(base, exp, mod=None)`

    Trả về baseexp. Nếu mod được cung cấp, nó sẽ trả về kết quả của (base**exp) % mod.

    print(pow(2, 3))      # Kết quả: 8
    print(pow(2, 3, 5))   # Kết quả: 3 (vì 8 % 5 = 3)
  19. `range(stop)` ; `range(start, stop[, step])`

    Tạo ra một chuỗi các số nguyên không thay đổi. Thường được sử dụng trong các vòng lặp for.

    for i in range(5):
        print(i, end=" ")  # Kết quả: 0 1 2 3 4
    print()
    for i in range(2, 10, 2):
        print(i, end=" ")  # Kết quả: 2 4 6 8
  20. `round(number, ndigits=None)`

    Làm tròn một số đến một số chữ số thập phân xác định (ndigits). Nếu ndigits không được cung cấp, nó làm tròn đến số nguyên gần nhất.

    Lưu ý: Khi phần thập phân là 5, quy tắc làm tròn có thể thay đổi giữa các phiên bản Python hoặc tùy thuộc vào hệ điều hành (làm tròn đến số chẵn gần nhất).

    print(round(3.14159, 2))  # Kết quả: 3.14
    print(round(2.5))         # Kết quả có thể là 2 hoặc 3
    print(round(3.5))         # Kết quả có thể là 3 hoặc 4
  21. `sum(iterable, start=0)`

    Tính tổng các phần tử trong một đối tượng có thể lặp lại. Tham số start là giá trị ban đầu được cộng vào tổng.

    so_luong = [10, 20, 30]
    print(sum(so_luong))        # Kết quả: 60
    print(sum(so_luong, 100))   # Kết quả: 160

Đối tượng có thể lặp và Trình lặp (Iterators)

1. Đối tượng có thể lặp (Iterable)

Trong Python, mọi thứ đều là đối tượng. Các đối tượng có thể được phân loại theo nhiều cách. Một đối tượng được gọi là có thể lặp (iterable) nếu nó có phương thức đặc biệt __iter__().

  • Phương thức đặc biệt (Dunder methods): Các phương thức được bao quanh bởi hai dấu gạch dưới, ví dụ: __iter__(). Chúng thường được gọi một cách tự động bởi các hàm hoặc cấu trúc điều khiển của Python.
  • __iter__(): Phương thức này, khi được gọi, phải trả về một đối tượng trình lặp (iterator object).

Phạm vi của đối tượng có thể lặp:

  • Không phải là đối tượng có thể lặp: Kiểu số nguyên (int), số thực (float), boolean (bool), đối tượng hàm.
  • Là đối tượng có thể lặp: Chuỗi (str), danh sách (list), từ điển (dict), tuple (tuple), tập hợp (set), đối tượng tệp.

Ý nghĩa của "có thể lặp":

Trong ngữ cảnh của Python, "có thể lặp" có nghĩa là đối tượng đó hỗ trợ việc duyệt qua các phần tử của nó, điển hình là bằng vòng lặp for.

2. Đối tượng trình lặp (Iterator)

Định nghĩa:

Một đối tượng trình lặp được tạo ra khi bạn gọi phương thức __iter__() trên một đối tượng có thể lặp. Để được coi là một trình lặp, một đối tượng phải có cả hai phương thức đặc biệt: __iter__()__next__().

Vai trò của trình lặp:

Trình lặp cung cấp một cách để truy cập các phần tử của một đối tượng có thể lặp mà không cần biết chỉ số. Chính nhờ có trình lặp mà các cấu trúc như từ điển và tập hợp mới có thể được duyệt bằng vòng lặp for.

Thực hành với trình lặp:

s1 = 'hello'  # Đối tượng có thể lặp

# Lấy trình lặp từ chuỗi
trinh_lap_s1 = s1.__iter__()

# Sử dụng __next__() để lấy từng phần tử
print(trinh_lap_s1.__next__())  # Kết quả: h
print(trinh_lap_s1.__next__())  # Kết quả: e
print(trinh_lap_s1.__next__())  # Kết quả: l
print(trinh_lap_s1.__next__())  # Kết quả: l
print(trinh_lap_s1.__next__())  # Kết quả: o

# print(trinh_lap_s1.__next__())  # Dòng này sẽ gây ra lỗi StopIteration vì đã hết phần tử

Lưu ý: Khi phương thức __next__() không còn lấy được phần tử nào, nó sẽ báo lỗi StopIteration.

Lưu ý quan trọng:

  • Việc gọi __iter__() trên một đối tượng có thể lặp sẽ tạo ra một trình lặp. Nếu bạn gọi __iter__() trên chính đối tượng trình lặp đó, nó sẽ trả về chính nó (vì trình lặp là một đối tượng có thể lặp mà chính nó là trình lặp của nó).
  • Trình lặp không tải tất cả các phần tử vào bộ nhớ cùng một lúc. Chúng tạo ra và trả về kết quả khi cần thiết, giúp tiết kiệm bộ nhớ cho các đối tượng lớn.
  • Vì trình lặp triển khai phương thức __next__(), bạn có thể sử dụng hàm dựng sẵn next() để lấy phần tử tiếp theo một cách trực tiếp.

Bản chất của vòng lặp for

Khi bạn viết một vòng lặp for trong Python, ví dụ:

for bien_so_moi in doi_tuong_co_the_lap:
    # Khối mã lặp lại
    print(bien_so_moi)

Python thực hiện các bước sau:

  1. Đầu tiên, nó gọi phương thức __iter__() trên doi_tuong_co_the_lap để lấy ra một đối tượng trình lặp.
  2. Tiếp theo, nó liên tục gọi phương thức __next__() trên đối tượng trình lặp này để lấy từng phần tử.
  3. Mỗi giá trị trả về từ __next__() được gán cho bien_so_moi và khối mã bên trong vòng lặp sẽ được thực thi.
  4. Khi __next__() không còn phần tử nào để trả về và gây ra lỗi StopIteration, vòng lặp for sẽ tự động bắt và xử lý lỗi này để kết thúc vòng lặp một cách êm đẹp.

Mối quan hệ: Hàm next(obj) thực chất gọi phương thức obj.__next__(), và hàm iter(obj) thực chất gọi phương thức obj.__iter__().

Bắt và Xử lý Lỗi (Exception Handling)

1. Lỗi (Exception)

Lỗi là các vấn đề xảy ra trong quá trình thực thi mã của bạn, thường được gọi là "bug". Khi một lỗi xảy ra, nếu không được xử lý, nó sẽ làm dừng toàn bộ chương trình của bạn. Mục tiêu của lập trình viên là cố gắng giảm thiểu hoặc xử lý các lỗi này.

2. Phân loại Lỗi

  • Lỗi cú pháp (Syntax Errors):

    Đây là những lỗi bạn không bao giờ muốn thấy. Chúng ngăn cản chương trình của bạn chạy ngay từ đầu. Ví dụ: thiếu dấu hai chấm, dấu ngoặc không khớp. Nếu gặp lỗi này, bạn cần sửa ngay lập tức.

  • Lỗi logic (Logic Errors):

    Đây là loại lỗi khó phát hiện hơn. Mã của bạn có thể chạy mà không báo lỗi cú pháp, nhưng kết quả trả về lại không như mong đợi. Lỗi logic chỉ bộc lộ khi chương trình chạy và thực hiện các phép tính hoặc xử lý dữ liệu.

3. Cấu trúc thông báo Lỗi

Khi một lỗi xảy ra, Python thường cung cấp một thông báo chi tiết bao gồm:

  • Vị trí xảy ra lỗi: Dòng mã nào gây ra vấn đề.
  • Loại lỗi: Tên của lỗi (ví dụ: TypeError, NameError, IndexError).
  • Chi tiết lỗi: Mô tả cụ thể hơn về nguyên nhân lỗi.

Nếu thông báo lỗi hiển thị nhiều dòng, hãy chú ý đến dòng cuối cùng vì nó thường chỉ ra chính xác nguyên nhân.

Thẻ: python hàm dựng sẵn iterable Iterator Exception Handling

Đăng vào ngày 25 tháng 8 lúc 12:56