# Danh sách trong Python (list): phương thức, cắt lát, sắp xếp, append, tuple và set

URL: https://skillaria.top/vi/guides/python/danh-sach-python

Danh sách trong Python (list) là tập hợp có thứ tự, có thể thay đổi, chứa giá trị bất kỳ, viết trong dấu ngoặc vuông, ví dụ [250, 1200, 99]: có thể thêm phần tử bằng append, chèn, xóa, lấy bằng cắt lát và sắp xếp. Phương thức sort sắp xếp ngay trên danh sách và trả về None, còn hàm sorted trả về một danh sách mới đã sắp xếp.

Danh sách trong Python (`list`) là tập hợp có thứ tự gồm các giá trị bất kỳ, viết trong dấu ngoặc vuông, và có thể thay đổi: thêm phần tử vào cuối, chèn vào giữa, xóa, sắp xếp. Đây là tập hợp được dùng nhiều nhất trong ngôn ngữ: các dòng của tệp, chi tiêu trong tháng, phản hồi API — hầu hết đều bắt đầu từ một danh sách. Dưới đây là toàn bộ list trong Python: cách tạo, phương thức, cắt lát, sắp xếp, list comprehension, sao chép, và cuối cùng là tuple và set.

## Danh sách trong Python là gì

Danh sách lưu các phần tử theo đúng thứ tự bạn đã đặt và truy cập chúng qua chỉ số bắt đầu từ 0. Một danh sách có thể chứa nhiều kiểu giá trị khác nhau, dù trong thực tế người ta thường lưu dữ liệu cùng kiểu.

```
amounts = [250, 1200, 99]
print(amounts[0])     # 250 — phần tử đầu tiên
print(amounts[-1])    # 99 — phần tử cuối cùng
amounts[1] = 1300     # danh sách có thể thay đổi
print(len(amounts))   # 3 — độ dài danh sách
```

Độ dài của danh sách lấy bằng hàm `len()`. Truy cập một chỉ số không tồn tại sẽ gây `IndexError: list index out of range`.

## Tạo danh sách

```
empty = []                       # danh sách rỗng
also_empty = list()              # cũng vậy, qua hàm list
nums = [1, 2, 3]
chars = list("abc")              # ['a', 'b', 'c']
digits = list(range(5))          # [0, 1, 2, 3, 4]
zeros = [0] * 4                  # [0, 0, 0, 0]
```

Hàm `list()` biến bất kỳ đối tượng có thể lặp nào thành danh sách: chuỗi, `range`, set, các khóa của dict.

## append và các phương thức khác của list

Phương thức `append` thêm một phần tử vào cuối danh sách — đây là phương thức được dùng nhiều nhất trong Python. Nó thay đổi danh sách tại chỗ và trả về `None`.

```
amounts = [250, 1200]
amounts.append(450)          # [250, 1200, 450]
amounts.extend([10, 20])     # [250, 1200, 450, 10, 20]
amounts.insert(0, 5)         # [5, 250, 1200, 450, 10, 20]
amounts.remove(1200)         # xóa giá trị 1200 xuất hiện đầu tiên
last = amounts.pop()         # 20 — xóa và trả về phần tử cuối
```

Lỗi hay gặp: `a.append([10, 20])` chỉ thêm một phần tử duy nhất — một danh sách lồng nhau, chứ không phải hai số. Để thêm nhiều phần tử, dùng `extend`.

| Phương thức | Tác dụng |
| --- | --- |

| `append(x)` | thêm x vào cuối |

| `extend(iter)` | thêm toàn bộ phần tử của một dãy |

| `insert(i, x)` | chèn x trước chỉ số i |

| `remove(x)` | xóa lần xuất hiện đầu tiên của x (ValueError nếu không có) |

| `pop(i=-1)` | xóa và trả về phần tử ở chỉ số i |

| `index(x)` | chỉ số của lần xuất hiện đầu tiên |

| `count(x)` | x xuất hiện bao nhiêu lần |

| `sort()` | sắp xếp tại chỗ |

| `reverse()` | đảo ngược tại chỗ |

| `copy()` | bản sao nông |

| `clear()` | xóa toàn bộ danh sách |

Xóa một phần tử theo chỉ số hoặc cả một đoạn còn có thể dùng toán tử `del`: `del a[0]`, `del a[1:3]`. Các hàm tích hợp hữu ích với danh sách: `len`, `sum`, `min`, `max`, `sorted`, `reversed`, `enumerate`, `zip`.

## Cắt lát trong Python

Phép cắt lấy một phần danh sách theo cú pháp `a[start:stop:step]`: từ `start` (bao gồm) đến `stop` (không bao gồm), với bước `step`. Có thể bỏ qua bất kỳ phần nào. Phép cắt luôn trả về một danh sách mới.

```
a = [0, 1, 2, 3, 4, 5]
print(a[1:4])     # [1, 2, 3]
print(a[:2])      # [0, 1] — hai phần tử đầu
print(a[-2:])     # [4, 5] — hai phần tử cuối
print(a[::2])     # [0, 2, 4] — cứ mỗi phần tử lấy một
print(a[::-1])    # [5, 4, 3, 2, 1, 0] — đảo ngược
print(a[:])       # bản sao của cả danh sách
```

Khác với chỉ số, cắt ngoài phạm vi không gây lỗi: `a[10:20]` chỉ trả về `[]`. Có thể gán giá trị cho một lát để thay thế một đoạn của danh sách: `a[1:3] = ["x", "y", "z"]`.

### Cắt chuỗi

Phép cắt hoạt động giống nhau với mọi dãy, kể cả chuỗi và tuple:

```
s = "Python 3.15"
print(s[:6])      # Python
print(s[-4:])     # 3.15
print(s[::-1])    # 51.3 nohtyP
```

Không thể thay đổi chuỗi bằng phép cắt — chuỗi là bất biến, phép cắt tạo ra một chuỗi mới.

## Sắp xếp danh sách: sort và sorted

Đây là chỗ người mới hay nhầm lẫn nhất. Phương thức `sort` sắp xếp lại các phần tử ngay trong danh sách và trả về `None`. Hàm `sorted` không đụng đến danh sách gốc mà trả về một danh sách mới đã được sắp xếp.

```
amounts = [250, 1200, 99]
amounts.append(450)
print(sorted(amounts))     # [99, 250, 450, 1200] — bản sao mới
print(amounts)             # [250, 1200, 99, 450] — bản gốc vẫn nguyên

result = amounts.sort()    # sắp xếp tại chỗ
print(result, amounts)     # None [99, 250, 450, 1200]
```

Từ đây có lỗi kinh điển «sao lại ra None»: `a = a.sort()` ghi đè danh sách bằng giá trị `None`. Hãy viết hoặc `a.sort()`, hoặc `a = sorted(a)`.

### Khóa và thứ tự ngược

Cả hai đều nhận tham số `key` và `reverse`. `key` là một hàm; kết quả của hàm này là thứ dùng để so sánh các phần tử.

```
words = ["cà phê", "Taxi", "vé tháng"]
print(sorted(words, key=len))               # ['Taxi', 'cà phê', 'vé tháng']
print(sorted(words, key=str.lower))         # không phân biệt hoa thường
print(sorted([3, 1, 2], reverse=True))      # [3, 2, 1]

expenses = [("Ăn uống", 650), ("Xem phim", 120), ("Taxi", 300)]
expenses.sort(key=lambda e: e[1])           # theo số tiền
```

Sắp xếp trong Python có tính ổn định: các phần tử có cùng khóa giữ nguyên thứ tự ban đầu. Điểm khác nữa là phạm vi dùng: `sorted` nhận mọi dãy (chuỗi, dict, set), còn `sort` chỉ có ở list.

## Đảo ngược danh sách

Có ba cách với hành vi khác nhau:

- `a.reverse()` — đảo tại chỗ, trả về `None`;
- `a[::-1]` — tạo danh sách mới đã đảo;
- `reversed(a)` — iterator lười, tiện trong vòng lặp: `for x in reversed(a):`.

## Duyệt danh sách

```
items = ["cà phê", "tàu điện", "xem phim"]
for item in items:
    print(item)

for i, item in enumerate(items, start=1):
    print(i, item)          # 1 cà phê, 2 tàu điện, 3 xem phim

print("xem phim" in items)  # True — kiểm tra có phần tử hay không
```

Đừng xóa phần tử của danh sách đang được duyệt bằng vòng lặp — sẽ có phần tử bị bỏ sót. Hãy tạo một danh sách mới bằng list comprehension, hoặc duyệt trên bản sao `a[:]`.

## List comprehension

List comprehension tạo danh sách mới chỉ trong một dòng: biểu thức, vòng lặp và điều kiện tùy chọn.

```
squares = [n * n for n in range(6)]          # [0, 1, 4, 9, 16, 25]
big = [a for a in [250, 1200, 99] if a > 200]  # [250, 1200]
names = [s.strip().title() for s in [" lan ", "nam"]]
```

List comprehension thường nhanh hơn và dễ đọc hơn vòng lặp với `append`. Nhưng nếu logic không gói gọn được trong một dòng dễ hiểu, một vòng lặp thông thường sẽ tốt hơn.

## Gộp, cộng và trừ danh sách

```
a = [1, 2, 3]
b = [3, 4]
print(a + b)       # [1, 2, 3, 3, 4] — danh sách mới
a += b             # tương đương a.extend(b)
print([*a, *b])    # giải nén vào danh sách mới
```

Không có phép trừ danh sách bằng toán tử `-`. Để bỏ khỏi một danh sách các phần tử có trong danh sách khác, dùng list comprehension: `[x for x in a if x not in set(b)]`. Giao của hai danh sách thuận tiện nhất khi dùng set: `set(a) & set(b)` (khi đó thứ tự và phần tử trùng sẽ mất).

## Danh sách lồng nhau và hai chiều

Danh sách của danh sách là cách đơn giản để lưu một bảng hoặc ma trận:

```
matrix = [[1, 2, 3], [4, 5, 6]]
print(matrix[1][2])     # 6
grid = [[0] * 3 for _ in range(2)]   # đúng cách
bad = [[0] * 3] * 2                  # bẫy: hai tham chiếu tới cùng một danh sách
bad[0][0] = 9
print(bad)              # [[9, 0, 0], [9, 0, 0]]
```

## Giải nén danh sách

```
first, second, third = [10, 20, 30]
head, *rest = [1, 2, 3, 4]      # head=1, rest=[2, 3, 4]
print(*["a", "b"], sep=", ")    # a, b — in các phần tử của danh sách không có ngoặc
```

Nếu số biến không khớp với độ dài danh sách sẽ có `ValueError` — dấu sao giúp giải quyết điều này.

## Sao chép danh sách

`b = a` không sao chép danh sách, mà chỉ gắn thêm một nhãn thứ hai vào cùng một đối tượng: thay đổi qua `b` sẽ thấy trong `a`. Muốn có bản sao độc lập, hãy tạo rõ ràng: `a.copy()`, `list(a)` hoặc `a[:]`. Với dữ liệu lồng nhau thì như vậy là chưa đủ — các danh sách bên trong vẫn được dùng chung, cần `copy.deepcopy(a)`.

```
import copy
a = [[1, 2], [3]]
b = a.copy()
b[0].append(99)
print(a)                 # [[1, 2, 99], [3]] — danh sách con vẫn dùng chung
c = copy.deepcopy(a)     # bản sao hoàn toàn độc lập
```

## Tuple: danh sách bất biến

Tuple (`tuple`) giống danh sách, nhưng sau khi tạo không thể thay đổi. Đây là tín hiệu rõ ràng cho người đọc: đây là một bản ghi với các trường cố định, không phải một tập hợp lớn dần. Tuple dễ dàng được giải nén, và hàm trả về nhiều giá trị thường trả về tuple.

```
expense = (250, "Cà phê")
amount, item = expense
expense[0] = 300
# TypeError: 'tuple' object does not support item assignment
```

Tuple chứa toàn giá trị bất biến có thể băm được, nên dùng được làm khóa của dict. Tuple gồm một phần tử được viết kèm dấu phẩy: `(5,)`.

## Set: chỉ chứa giá trị duy nhất

Set (`set`) chỉ lưu các giá trị duy nhất và không giữ thứ tự. Đây là cách nhanh nhất để loại trùng lặp và kiểm tra «phần tử có tồn tại hay không»; set còn hỗ trợ giao, hợp và hiệu.

```
jan = {"Ăn uống", "Taxi", "Xem phim"}
feb = {"Ăn uống", "Đăng ký dịch vụ"}
print(jan & feb)    # {'Ăn uống'}
print(feb - jan)    # {'Đăng ký dịch vụ'}
unique = list(dict.fromkeys([3, 1, 3, 2]))   # [3, 1, 2] — bỏ trùng, giữ thứ tự
empty = set()       # {} là dict rỗng!
```

## Chọn cấu trúc nào

| Nhu cầu | Cấu trúc |
| --- | --- |

| Có thứ tự và cần thay đổi | `list` |

| Bản ghi cố định | `tuple` |

| Tìm theo khóa | `dict` |

| Duy nhất, kiểm tra «có hay không» | `set` |

| Cấu hình bất biến | `frozendict` (Python 3.15) |

Phép kiểm tra `x in list` duyệt từng phần tử, còn `x in set` gần như tức thì. Nếu trong vòng lặp bạn kiểm tra nhiều lần sự có mặt trong một danh sách lớn, hãy chuyển nó thành set.

## Tổng kết

List trong Python là tập hợp có thứ tự và có thể thay đổi: `append` và `extend` thêm phần tử, `pop` và `remove` xóa, phép cắt `a[start:stop:step]` lấy từng phần, `sort` sắp xếp tại chỗ và trả về `None`, còn `sorted` trả về danh sách mới. Hãy sao chép một cách tường minh, dùng tuple cho bản ghi cố định và set cho giá trị duy nhất. Các cấu trúc danh sách, tuple, dict và set được thực hành trong khóa học «Python 3.15: từ người mới đến chuyên nghiệp».

## FAQ

**List trong Python khác mảng (array) ở điểm nào?**

Python không có mảng theo nghĩa của các ngôn ngữ khác; vai trò đó do list đảm nhiệm, và list có thể chứa các kiểu giá trị khác nhau. Với mảng số gọn nhẹ có module array, còn với tính toán khoa học có thư viện NumPy.

**Làm sao tìm chỉ số của một phần tử trong danh sách?**

Phương thức a.index(x) trả về chỉ số lần xuất hiện đầu tiên và gây ValueError nếu không có phần tử đó. Vì vậy nên kiểm tra x in a trước khi gọi.

**Làm sao xóa phần tử trùng lặp khỏi danh sách nhưng vẫn giữ thứ tự?**

Dùng list(dict.fromkeys(a)): dict chỉ giữ các khóa duy nhất theo thứ tự thêm vào. Cách list(set(a)) cũng loại trùng nhưng không giữ thứ tự.

**Làm sao nối một danh sách chuỗi thành một chuỗi?**

Dùng phương thức join của chuỗi: ", ".join(["a", "b", "c"]) cho "a, b, c". Mọi phần tử phải là chuỗi; số thì chuyển trước bằng map(str, a).

**Có thể để nhiều kiểu dữ liệu khác nhau trong một danh sách không?**

Có, một list có thể đồng thời chứa số, chuỗi, danh sách khác và các đối tượng bất kỳ. Nhưng sắp xếp một danh sách như vậy mà không có tham số key thường không được — so sánh số với chuỗi sẽ gây TypeError.


---
Source: Skillaria.Top (https://skillaria.top)
