HƯỚNG DẪN HỌC PYTHON CHO NGƯỜI MỚI
Chào mừng bạn đến với hành trình khám phá Python đầy thú vị! Tại TechData.AI, chúng tôi tin rằng việc học hiệu quả nhất là tập trung vào những kiến thức cốt lõi, giúp bạn nắm vững 80% chủ đề quan trọng chỉ trong 20% thời gian. Cuốn cẩm nang này được xây dựng dựa trên nguyên tắc đó, mang đến cho bạn một cái nhìn tổng quan toàn diện và dễ hiểu về Python, giúp bạn sẵn sàng chinh phục thế giới lập trình.
Chúng tôi không cố gắng bao quát mọi ngóc ngách của ngôn ngữ Python. Thay vào đó, bài viết này tập trung vào những khái niệm cơ bản nhất, giúp bạn đơn giản hóa các chủ đề phức tạp và xây dựng một nền tảng vững chắc. Với những kiến thức được chia sẻ tại đây, chúng tôi hy vọng bạn sẽ đạt được mục tiêu của mình: nắm vững các kiến thức cơ bản về Python để tự tin bước vào thế giới lập trình.
Cùng bắt đầu nhé!
Mục Lục Tổng Quan
- Giới Thiệu Về Python
- Cách Cài Đặt Python
- Cách Chạy Chương Trình Python
- Python 2 so với Python 3
- Những Kiến Thức Cơ Bản Về Python
- Các Kiểu Dữ Liệu Trong Python
- Các Toán Tử Trong Python
- Toán Tử Ba Ngôi Trong Python
- Chuỗi Ký Tự Trong Python
- Kiểu Dữ Liệu Boolean Trong Python
- Các Kiểu Số Trong Python
- Hằng Số Trong Python
- Kiểu Dữ Liệu Enum Trong Python
- Nhận Dữ Liệu Đầu Vào Từ Người Dùng Trong Python
- Các Câu Lệnh Điều Khiển Trong Python
- Danh Sách (Lists) Trong Python
- Bộ Giá Trị (Tuples) Trong Python
- Từ Điển (Dictionaries) Trong Python
- Tập Hợp (Sets) Trong Python
- Hàm (Functions) Trong Python
- Đối Tượng (Objects) Trong Python
- Vòng Lặp (Loops) Trong Python
- Lớp (Classes) Trong Python
- Module Trong Python
- Thư Viện Chuẩn Của Python
- Hướng Dẫn Phong Cách PEP8 Python
- Gỡ Lỗi (Debugging) Trong Python
- Phạm Vi Biến Trong Python
- Cách Chấp Nhận Đối Số Từ Dòng Lệnh Trong Python
- Hàm Lambda Trong Python
- Đệ Quy Trong Python
- Hàm Lồng Nhau Trong Python
- Closures Trong Python
- Decorators Trong Python
- Docstrings Trong Python
- Introspection Trong Python
- Annotations Trong Python
- Ngoại Lệ (Exceptions) Trong Python
- Câu Lệnh with Trong Python
- Cách Cài Đặt Các Gói Bên Thứ 3 Bằng pip
- List Comprehensions Trong Python
- Đa Hình (Polymorphism) Trong Python
- Nạp Chồng Toán Tử (Operator Overloading) Trong Python
- Môi Trường Ảo (Virtual Environments) Trong Python
- Kết Luận
Giới Thiệu Về Python
Python đang thực sự bùng nổ trong thế giới lập trình. Sự phổ biến và mức độ sử dụng của nó tăng lên theo những cách chưa từng có trong lịch sử máy tính. Python vượt trội trong nhiều tình huống khác nhau như scripting shell, tự động hóa tác vụ và phát triển web chỉ là một vài ví dụ cơ bản.
Python là ngôn ngữ được lựa chọn hàng đầu cho phân tích dữ liệu và học máy, nhưng nó cũng có thể được dùng để tạo trò chơi và làm việc với các thiết bị nhúng. Quan trọng nhất, đây là ngôn ngữ được ưu tiên cho các khóa học khoa học máy tính giới thiệu tại các trường đại học trên toàn thế giới.
Rất nhiều sinh viên học Python làm ngôn ngữ lập trình đầu tiên của họ. Nhiều người đang học nó ngay bây giờ, và nhiều người nữa sẽ học nó trong tương lai. Đối với nhiều người trong số họ, Python sẽ là ngôn ngữ lập trình duy nhất họ cần. Nhờ vị trí độc đáo này, Python có khả năng sẽ còn phát triển mạnh mẽ hơn nữa trong tương lai.
Ngôn ngữ này đơn giản, dễ diễn đạt và khá dễ học. Hệ sinh thái của nó rất lớn, dường như có một thư viện cho mọi thứ bạn có thể tưởng tượng. Python là một ngôn ngữ lập trình cấp cao phù hợp cho người mới bắt đầu nhờ cú pháp trực quan, cộng đồng lớn mạnh và hệ sinh thái sôi động. Nó cũng được các chuyên gia đánh giá cao trong nhiều lĩnh vực khác nhau.
Về mặt kỹ thuật, Python là một ngôn ngữ thông dịch không có giai đoạn biên dịch trung gian như các ngôn ngữ biên dịch, ví dụ như C hoặc Java. Và giống như nhiều ngôn ngữ thông dịch, nó là ngôn ngữ kiểu động. Điều này có nghĩa là bạn không cần phải chỉ định kiểu của các biến bạn sử dụng, và các biến không bị ràng buộc với một kiểu cụ thể. Điều này có cả ưu và nhược điểm. Cụ thể, bạn viết chương trình nhanh hơn, nhưng mặt khác, bạn ít nhận được sự hỗ trợ từ các công cụ để ngăn ngừa lỗi có thể xảy ra. Điều này có nghĩa là bạn sẽ chỉ phát hiện ra một số vấn đề nhất định khi chạy chương trình.
Python hỗ trợ nhiều mô hình lập trình khác nhau, bao gồm lập trình thủ tục, lập trình hướng đối tượng và lập trình chức năng. Nó đủ linh hoạt để thích ứng với nhiều nhu cầu khác nhau. Được tạo ra vào năm 1991 bởi Guido van Rossum, nó đã trở nên phổ biến, đặc biệt trong 5 năm qua, như biểu đồ Google Trends này cho thấy:
Bắt đầu với Python rất dễ dàng. Tất cả những gì bạn cần là cài đặt gói chính thức từ python.org, cho Windows, macOS hoặc Linux, và bạn đã sẵn sàng.
Nếu bạn mới làm quen với lập trình, trong các phần tiếp theo, chúng tôi sẽ hướng dẫn bạn từ con số 0 để trở thành một lập trình viên Python. Và ngay cả khi bạn hiện là một lập trình viên chuyên về một ngôn ngữ khác, Python là một ngôn ngữ đáng để tìm hiểu vì chúng tôi nghĩ rằng nó sẽ tiếp tục phát triển mạnh mẽ hơn nữa.
Các ngôn ngữ cấp thấp hơn như C++ và Rust có thể tuyệt vời cho các lập trình viên chuyên nghiệp, nhưng chúng rất khó bắt đầu và mất nhiều thời gian để thành thạo. Python, mặt khác, là một ngôn ngữ lập trình dành cho tất cả mọi người, sinh viên, những người làm công việc hàng ngày với Excel, các nhà khoa học, và nhiều đối tượng khác.
Đây là ngôn ngữ mà tất cả những ai quan tâm đến lập trình nên học đầu tiên.
Cách Cài Đặt Python
Bạn hãy truy cập trang web https://www.python.org, chọn menu Downloads (Tải xuống), chọn hệ điều hành của bạn, và một bảng điều khiển với liên kết để tải xuống gói cài đặt chính thức sẽ xuất hiện:
Hãy đảm bảo bạn làm theo hướng dẫn cụ thể cho hệ điều hành của mình. Trên macOS, bạn có thể tìm thấy hướng dẫn chi tiết tại https://flaviocopes.com/python-installation-macos/.
Cách Chạy Chương Trình Python
Có nhiều cách khác nhau để chạy chương trình Python. Cụ thể, có sự khác biệt giữa việc sử dụng các dấu nhắc tương tác (REPL), nơi bạn gõ mã Python và nó được thực thi ngay lập tức, và việc lưu chương trình Python vào một tệp rồi thực thi tệp đó.
Hãy bắt đầu với các dấu nhắc tương tác. Nếu bạn mở terminal của mình và gõ `python`, bạn sẽ thấy màn hình như thế này:
Đây là Python REPL (Read-Evaluate-Print-Loop – Đọc-Đánh giá-In-Lặp lại). Hãy chú ý đến ký hiệu `>>>` và con trỏ sau đó. Bạn có thể gõ bất kỳ mã Python nào ở đây và nhấn phím Enter để chạy nó.
Ví dụ, hãy thử định nghĩa một biến mới bằng cách sử dụng:
name = "Flavio"
và sau đó in giá trị của nó, sử dụng `print()`:
print(name)
Lưu ý: Trong REPL, bạn cũng có thể chỉ cần gõ `name`, nhấn phím Enter và bạn sẽ nhận được giá trị trở lại. Nhưng trong một chương trình, bạn sẽ không thấy bất kỳ đầu ra nào nếu bạn làm vậy, bạn cần sử dụng `print()` thay vào đó. Bất kỳ dòng Python nào bạn viết ở đây sẽ được thực thi ngay lập tức. Gõ `quit()` để thoát khỏi Python REPL này.
Bạn có thể truy cập dấu nhắc tương tác tương tự bằng cách sử dụng ứng dụng IDLE được Python tự động cài đặt:
Điều này có thể thuận tiện hơn cho bạn vì với chuột, bạn có thể di chuyển và sao chép/dán dễ dàng hơn so với terminal.
Đó là những kiến thức cơ bản đi kèm với Python theo mặc định. Tuy nhiên, chúng tôi khuyên bạn nên cài đặt IPython, có lẽ là ứng dụng REPL dòng lệnh tốt nhất mà bạn có thể tìm thấy. Cài đặt nó bằng:
pip install ipython
Đảm bảo các file thực thi của pip nằm trong đường dẫn của bạn, sau đó chạy `ipython`:
`ipython` là một giao diện khác cho phép bạn làm việc với Python REPL và cung cấp một số tính năng tuyệt vời như tô sáng cú pháp, tự động hoàn thành mã, và nhiều hơn nữa.
Cách thứ hai để chạy chương trình Python là viết mã chương trình Python của bạn vào một tệp, ví dụ `program.py`:
và sau đó chạy nó bằng `python program.py`:
Lưu ý rằng chúng ta lưu các chương trình Python với phần mở rộng `.py`, đó là một quy ước. Trong trường hợp này, chương trình được thực thi toàn bộ, không phải từng dòng một. Và đó thường là cách chúng ta chạy các chương trình. Chúng ta sử dụng REPL cho việc thử nghiệm nhanh và để học.
Trên Linux và macOS, một chương trình Python cũng có thể được biến thành một script shell, bằng cách thêm vào đầu tất cả nội dung của nó một dòng đặc biệt chỉ ra file thực thi nào sẽ được sử dụng để chạy nó. Trên hệ thống của tôi, file thực thi Python nằm ở `/usr/bin/python3`, vì vậy tôi gõ `#!/usr/bin/python3` vào dòng đầu tiên:
Sau đó, tôi có thể đặt quyền thực thi cho tệp:
chmod u+x program.py
và tôi có thể chạy chương trình với:
./program.py
Điều này đặc biệt hữu ích khi bạn viết các script tương tác với terminal. Chúng ta có nhiều cách khác để chạy chương trình Python.
Một trong số đó là sử dụng VS Code, và cụ thể là tiện ích mở rộng Python chính thức từ Microsoft:
Sau khi cài đặt tiện ích này, bạn sẽ có tính năng tự động hoàn thành mã Python và kiểm tra lỗi, định dạng tự động và kiểm tra mã (linting) với `pylint`, cùng một số lệnh đặc biệt, bao gồm:
- Python: Start REPL để chạy REPL trong terminal tích hợp:
- Python: Run Python File in Terminal để chạy tệp hiện tại trong terminal:
- Python: Run Current File in Python Interactive Window:
và nhiều hơn nữa. Chỉ cần mở bảng lệnh (View -> Command Palette, hoặc Cmd-Shift-P) và gõ `python` để xem tất cả các lệnh liên quan đến Python:
Một cách khác để dễ dàng chạy mã Python là sử dụng repl.it, một trang web rất hay cung cấp môi trường lập trình mà bạn có thể tạo và chạy ứng dụng của mình, bằng bất kỳ ngôn ngữ nào, bao gồm Python:
Đăng ký (miễn phí), sau đó dưới "create a repl" (tạo một repl) nhấp vào Python:
và bạn sẽ ngay lập tức được hiển thị một trình soạn editor với tệp `main.py`, sẵn sàng để điền nhiều mã Python:
Khi bạn đã có một số mã, nhấp vào "Run" (Chạy) để chạy nó ở phía bên phải của cửa sổ:
Chúng tôi nghĩ repl.it tiện dụng vì:
- bạn có thể dễ dàng chia sẻ mã chỉ bằng cách chia sẻ liên kết
- nhiều người có thể làm việc trên cùng một mã
- nó có thể lưu trữ các chương trình chạy dài
- bạn có thể cài đặt các gói
- nó cung cấp cho bạn một cơ sở dữ liệu key-value cho các ứng dụng phức tạp hơn
Python 2 so với Python 3
Một chủ đề quan trọng mà chúng ta nên đề cập ngay từ đầu là cuộc thảo luận về Python 2 so với Python 3. Python 3 được giới thiệu vào năm 2008, và nó đã được phát triển như phiên bản Python chính, trong khi Python 2 tiếp tục được duy trì với các bản sửa lỗi và vá lỗi bảo mật cho đến đầu năm 2020. Vào thời điểm đó, hỗ trợ Python 2 đã bị ngừng.
Nhiều chương trình vẫn được viết bằng Python 2, và các tổ chức vẫn tích cực làm việc trên chúng, vì việc di chuyển sang Python 3 không hề đơn giản và sẽ đòi hỏi rất nhiều công sức để nâng cấp các chương trình đó. Và các cuộc di chuyển lớn và quan trọng luôn mang lại những lỗi mới. Nhưng mã mới, trừ khi bạn phải tuân thủ các quy tắc do tổ chức của bạn đặt ra buộc phải sử dụng Python 2, thì luôn nên được viết bằng Python 3.
Lưu ý: Cuốn cẩm nang này tập trung vào Python 3.
Những Kiến Thức Cơ Bản Về Python
Biến trong Python
Chúng ta có thể tạo một biến Python mới bằng cách gán một giá trị cho một tên, sử dụng toán tử gán `=`. Trong ví dụ này, chúng ta gán một chuỗi với giá trị "Roger" cho biến `name`:
name = "Roger"
Đây là một ví dụ với một số:
age = 8
Tên biến có thể bao gồm các ký tự, số và ký tự gạch dưới (_). Nó không thể bắt đầu bằng một số. Đây là tất cả các tên biến hợp lệ:
name1
AGE
aGE
a11111
my_name
_name
Đây là các tên biến không hợp lệ:
123
test!
name%
Ngoài ra, mọi thứ đều hợp lệ trừ khi đó là từ khóa của Python. Có một số từ khóa như `for`, `if`, `while`, `import` và nhiều từ khác. Không cần phải ghi nhớ chúng, vì Python sẽ cảnh báo bạn nếu bạn sử dụng một trong số đó làm biến, và bạn sẽ dần nhận ra chúng là một phần của cú pháp ngôn ngữ lập trình Python.
Biểu thức và câu lệnh trong Python
Chúng ta có thể hiểu biểu thức là bất kỳ loại mã nào trả về một giá trị. Ví dụ:
1 + 1
"Roger"
Mặt khác, một câu lệnh là một hoạt động trên một giá trị. Ví dụ, đây là 2 câu lệnh:
name = "Roger"
print(name)
Một chương trình được hình thành bởi một loạt các câu lệnh. Mỗi câu lệnh được đặt trên một dòng riêng, nhưng bạn có thể sử dụng dấu chấm phẩy để có nhiều hơn một câu lệnh trên một dòng:
name = "Roger"; print(name)
Ghi chú (Comments)
Trong một chương trình Python, mọi thứ sau dấu thăng (#) đều bị bỏ qua và được coi là một ghi chú:
#đây là một dòng được ghi chú
name = "Roger" # đây là một ghi chú nội dòng
Thụt lề (Indentation) trong Python
Thụt lề trong Python có ý nghĩa đặc biệt. Bạn không thể thụt lề ngẫu nhiên như thế này:
name = "Flavio"
print(name)
Một số ngôn ngữ khác không có khoảng trắng có ý nghĩa, nhưng trong Python, thụt lề rất quan trọng. Trong trường hợp này, nếu bạn cố gắng chạy chương trình này, bạn sẽ nhận được lỗi `IndentationError: unexpected indent` (Lỗi thụt lề: thụt lề không mong muốn), bởi vì thụt lề có ý nghĩa đặc biệt.
Mọi thứ được thụt lề đều thuộc về một khối, chẳng hạn như một câu lệnh điều khiển hoặc khối điều kiện, hoặc phần thân của một hàm hoặc lớp. Chúng ta sẽ tìm hiểu thêm về những điều đó sau.
Các Kiểu Dữ Liệu Trong Python
Python có một số kiểu dữ liệu tích hợp sẵn. Nếu bạn tạo biến `name` gán cho nó giá trị "Roger", tự động biến này bây giờ đại diện cho kiểu dữ liệu Chuỗi (String).
name = "Roger"
Bạn có thể kiểm tra kiểu của một biến bằng cách sử dụng hàm `type()`, truyền biến làm đối số, và sau đó so sánh kết quả với `str`:
name = "Roger"
type(name) == str #True
Hoặc sử dụng `isinstance()`:
name = "Roger"
isinstance(name, str) #True
Lưu ý: Để thấy giá trị `True` trong Python, bên ngoài REPL, bạn cần bọc mã này trong `print()`, nhưng để rõ ràng, chúng tôi tránh sử dụng nó. Chúng ta đã sử dụng lớp `str` ở đây, nhưng điều tương tự cũng áp dụng cho các kiểu dữ liệu khác.
Đầu tiên, chúng ta có các kiểu số. Các số nguyên được biểu diễn bằng lớp `int`. Các số dấu phẩy động (phân số) có kiểu `float`:
age = 1
type(age) == int #True
fraction = 0.1
type(fraction) == float #True
Bạn đã thấy cách tạo một kiểu từ một giá trị literal, như thế này:
name = "Flavio"
age = 20
Python tự động phát hiện kiểu từ kiểu giá trị. Bạn cũng có thể tạo một biến có kiểu cụ thể bằng cách sử dụng constructor của lớp, truyền một giá trị literal hoặc tên biến:
name = str("Flavio")
anotherName = str(name)
Bạn cũng có thể chuyển đổi từ một kiểu sang kiểu khác bằng cách sử dụng constructor của lớp. Python sẽ cố gắng xác định giá trị chính xác, ví dụ như trích xuất một số từ một chuỗi:
age = int("20")
print(age) #20
fraction = 0.1
intFraction = int(fraction)
print(intFraction) #0
Đây được gọi là ép kiểu (casting). Tất nhiên, việc chuyển đổi này có thể không luôn hoạt động tùy thuộc vào giá trị được truyền. Nếu bạn viết `test` thay vì `20` trong chuỗi trên, bạn sẽ nhận được lỗi `ValueError: invalid literal for int() with base 10: 'test'`.
Đây chỉ là những điều cơ bản về các kiểu dữ liệu. Chúng ta còn rất nhiều kiểu khác trong Python:
- `complex` cho số phức
- `bool` cho giá trị boolean (logic)
- `list` cho danh sách
- `tuple` cho bộ giá trị
- `range` cho các khoảng
- `dict` cho từ điển
- `set` cho tập hợp
và nhiều hơn nữa! Chúng ta sẽ khám phá tất cả chúng sớm thôi.
Các Toán Tử Trong Python
Các toán tử Python là các ký hiệu chúng ta sử dụng để thực hiện các phép toán trên giá trị và biến. Chúng ta có thể chia các toán tử dựa trên loại phép toán chúng thực hiện:
- toán tử gán
- các toán tử số học
- các toán tử so sánh
- các toán tử logic
- các toán tử bitwise
cùng với một số toán tử thú vị khác như `is` và `in`.
Toán tử gán trong Python
Toán tử gán được sử dụng để gán một giá trị cho một biến:
age = 8
Hoặc để gán giá trị của một biến cho một biến khác:
age = 8
anotherVariable = age
Kể từ Python 3.8, toán tử `:=` hay còn gọi là *walrus operator* (toán tử hải mã) được sử dụng để gán một giá trị cho một biến như một phần của một phép toán khác. Ví dụ, bên trong một câu lệnh `if` hoặc trong phần điều kiện của một vòng lặp. Chúng ta sẽ tìm hiểu thêm về điều này sau.
Các toán tử số học trong Python
Python có một số toán tử số học: `+`, `-`, `*`, `/` (chia), `%` (phần dư), `**` (lũy thừa) và `//` (chia lấy phần nguyên):
1 + 1 #2
2 - 1 #1
2 * 2 #4
4 / 2 #2.0 (lưu ý kết quả là float)
4 % 3 #1
4 ** 2 #16
4 // 2 #2
Lưu ý: bạn không cần khoảng trắng giữa các toán hạng, nhưng có khoảng trắng sẽ giúp dễ đọc hơn. `-` cũng hoạt động như một toán tử âm một ngôi:
print(-4) #-4
`+` cũng được sử dụng để nối các giá trị chuỗi:
"Roger" + " is a good dog"
#Roger is a good dog
Chúng ta có thể kết hợp toán tử gán với các toán tử số học:
- `+=`
- `-=`
- `*=`
- `/=`
- `%=`
- ...và cứ thế
Ví dụ:
age = 8
age += 1
# age bây giờ là 9
Các toán tử so sánh trong Python
Python định nghĩa một vài toán tử so sánh:
- `==` (bằng)
- `!=` (không bằng)
- `>` (lớn hơn)
- `<` (nhỏ hơn)
- `>=` (lớn hơn hoặc bằng)
- `<=` (nhỏ hơn hoặc bằng)
Bạn có thể sử dụng các toán tử này để nhận một giá trị boolean (`True` hoặc `False`) tùy thuộc vào kết quả:
a = 1
b = 2
a == b #False
a != b #True
a > b #False
a <= b #True
Các toán tử Boolean trong Python
Python cung cấp cho chúng ta các toán tử boolean sau:
- `not` (phủ định)
- `and` (và)
- `or` (hoặc)
Khi làm việc với các thuộc tính `True` hoặc `False`, các toán tử này hoạt động như AND, OR và NOT logic, và thường được sử dụng trong việc đánh giá biểu thức điều kiện `if`:
condition1 = True
condition2 = False
not condition1 #False
condition1 and condition2 #False
condition1 or condition2 #True
Ngoài ra, hãy chú ý đến một nguồn gây nhầm lẫn tiềm ẩn:
`or` được sử dụng trong một biểu thức trả về giá trị của toán hạng đầu tiên không phải là giá trị "falsy" (`False`, `0`, `''`, `[]`..). Nếu không, nó trả về toán hạng cuối cùng.
print(0 or 1) ## 1
print(False or 'hey') ## 'hey'
print('hi' or 'hey') ## 'hi'
print([] or False) ## 'False'
print(False or []) ## '[]'
Tài liệu Python mô tả nó là `if x is false, then y, else x` (nếu x là false, thì y, ngược lại là x).
`and` chỉ đánh giá đối số thứ hai nếu đối số đầu tiên là true. Vì vậy, nếu đối số đầu tiên là "falsy" (`False`, `0`, `''`, `[]`..), nó trả về đối số đó. Nếu không, nó đánh giá đối số thứ hai:
print(0 and 1) ## 0
print(1 and 0) ## 0
print(False and 'hey') ## False
print('hi' and 'hey') ## 'hey'
print([] and False ) ## []
print(False and [] ) ## False
Tài liệu Python mô tả nó là `if x is false, then x, else y` (nếu x là false, thì x, ngược lại là y).
Các toán tử Bitwise trong Python
Một số toán tử được sử dụng để làm việc trên các bit và số nhị phân:
- `&` thực hiện phép AND nhị phân
- `|` thực hiện phép OR nhị phân
- `^` thực hiện phép XOR nhị phân
- `~` thực hiện phép NOT nhị phân
- `<<` phép dịch trái
- `>>` phép dịch phải
Các toán tử bitwise ít được sử dụng, chỉ trong những tình huống rất cụ thể, nhưng chúng đáng được đề cập.
`is` và `in` trong Python
`is` được gọi là toán tử định danh (identity operator). Nó được sử dụng để so sánh hai đối tượng và trả về true nếu cả hai là cùng một đối tượng. Chúng ta sẽ tìm hiểu thêm về đối tượng sau.
`in` được gọi là toán tử thành viên (membership operator). Nó được sử dụng để cho biết một giá trị có nằm trong một danh sách, hoặc một chuỗi khác hay không. Chúng ta sẽ tìm hiểu thêm về danh sách và các chuỗi khác sau.
Toán Tử Ba Ngôi Trong Python
Toán tử ba ngôi trong Python cho phép bạn định nghĩa một điều kiện một cách nhanh chóng. Giả sử bạn có một hàm so sánh biến `age` với giá trị `18`, và trả về True hoặc False tùy thuộc vào kết quả.
Thay vì viết:
def is_adult(age):
if age > 18:
return True
else:
return False
Bạn có thể triển khai nó với toán tử ba ngôi theo cách này:
def is_adult(age):
return True if age > 18 else False
Đầu tiên bạn định nghĩa kết quả nếu điều kiện là True, sau đó bạn đánh giá điều kiện, sau đó bạn định nghĩa kết quả nếu điều kiện là False:
<kết_quả_nếu_đúng> if <điều_kiện> else <kết_quả_nếu_sai>
Chuỗi Ký Tự Trong Python
Một chuỗi trong Python là một chuỗi các ký tự được bao quanh bởi dấu nháy đơn hoặc dấu nháy kép:
"Roger"
'Roger'
Bạn có thể gán một giá trị chuỗi cho một biến:
name = "Roger"
Bạn có thể nối hai chuỗi bằng toán tử `+`:
phrase = "Roger" + " is a good dog"
Bạn có thể thêm vào một chuỗi bằng cách sử dụng `+=`:
name = "Roger"
name += " is a good dog"
print(name) #Roger is a good dog
Bạn có thể chuyển đổi một số thành chuỗi bằng cách sử dụng constructor của lớp `str`:
str(8) #"8"
Điều này rất cần thiết để nối một số vào một chuỗi:
print("Roger is " + str(8) + " years old") #Roger is 8 years old
Một chuỗi có thể có nhiều dòng khi được định nghĩa bằng một cú pháp đặc biệt, bao quanh chuỗi bằng một bộ 3 dấu nháy:
print("""Roger is
8
years old
""")
#dấu nháy kép, hoặc dấu nháy đơn
print('''
Roger is
8
years old
''')
Một chuỗi có một tập hợp các phương thức tích hợp sẵn, như:
- `isalpha()` để kiểm tra xem một chuỗi chỉ chứa các ký tự và không rỗng
- `isalnum()` để kiểm tra xem một chuỗi chứa các ký tự hoặc chữ số và không rỗng
- `isdecimal()` để kiểm tra xem một chuỗi chứa các chữ số và không rỗng
- `lower()` để lấy phiên bản chữ thường của một chuỗi
- `islower()` để kiểm tra xem một chuỗi có phải là chữ thường hay không
- `upper()` để lấy phiên bản chữ hoa của một chuỗi
- `isupper()` để kiểm tra xem một chuỗi có phải là chữ hoa hay không
- `title()` để lấy phiên bản chữ hoa chữ cái đầu của mỗi từ trong chuỗi
- `startswith()` để kiểm tra xem chuỗi có bắt đầu bằng một chuỗi con cụ thể hay không
- `endswith()` để kiểm tra xem chuỗi có kết thúc bằng một chuỗi con cụ thể hay không
- `replace()` để thay thế một phần của chuỗi
- `split()` để tách một chuỗi dựa trên một ký tự phân tách cụ thể
- `strip()` để cắt bỏ khoảng trắng từ một chuỗi
- `join()` để nối các ký tự mới vào một chuỗi
- `find()` để tìm vị trí của một chuỗi con
và nhiều nữa. Không có phương thức nào trong số đó làm thay đổi chuỗi gốc. Thay vào đó, chúng trả về một chuỗi mới, đã sửa đổi. Ví dụ:
name = "Roger"
print(name.lower()) #"roger"
print(name) #"Roger"
Bạn cũng có thể sử dụng một số hàm toàn cục để làm việc với chuỗi. Cụ thể, chúng tôi nghĩ đến `len()`, giúp bạn lấy độ dài của một chuỗi:
name = "Roger"
print(len(name)) #5
Toán tử `in` cho phép bạn kiểm tra xem một chuỗi có chứa một chuỗi con hay không:
name = "Roger"
print("ger" in name) #True
Ký tự thoát (escaping) là một cách để thêm các ký tự đặc biệt vào một chuỗi. Ví dụ, làm thế nào để bạn thêm dấu nháy kép vào một chuỗi được bao quanh bởi dấu nháy kép?
name = "Roger"
`"Ro"Ger"` sẽ không hoạt động, vì Python sẽ nghĩ chuỗi kết thúc ở `"Ro"`. Cách giải quyết là thoát dấu nháy kép bên trong chuỗi, bằng ký tự dấu gạch chéo ngược `\`:
name = "Ro\"ger"
Điều này cũng áp dụng cho dấu nháy đơn `\'`, và cho các ký tự định dạng đặc biệt như `\t` cho tab, `\n` cho dòng mới và `\\` cho dấu gạch chéo ngược.
Cho một chuỗi, bạn có thể lấy các ký tự của nó bằng cách sử dụng dấu ngoặc vuông để lấy một mục cụ thể, dựa trên chỉ mục của nó, bắt đầu từ 0:
name = "Roger"
name[0] #'R'
name[1] #'o'
name[2] #'g'
Sử dụng một số âm sẽ bắt đầu đếm từ cuối:
name = "Roger"
name[-1] #"r"
Bạn cũng có thể sử dụng một phạm vi, bằng cách sử dụng cái mà chúng ta gọi là cắt lát (slicing):
name = "Roger"
name[0:2] #"Ro"
name[:2] #"Ro"
name[2:] #"ger"
Kiểu Dữ Liệu Boolean Trong Python
Python cung cấp kiểu `bool`, có thể có hai giá trị: `True` và `False` (viết hoa chữ cái đầu).
done = False
done = True
Các giá trị boolean đặc biệt hữu ích với các cấu trúc điều khiển có điều kiện như câu lệnh `if`:
done = True
if done:
# chạy một số mã ở đây
else:
# chạy một số mã khác
Khi đánh giá một giá trị cho `True` hoặc `False`, nếu giá trị không phải là kiểu `bool` thì chúng ta có một số quy tắc tùy thuộc vào kiểu mà chúng ta đang kiểm tra:
- các số luôn là `True` ngoại trừ số `0`
- các chuỗi chỉ là `False` khi rỗng
- các danh sách, bộ giá trị, tập hợp và từ điển chỉ là `False` khi rỗng
Bạn có thể kiểm tra xem một giá trị có phải là boolean theo cách này:
done = True
type(done) == bool #True
Hoặc sử dụng `isinstance()`, truyền 2 đối số: biến và lớp `bool`:
done = True
isinstance(done, bool) #True
Hàm toàn cục `any()` cũng rất hữu ích khi làm việc với boolean, vì nó trả về `True` nếu bất kỳ giá trị nào trong đối số iterable (ví dụ: danh sách) được truyền vào là `True`:
book_1_read = True
book_2_read = False
read_any_book = any([book_1_read, book_2_read])
Hàm toàn cục `all()` tương tự, nhưng trả về `True` nếu tất cả các giá trị được truyền vào đều là `True`:
ingredients_purchased = True
meal_cooked = False
ready_to_serve = all([ingredients_purchased, meal_cooked])
Các Kiểu Số Trong Python
Các số trong Python có thể thuộc 3 kiểu: `int`, `float` và `complex`.
Số nguyên trong Python
Số nguyên được biểu diễn bằng lớp `int`. Bạn có thể định nghĩa một số nguyên bằng một giá trị literal:
age = 8
Bạn cũng có thể định nghĩa một số nguyên bằng cách sử dụng constructor `int()`:
age = int(8)
Để kiểm tra xem một biến có kiểu `int` hay không, bạn có thể sử dụng hàm toàn cục `type()`:
type(age) == int #True
Số dấu phẩy động trong Python
Số dấu phẩy động (phân số) có kiểu `float`. Bạn có thể định nghĩa một số dấu phẩy động bằng một giá trị literal:
fraction = 0.1
Hoặc sử dụng constructor `float()`:
fraction = float(0.1)
Để kiểm tra xem một biến có kiểu `float` hay không, bạn có thể sử dụng hàm toàn cục `type()`:
type(fraction) == float #True
Số phức trong Python
Số phức có kiểu `complex`. Bạn có thể định nghĩa chúng bằng một giá trị literal:
complexNumber = 2+3j
hoặc sử dụng constructor `complex()`:
complexNumber = complex(2, 3)
Khi bạn có một số phức, bạn có thể lấy phần thực và phần ảo của nó:
complexNumber.real #2.0
complexNumber.imag #3.0
Một lần nữa, để kiểm tra xem một biến có kiểu `complex` hay không, bạn có thể sử dụng hàm toàn cục `type()`:
type(complexNumber) == complex #True
Các phép toán số học trên số trong Python
Bạn có thể thực hiện các phép toán số học trên các số, sử dụng các toán tử số học: `+`, `-`, `*`, `/` (chia), `%` (phần dư), `**` (lũy thừa) và `//` (chia lấy phần nguyên):
1 + 1 #2
2 - 1 #1
2 * 2 #4
4 / 2 #2.0
4 % 3 #1
4 ** 2 #16
4 // 2 #2
và bạn có thể sử dụng các toán tử gán rút gọn:
- `+=`
- `-=`
- `*=`
- `/=`
- `%=`
- ...và cứ thế
để nhanh chóng thực hiện các phép toán trên các biến:
age = 8
age += 1
Các Hàm Tích Hợp Sẵn Trong Python
Có 2 hàm tích hợp sẵn giúp làm việc với các số:
`abs()` trả về giá trị tuyệt đối của một số.
`round()` cho một số, trả về giá trị của nó được làm tròn đến số nguyên gần nhất:
round(0.12) #0
Bạn có thể chỉ định một tham số thứ hai để đặt độ chính xác của dấu thập phân:
round(0.12, 1) #0.1
Một số hàm tiện ích toán học và hằng số khác được cung cấp bởi thư viện chuẩn Python:
- gói `math` cung cấp các hàm và hằng số toán học chung
- gói `cmath` cung cấp các tiện ích để làm việc với số phức
- gói `decimal` cung cấp các tiện ích để làm việc với số thập phân và số dấu phẩy động
- gói `fractions` cung cấp các tiện ích để làm việc với số hữu tỉ
Chúng ta sẽ khám phá một số trong số đó riêng biệt sau.
Hằng Số Trong Python
Python không có cách nào để ép buộc một biến phải là một hằng số. Cách gần nhất bạn có thể làm là sử dụng một enum (kiểu liệt kê):
class Constants(Enum):
WIDTH = 1024
HEIGHT = 256
Và truy cập từng giá trị bằng cách, ví dụ, `Constants.WIDTH.value`. Không ai có thể gán lại giá trị đó.
Nếu không, nếu bạn muốn dựa vào các quy ước đặt tên, bạn có thể tuân thủ quy ước này: khai báo các biến không bao giờ thay đổi bằng chữ hoa:
WIDTH = 1024
Không ai có thể ngăn bạn ghi đè giá trị này, và Python sẽ không ngăn chặn điều đó. Đó là những gì hầu hết mã Python bạn sẽ thấy sử dụng.
Kiểu Dữ Liệu Enum Trong Python
Enum là những tên dễ đọc được gán cho một giá trị hằng số. Để sử dụng enum, hãy import `Enum` từ module thư viện chuẩn `enum`:
from enum import Enum
Sau đó, bạn có thể khởi tạo một enum mới theo cách này:
class State(Enum):
INACTIVE = 0
ACTIVE = 1
Khi bạn làm như vậy, bạn có thể tham chiếu `State.INACTIVE` và `State.ACTIVE`, và chúng phục vụ như các hằng số. Bây giờ nếu bạn thử in `State.ACTIVE` chẳng hạn:
print(State.ACTIVE)
nó sẽ không trả về `1`, mà là `State.ACTIVE`. Giá trị tương tự có thể được truy cập bằng số được gán trong enum: `print(State(1))` sẽ trả về `State.ACTIVE`. Tương tự khi sử dụng ký hiệu dấu ngoặc vuông `State['ACTIVE']`.
Tuy nhiên, bạn có thể lấy giá trị bằng cách sử dụng `State.ACTIVE.value`. Bạn có thể liệt kê tất cả các giá trị có thể có của một enum:
list(State) # [<State.INACTIVE: 0>, <State.ACTIVE: 1>]
Bạn có thể đếm chúng:
len(State) # 2
Nhận Dữ Liệu Đầu Vào Từ Người Dùng Trong Python
Trong một ứng dụng dòng lệnh Python, bạn có thể hiển thị thông tin cho người dùng bằng hàm `print()`:
name = "Roger"
print(name)
Chúng ta cũng có thể chấp nhận đầu vào từ người dùng, sử dụng `input()`:
print('What is your age?')
age = input()
print('Your age is ' + age)
Cách tiếp cận này lấy đầu vào tại thời điểm chạy, nghĩa là chương trình sẽ ngừng thực thi và sẽ đợi cho đến khi người dùng gõ gì đó và nhấn phím Enter. Bạn cũng có thể xử lý đầu vào phức tạp hơn và chấp nhận đầu vào tại thời điểm gọi chương trình, và chúng ta sẽ xem cách làm điều đó sau.
Điều này hoạt động cho các ứng dụng dòng lệnh. Các loại ứng dụng khác sẽ cần một cách khác để chấp nhận đầu vào.
Các Câu Lệnh Điều Khiển Trong Python
Khi bạn làm việc với các giá trị boolean và đặc biệt là các biểu thức trả về một giá trị boolean, chúng ta có thể đưa ra quyết định và đi theo các hướng khác nhau tùy thuộc vào giá trị `True` hoặc `False` của chúng.
Trong Python, chúng ta làm điều đó bằng cách sử dụng câu lệnh `if`:
condition = True
if condition == True:
# làm gì đó
Khi kiểm tra điều kiện giải quyết thành `True`, như trong trường hợp trên, khối mã của nó sẽ được thực thi.
Một khối mã là gì? Một khối mã là phần được thụt lề một cấp (thường là 4 khoảng trắng) sang bên phải:
condition = True
if condition == True:
print("The condition")
print("was true")
Khối mã có thể được hình thành bởi một dòng duy nhất hoặc nhiều dòng, và nó kết thúc khi bạn quay lại cấp độ thụt lề trước đó:
condition = True
if condition == True:
print("The condition")
print("was true")
print("Outside of the if")
Kết hợp với `if`, bạn có thể có một khối `else` được thực thi nếu kiểm tra điều kiện của `if` trả về `False`:
condition = True
if condition == True:
print("The condition")
print("was True")
else:
print("The condition")
print("was False")
Và bạn có thể có các kiểm tra `if` được liên kết khác nhau với `elif` (viết tắt của "else if") được thực thi nếu kiểm tra trước đó là `False`:
condition = True
name = "Roger"
if condition == True:
print("The condition")
print("was True")
elif name == "Roger":
print("Hello Roger")
else:
print("The condition")
print("was False")
Khối thứ hai trong trường hợp này được thực thi nếu `condition` là `False` và giá trị biến `name` là "Roger". Trong một câu lệnh `if`, bạn chỉ có thể có một kiểm tra `if` và `else`, nhưng nhiều chuỗi kiểm tra `elif`:
condition = True
name = "Roger"
if condition == True:
print("The condition")
print("was True")
elif name == "Roger":
print("Hello Roger")
elif name == "Syd":
print("Hello Syd")
elif name == "Flavio":
print("Hello Flavio")
else:
print("The condition")
print("was False")
`if` và `else` cũng có thể được sử dụng ở định dạng nội dòng, cho phép chúng ta trả về một giá trị này hoặc giá trị khác dựa trên một điều kiện. Ví dụ:
a = 2
result = 2 if a == 0 else 3
print(result) # 3
Danh Sách (Lists) Trong Python
Danh sách là một cấu trúc dữ liệu thiết yếu của Python. Chúng cho phép bạn nhóm nhiều giá trị lại với nhau và tham chiếu đến tất cả chúng bằng một tên chung. Ví dụ:
dogs = ["Roger", "Syd"]
Một danh sách có thể chứa các giá trị thuộc các kiểu khác nhau:
items = ["Roger", 1, "Syd", True]
Bạn có thể kiểm tra xem một mục có nằm trong danh sách hay không bằng toán tử `in`:
print("Roger" in items) # True
Một danh sách cũng có thể được định nghĩa là rỗng:
items = []
Bạn có thể tham chiếu đến các mục trong danh sách bằng chỉ mục của chúng, bắt đầu từ số không:
items[0] # "Roger"
items[1] # 1
items[3] # True
Sử dụng cùng một ký hiệu, bạn có thể thay đổi giá trị được lưu trữ tại một chỉ mục cụ thể:
items[0] = "Roger"
Bạn cũng có thể sử dụng phương thức `index()` để tìm chỉ mục của một giá trị:
items.index("Roger") # 0
items.index(1) # 1
Cũng như với chuỗi, sử dụng chỉ mục âm sẽ bắt đầu tìm kiếm từ cuối:
items[-1] # True
Bạn cũng có thể trích xuất một phần của danh sách, sử dụng cắt lát (slicing):
items[0:2] # ["Roger", 1]
items[2:] # ["Syd", True]
Lấy số lượng mục có trong danh sách bằng cách sử dụng hàm toàn cục `len()`, tương tự như chúng ta đã sử dụng để lấy độ dài của một chuỗi:
len(items) #4
Bạn có thể thêm các mục vào danh sách bằng cách sử dụng phương thức `append()` của danh sách:
items.append("Test")
hoặc phương thức `extend()`:
items.extend(["Test"])
Bạn cũng có thể sử dụng toán tử `+=`:
items += ["Test"]
# items là ['Roger', 1, 'Syd', True, 'Test']
Mẹo: với `extend()` hoặc `+=` đừng quên dấu ngoặc vuông. Đừng làm `items += "Test"` hoặc `items.extend("Test")` nếu không Python sẽ thêm 4 ký tự riêng lẻ vào danh sách, dẫn đến `['Roger', 1, 'Syd', True, 'T', 'e', 's', 't']`
Xóa một mục bằng phương thức `remove()`:
items.remove("Test")
Bạn có thể thêm nhiều phần tử bằng cách sử dụng:
items += ["Test1", "Test2"]
#hoặc
items.extend(["Test1", "Test2"])
Những phương thức này sẽ thêm mục vào cuối danh sách. Để thêm một mục vào giữa danh sách, tại một chỉ mục cụ thể, hãy sử dụng phương thức `insert()`:
items.insert(1, "Test") # thêm "Test" vào chỉ mục 1
Để thêm nhiều mục tại một chỉ mục cụ thể, bạn cần sử dụng cắt lát:
items[1:1] = ["Test1", "Test2"]
Sắp xếp một danh sách bằng phương thức `sort()`:
items.sort()
Mẹo: `sort()` sẽ chỉ hoạt động nếu danh sách chứa các giá trị có thể so sánh được. Ví dụ, chuỗi và số nguyên không thể so sánh trực tiếp, và bạn sẽ gặp lỗi như `TypeError: '<' not supported between instances of 'int' and 'str'` nếu bạn thử.
Phương thức `sort()` sẽ sắp xếp các chữ cái viết hoa trước, sau đó là các chữ cái viết thường. Để khắc phục điều này, hãy sử dụng:
items.sort(key=str.lower)
thay vào đó.
Sắp xếp sửa đổi nội dung danh sách gốc. Để tránh điều đó, bạn có thể sao chép nội dung danh sách bằng cách sử:
itemscopy = items[:]
hoặc sử dụng hàm toàn cục `sorted()`:
print(sorted(items, key=str.lower))
phương thức này sẽ trả về một danh sách mới, đã được sắp xếp, thay vì sửa đổi danh sách gốc.
Bộ Giá Trị (Tuples) Trong Python
Bộ giá trị (Tuples) là một cấu trúc dữ liệu cơ bản khác của Python. Chúng cho phép bạn tạo các nhóm đối tượng bất biến. Điều này có nghĩa là một khi một bộ giá trị được tạo, nó không thể sửa đổi. Bạn không thể thêm hoặc xóa các mục.
Chúng được tạo theo cách tương tự như danh sách, nhưng sử dụng dấu ngoặc đơn thay vì dấu ngoặc vuông:
names = ("Roger", "Syd")
Một bộ giá trị được sắp xếp, giống như một danh sách, vì vậy bạn có thể lấy các giá trị của nó bằng cách tham chiếu một giá trị chỉ mục:
names[0] # "Roger"
names[1] # "Syd"
Bạn cũng có thể sử dụng phương thức `index()`:
names.index('Roger') # 0
names.index('Syd') # 1
Cũng như với chuỗi và danh sách, sử dụng chỉ mục âm sẽ bắt đầu tìm kiếm từ cuối:
names[-1] #"Syd"
Bạn có thể đếm các mục trong một bộ giá trị bằng hàm `len()`:
len(names) # 2
Bạn có thể kiểm tra xem một mục có nằm trong một bộ giá trị hay không bằng toán tử `in`:
print("Roger" in names) # True
Bạn cũng có thể trích xuất một phần của một bộ giá trị, sử dụng cắt lát:
names[0:2] # ('Roger', 'Syd')
names[1:] # ('Syd',)
Lấy số lượng mục trong một bộ giá trị bằng cách sử dụng hàm toàn cục `len()`, tương tự như chúng ta đã sử dụng để lấy độ dài của một chuỗi:
len(names) #2
Bạn có thể tạo một phiên bản đã sắp xếp của một bộ giá trị bằng cách sử dụng hàm toàn cục `sorted()`:
sorted(names)
Bạn có thể tạo một bộ giá trị mới từ các bộ giá trị hiện có bằng cách sử dụng toán tử `+`:
newTuple = names + ("Vanille", "Tina")
Từ Điển (Dictionaries) Trong Python
Từ điển là một cấu trúc dữ liệu rất quan trọng của Python. Trong khi danh sách cho phép bạn tạo các tập hợp giá trị, từ điển cho phép bạn tạo các tập hợp cặp khóa / giá trị.
Đây là một ví dụ từ điển với một cặp khóa/giá trị:
dog = { 'name': 'Roger' }
Khóa có thể là bất kỳ giá trị bất biến nào như chuỗi, số hoặc bộ giá trị. Giá trị có thể là bất cứ thứ gì bạn muốn.
Một từ điển có thể chứa nhiều cặp khóa/giá trị:
dog = { 'name': 'Roger', 'age': 8 }
Bạn có thể truy cập các giá trị khóa riêng lẻ bằng cách sử dụng ký hiệu này:
dog['name'] # 'Roger'
dog['age'] # 8
Sử dụng cùng một ký hiệu, bạn có thể thay đổi giá trị được lưu trữ tại một chỉ mục cụ thể:
dog['name'] = 'Syd'
Và một cách khác là sử dụng phương thức `get()`, có tùy chọn để thêm một giá trị mặc định:
dog.get('name') # 'Roger'
dog.get('test', 'default') # 'default'
Phương thức `pop()` truy xuất giá trị của một khóa, và sau đó xóa mục đó khỏi từ điển:
dog.pop('name') # 'Roger'
Phương thức `popitem()` truy xuất và xóa cặp khóa/giá trị cuối cùng được chèn vào từ điển:
dog.popitem()
Bạn có thể kiểm tra xem một khóa có nằm trong từ điển hay không bằng toán tử `in`:
'name' in dog # True
Lấy một danh sách các khóa trong từ điển bằng cách sử dụng phương thức `keys()`, truyền kết quả của nó cho constructor `list()`:
list(dog.keys()) # ['name', 'age']
Lấy các giá trị bằng cách sử dụng phương thức `values()`, và các cặp khóa/giá trị dưới dạng bộ giá trị bằng cách sử dụng phương thức `items()`:
print(list(dog.values()))
# ['Roger', 8]
print(list(dog.items()))
# [('name', 'Roger'), ('age', 8)]
Lấy độ dài của từ điển bằng cách sử dụng hàm toàn cục `len()`, tương tự như chúng ta đã sử dụng để lấy độ dài của một chuỗi hoặc số mục trong một danh sách:
len(dog) #2
Bạn có thể thêm một cặp khóa/giá trị mới vào từ điển theo cách này:
dog['favorite food'] = 'Meat'
Bạn có thể xóa một cặp khóa/giá trị khỏi từ điển bằng câu lệnh `del`:
del dog['favorite food']
Để sao chép một từ điển, hãy sử dụng phương thức `copy()`:
dogCopy = dog.copy()
Tập Hợp (Sets) Trong Python
Tập hợp (Sets) là một cấu trúc dữ liệu quan trọng khác của Python. Chúng ta có thể nói chúng hoạt động giống như bộ giá trị (tuples), nhưng chúng không có thứ tự và chúng có thể thay đổi (mutable). Hoặc chúng ta có thể nói chúng hoạt động giống như từ điển (dictionaries), nhưng chúng không có khóa.
Chúng cũng có một phiên bản bất biến, được gọi là `frozenset`. Bạn có thể tạo một tập hợp bằng cú pháp này:
names = {"Roger", "Syd"}
Tập hợp hoạt động tốt khi bạn nghĩ về chúng như các tập hợp toán học. Bạn có thể giao hai tập hợp:
set1 = {"Roger", "Syd"}
set2 = {"Roger"}
intersect = set1 & set2 #{'Roger'}
Bạn có thể tạo một hợp nhất của hai tập hợp:
set1 = {"Roger", "Syd"}
set2 = {"Luna"}
union = set1 | set2
#{'Syd', 'Luna', 'Roger'}
Bạn có thể lấy sự khác biệt giữa hai tập hợp:
set1 = {"Roger", "Syd"}
set2 = {"Roger"}
difference = set1 - set2 #{'Syd'}
Bạn có thể kiểm tra xem một tập hợp có phải là tập hợp con của tập hợp khác không (và tất nhiên là ngược lại):
set1 = {"Roger", "Syd"}
set2 = {"Roger"}
isSuperset = set1 > set2 # True
Bạn có thể đếm các mục trong một tập hợp bằng hàm toàn cục `len()`:
names = {"Roger", "Syd"}
len(names) # 2
Bạn có thể lấy một danh sách từ các mục trong một tập hợp bằng cách truyền tập hợp đó cho constructor `list()`:
names = {"Roger", "Syd"}
list(names) #['Syd', 'Roger']
Bạn có thể kiểm tra xem một mục có nằm trong một tập hợp hay không bằng toán tử `in`:
print("Roger" in names) # True
Hàm (Functions) Trong Python
Một hàm cho phép chúng ta tạo một tập hợp các hướng dẫn mà chúng ta có thể chạy khi cần. Hàm là thiết yếu trong Python và trong nhiều ngôn ngữ lập trình khác. Chúng giúp chúng ta tạo ra các chương trình có ý nghĩa, bởi vì chúng cho phép chúng ta phân tách một chương trình thành các phần dễ quản lý và chúng thúc đẩy khả năng đọc và tái sử dụng mã.
Đây là một ví dụ hàm có tên `hello` in "Hello!":
def hello():
print('Hello!')
Đây là định nghĩa hàm. Có một tên (`hello`) và một thân hàm, tập hợp các hướng dẫn, là phần theo sau dấu hai chấm. Nó được thụt lề một cấp sang bên phải. Để chạy hàm này, chúng ta phải gọi nó. Đây là cú pháp để gọi hàm:
hello()
Chúng ta có thể thực thi hàm này một lần hoặc nhiều lần. Tên của hàm, `hello`, rất quan trọng. Nó nên mang tính mô tả, để bất kỳ ai gọi nó cũng có thể hình dung được hàm làm gì.
Một hàm có thể chấp nhận một hoặc nhiều tham số:
def hello(name):
print('Hello ' + name + '!')
Trong trường hợp này, chúng ta gọi hàm bằng cách truyền đối số:
hello('Roger')
Chúng ta gọi *tham số* là các giá trị được hàm chấp nhận bên trong định nghĩa hàm, và *đối số* là các giá trị chúng ta truyền cho hàm khi chúng ta gọi nó. Thường thì có thể nhầm lẫn về sự khác biệt này.
Một đối số có thể có giá trị mặc định được áp dụng nếu đối số không được chỉ định:
def hello(name='my friend'):
print('Hello ' + name + '!')
hello()
#Hello my friend!
Đây là cách chúng ta có thể chấp nhận nhiều tham số:
def hello(name, age):
print('Hello ' + name + ', you are ' + str(age) + ' years old!')
Trong trường hợp này, chúng ta gọi hàm bằng cách truyền một tập hợp các đối số:
hello('Roger', 8)
Các tham số được truyền bằng tham chiếu. Tất cả các kiểu trong Python đều là đối tượng, nhưng một số trong số đó là bất biến, bao gồm số nguyên, boolean, số thực, chuỗi và bộ giá trị. Điều này có nghĩa là nếu bạn truyền chúng làm tham số và bạn sửa đổi giá trị của chúng bên trong hàm, giá trị mới không được phản ánh bên ngoài hàm:
def change(value):
value = 2
val = 1
change(val)
print(val) #1
Nếu bạn truyền một đối tượng không bất biến và bạn thay đổi một trong các thuộc tính của nó, thay đổi sẽ được phản ánh bên ngoài.
Một hàm có thể trả về một giá trị, sử dụng câu lệnh `return`. Ví dụ, trong trường hợp này, chúng ta trả về tên tham số `name`:
def hello(name):
print('Hello ' + name + '!')
return name
Khi hàm gặp câu lệnh `return`, hàm kết thúc. Chúng ta có thể bỏ qua giá trị:
def hello(name):
print('Hello ' + name + '!')
return
Chúng ta có thể đặt câu lệnh return bên trong một điều kiện, đây là một cách phổ biến để kết thúc một hàm nếu một điều kiện bắt đầu không được đáp ứng:
def hello(name):
if not name:
return
print('Hello ' + name + '!')
Nếu chúng ta gọi hàm truyền một giá trị mà đánh giá thành `False`, như một chuỗi rỗng, hàm sẽ bị chấm dứt trước khi đạt đến câu lệnh `print()`.
Bạn có thể trả về nhiều giá trị bằng cách sử dụng các giá trị được phân tách bằng dấu phẩy:
def hello(name):
print('Hello ' + name + '!')
return name, 'Roger', 8
Trong trường hợp này, khi gọi `hello('Syd')`, giá trị trả về là một bộ giá trị (tuple) chứa 3 giá trị đó: `('Syd', 'Roger', 8)`.
Đối Tượng (Objects) Trong Python
Mọi thứ trong Python đều là một đối tượng. Ngay cả các giá trị của các kiểu nguyên thủy cơ bản (số nguyên, chuỗi, số thực...) cũng là đối tượng. Danh sách là đối tượng, cũng như bộ giá trị, từ điển, mọi thứ.
Các đối tượng có thuộc tính và phương thức có thể được truy cập bằng cú pháp dấu chấm. Ví dụ, hãy thử định nghĩa một biến mới kiểu `int`:
age = 8
`age` bây giờ có quyền truy cập vào các thuộc tính và phương thức được định nghĩa cho tất cả các đối tượng `int`. Điều này bao gồm, ví dụ, truy cập vào phần thực và phần ảo của số đó:
print(age.real) # 8
print(age.imag) # 0
print(age.bit_length()) #4
# phương thức bit_length() trả về số bit cần thiết để biểu diễn số này ở dạng nhị phân
Một biến giữ giá trị danh sách có quyền truy cập vào một tập hợp các phương thức khác:
items = [1, 2]
items.append(3)
items.pop()
Các phương thức phụ thuộc vào kiểu giá trị. Hàm toàn cục `id()` được Python cung cấp cho phép bạn kiểm tra vị trí trong bộ nhớ cho một đối tượng cụ thể.
id(age) # 140170065725376
Giá trị bộ nhớ của bạn sẽ thay đổi, tôi chỉ hiển thị nó làm ví dụ. Nếu bạn gán một giá trị khác cho biến, địa chỉ của nó sẽ thay đổi, bởi vì nội dung của biến đã được thay thế bằng một giá trị khác được lưu trữ ở một vị trí khác trong bộ nhớ:
age = 8
print(id(age)) # 140535918671808
age = 9
print(id(age)) # 140535918671840
Nhưng nếu bạn sửa đổi đối tượng bằng các phương thức của nó, địa chỉ vẫn giữ nguyên:
items = [1, 2]
print(id(items)) # 140093713593920
items.append(3)
print(items) # [1, 2, 3]
print(id(items)) # 140093713593920
Địa chỉ chỉ thay đổi nếu bạn gán lại một biến cho một giá trị khác. Một số đối tượng là *có thể thay đổi* (mutable), trong khi những đối tượng khác là *bất biến* (immutable). Điều này phụ thuộc vào chính đối tượng đó.
Nếu đối tượng cung cấp các phương thức để thay đổi nội dung của nó, thì nó có thể thay đổi. Ngược lại, nó bất biến. Hầu hết các kiểu được Python định nghĩa là bất biến. Ví dụ, một `int` là bất biến. Không có phương thức nào để thay đổi giá trị của nó. Nếu bạn tăng giá trị bằng cách:
age = 8
age = age + 1
#hoặc
age += 1
và bạn kiểm tra bằng `id(age)`, bạn sẽ thấy rằng `age` trỏ đến một vị trí bộ nhớ khác. Giá trị gốc không bị thay đổi, chúng ta chỉ chuyển sang một giá trị khác.
Vòng Lặp (Loops) Trong Python
Vòng lặp là một phần thiết yếu của lập trình. Trong Python, chúng ta có 2 loại vòng lặp: vòng lặp while và vòng lặp for.
Vòng lặp `while` trong Python
Vòng lặp `while` được định nghĩa bằng từ khóa `while`, và chúng lặp lại khối mã của chúng cho đến khi điều kiện được đánh giá là `False`:
condition = True
while condition == True:
print("The condition is True")
Đây là một vòng lặp vô hạn. Nó không bao giờ kết thúc. Hãy dừng vòng lặp ngay sau lần lặp đầu tiên:
condition = True
while condition == True:
print("The condition is True")
condition = False
print("After the loop")
Trong trường hợp này, lần lặp đầu tiên được chạy, vì kiểm tra điều kiện được đánh giá là `True`. Ở lần lặp thứ hai, kiểm tra điều kiện đánh giá là `False`, do đó quyền điều khiển chuyển sang lệnh tiếp theo sau vòng lặp.
Thông thường có một bộ đếm để dừng vòng lặp sau một số chu kỳ:
count = 0
while count < 10:
print("The condition is True")
count = count + 1
print("After the loop")
Vòng lặp `for` trong Python
Sử dụng vòng lặp `for`, chúng ta có thể yêu cầu Python thực thi một khối mã trong một số lần được xác định trước, ngay từ đầu, và không cần một biến riêng biệt và điều kiện để kiểm tra giá trị của nó. Ví dụ, chúng ta có thể lặp qua các mục trong một danh sách:
items = [1, 2, 3, 4]
for item in items:
print(item)
Hoặc, bạn có thể lặp một số lần cụ thể bằng cách sử dụng hàm `range()`:
for item in range(4):
print(item)
`range(4)` tạo một chuỗi bắt đầu từ 0 và chứa 4 mục: `[0, 1, 2, 3]`. Để lấy chỉ mục, bạn nên bọc chuỗi vào hàm `enumerate()`:
items = [1, 2, 3, 4]
for index, item in enumerate(items):
print(index, item)
Break và continue trong Python
Cả vòng lặp `while` và `for` đều có thể bị gián đoạn bên trong khối mã, sử dụng hai từ khóa đặc biệt: `break` và `continue`. `continue` dừng lần lặp hiện tại và yêu cầu Python thực thi lần lặp tiếp theo. `break` dừng hoàn toàn vòng lặp, và tiếp tục với lệnh tiếp theo sau khi vòng lặp kết thúc.
Ví dụ đầu tiên ở đây in `1, 3, 4`. Ví dụ thứ hai in `1`:
items = [1, 2, 3, 4]
for item in items:
if item == 2:
continue
print(item)
items = [1, 2, 3, 4]
for item in items:
if item == 2:
break
print(item)
Lớp (Classes) Trong Python
Ngoài việc sử dụng các kiểu dữ liệu do Python cung cấp, chúng ta có thể khai báo các lớp riêng của mình, và từ các lớp chúng ta có thể khởi tạo các đối tượng. Một đối tượng là một thể hiện của một lớp. Một lớp là kiểu của một đối tượng.
Chúng ta có thể định nghĩa một lớp theo cách này:
class <tên_lớp>:
# lớp của tôi
Ví dụ, hãy định nghĩa một lớp Dog (Chó):
class Dog:
# lớp Dog
Một lớp có thể định nghĩa các phương thức:
class Dog:
# lớp Dog
def bark(self):
print('WOF!')
`self` là đối số của phương thức trỏ đến thể hiện đối tượng hiện tại, và phải được chỉ định khi định nghĩa một phương thức.
Chúng ta tạo một thể hiện của một lớp, một đối tượng, bằng cú pháp này:
roger = Dog()
Bây giờ `roger` là một đối tượng mới thuộc kiểu Dog. Nếu bạn chạy:
print(type(roger))
Bạn sẽ nhận được `<class '__main__.Dog'>`
Một loại phương thức đặc biệt, `__init__()` được gọi là constructor (hàm khởi tạo), và chúng ta có thể sử dụng nó để khởi tạo một hoặc nhiều thuộc tính khi chúng ta tạo một đối tượng mới từ lớp đó:
class Dog:
# lớp Dog
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print('WOF!')
Chúng ta sử dụng nó theo cách này:
roger = Dog('Roger', 8)
print(roger.name) # 'Roger'
print(roger.age) # 8
roger.bark() # 'WOF!'
Một tính năng quan trọng của các lớp là thừa kế. Chúng ta có thể tạo một lớp Animal (Động vật) với phương thức `walk()`:
class Animal:
def walk(self):
print('Walking..')
và lớp Dog có thể thừa kế từ Animal:
class Dog(Animal):
def bark(self):
print('WOF!')
Bây giờ, việc tạo một đối tượng mới thuộc lớp `Dog` sẽ có phương thức `walk()` vì nó được thừa kế từ `Animal`:
roger = Dog()
roger.walk() # 'Walking..'
roger.bark() # 'WOF!'
Module Trong Python
Mọi tệp Python đều là một module. Bạn có thể import một module từ các tệp khác, và đó là cơ sở của bất kỳ chương trình có độ phức tạp trung bình nào, vì nó thúc đẩy tổ chức hợp lý và tái sử dụng mã.
Trong một chương trình Python điển hình, một tệp đóng vai trò là điểm vào. Các tệp khác là module và cung cấp các hàm mà chúng ta có thể gọi từ các tệp khác. Tệp `dog.py` chứa mã này:
def bark():
print('WOF!')
Chúng ta có thể import hàm này từ một tệp khác bằng cách sử dụng `import`. Và khi chúng ta làm vậy, chúng ta có thể tham chiếu hàm bằng ký hiệu dấu chấm, `dog.bark()`:
import dog
dog.bark()
Hoặc, chúng ta có thể sử dụng cú pháp `from .. import` và gọi hàm trực tiếp:
from dog import bark
bark()
Chiến lược đầu tiên cho phép chúng ta tải mọi thứ được định nghĩa trong một tệp. Chiến lược thứ hai cho phép chúng ta chọn những thứ chúng ta cần. Các module đó cụ thể cho chương trình của bạn, và việc import phụ thuộc vào vị trí của tệp trong hệ thống tệp.
Giả sử bạn đặt `dog.py` trong một thư mục con `lib`. Trong thư mục đó, bạn cần tạo một tệp rỗng có tên `__init__.py`. Điều này cho Python biết thư mục chứa các module.
Bây giờ bạn có thể chọn – bạn có thể import `dog` từ `lib`:
from lib import dog
dog.bark()
hoặc bạn có thể tham chiếu hàm cụ thể của module `dog` bằng cách import từ `lib.dog`:
from lib.dog import bark
bark()
Thư Viện Chuẩn Của Python
Python cung cấp rất nhiều chức năng tích hợp thông qua thư viện chuẩn của nó. Thư viện chuẩn là một bộ sưu tập khổng lồ gồm tất cả các loại tiện ích, từ các tiện ích toán học đến gỡ lỗi đến tạo giao diện người dùng đồ họa.
Bạn có thể tìm thấy danh sách đầy đủ các module thư viện chuẩn tại đây: https://docs.python.org/3/library/index.html
Một số module quan trọng là:
- `math` cho các tiện ích toán học
- `re` cho biểu thức chính quy
- `json` để làm việc với JSON
- `datetime` để làm việc với ngày tháng
- `sqlite3` để sử dụng SQLite
- `os` cho các tiện ích hệ điều hành
- `random` để tạo số ngẫu nhiên
- `statistics` cho các tiện ích thống kê
- `requests` để thực hiện các yêu cầu mạng HTTP
- `http` để tạo máy chủ HTTP
- `urllib` để quản lý URL
Hãy giới thiệu cách *sử dụng* một module của thư viện chuẩn. Bạn đã biết cách sử dụng các module bạn tạo, import từ các tệp khác trong thư mục chương trình. Điều đó cũng tương tự với các module được cung cấp bởi thư viện chuẩn:
import math
math.sqrt(4) # 2.0
hoặc
from math import sqrt
sqrt(4) # 2.0
Chúng ta sẽ sớm khám phá các module quan trọng nhất một cách riêng lẻ để hiểu những gì chúng ta có thể làm với chúng.
Hướng Dẫn Phong Cách PEP8 Python
Khi bạn viết mã, bạn nên tuân thủ các quy ước của ngôn ngữ lập trình bạn sử dụng. Nếu bạn học đúng các quy ước đặt tên và định dạng ngay từ đầu, bạn sẽ dễ dàng đọc mã do người khác viết hơn, và mọi người cũng sẽ thấy mã của bạn dễ đọc hơn.
Python định nghĩa các quy ước của mình trong hướng dẫn phong cách PEP8. PEP là viết tắt của *Python Enhancement Proposals* (Đề xuất Cải tiến Python) và đó là nơi diễn ra tất cả các cải tiến và thảo luận về ngôn ngữ Python.
Có rất nhiều đề xuất PEP, tất cả đều có sẵn tại: https://www.python.org/dev/peps/.
PEP8 là một trong những đề xuất đầu tiên, và cũng là một trong những đề xuất quan trọng nhất. Nó định nghĩa định dạng và cả một số quy tắc về cách viết Python một cách "pythonic" (theo phong cách Python). Bạn có thể đọc toàn bộ nội dung của nó tại đây: https://www.python.org/dev/peps/pep-0008/ nhưng đây là một tóm tắt nhanh về những điểm quan trọng bạn có thể bắt đầu:
- Thụt lề bằng dấu cách, không phải tab
- Thụt lề bằng 4 dấu cách
- Các tệp Python được mã hóa bằng UTF-8
- Sử dụng tối đa 80 cột cho mã của bạn
- Viết mỗi câu lệnh trên một dòng riêng
- Tên hàm, tên biến và tên tệp là chữ thường, với dấu gạch dưới giữa các từ (snake_case)
- Tên lớp được viết hoa, các từ riêng biệt cũng được viết hoa chữ cái đầu (CamelCase)
- Tên gói là chữ thường và không có dấu gạch dưới giữa các từ
- Các biến không nên thay đổi (hằng số) được viết bằng chữ hoa
- Tên biến nên có ý nghĩa
- Thêm các ghi chú hữu ích, nhưng tránh các ghi chú hiển nhiên
- Thêm khoảng trắng xung quanh các toán tử
- Không sử dụng khoảng trắng không cần thiết
- Thêm một dòng trống trước một hàm
- Thêm một dòng trống giữa các phương thức trong một lớp
- Bên trong các hàm/phương thức, các dòng trống có thể được sử dụng để phân tách các khối mã liên quan để giúp dễ đọc hơn
Gỡ Lỗi (Debugging) Trong Python
Gỡ lỗi là một trong những kỹ năng tốt nhất bạn có thể học, vì nó sẽ giúp bạn trong nhiều tình huống khó khăn. Mỗi ngôn ngữ đều có trình gỡ lỗi riêng. Python có `pdb`, có sẵn thông qua thư viện chuẩn.
Bạn gỡ lỗi bằng cách thêm một điểm dừng (breakpoint) vào mã của mình:
breakpoint()
Bạn có thể thêm nhiều điểm dừng nếu cần.
Khi trình thông dịch Python gặp một điểm dừng trong mã của bạn, nó sẽ dừng lại và cho bạn biết lệnh tiếp theo nó sẽ chạy là gì. Khi đó, bạn có thể thực hiện một vài điều.
Bạn có thể gõ tên của bất kỳ biến nào để kiểm tra giá trị của nó.
Bạn có thể nhấn `n` để chuyển sang dòng tiếp theo trong hàm hiện tại. Nếu mã gọi các hàm, trình gỡ lỗi không đi vào chúng và coi chúng là "hộp đen".
Bạn có thể nhấn `s` để chuyển sang dòng tiếp theo trong hàm hiện tại. Nếu dòng tiếp theo là một hàm, trình gỡ lỗi sẽ đi vào hàm đó, và bạn có thể chạy từng lệnh của hàm đó một lúc.
Bạn có thể nhấn `c` để tiếp tục thực thi chương trình bình thường, mà không cần thực hiện từng bước.
Bạn có thể nhấn `q` để dừng thực thi chương trình. Gỡ lỗi hữu ích để đánh giá kết quả của một lệnh, và đặc biệt tốt để biết cách sử dụng nó khi bạn có các vòng lặp hoặc thuật toán phức tạp mà bạn muốn sửa chữa.
Phạm Vi Biến Trong Python
Khi bạn khai báo một biến, biến đó sẽ hiển thị trong các phần của chương trình của bạn, tùy thuộc vào nơi bạn khai báo nó.
Nếu bạn khai báo nó bên ngoài bất kỳ hàm nào, biến đó sẽ hiển thị cho bất kỳ mã nào chạy sau khai báo, bao gồm cả các hàm:
age = 8
def test():
print(age)
print(age) # 8
test() # 8
Chúng ta gọi đó là một biến toàn cục.
Nếu bạn định nghĩa một biến bên trong một hàm, biến đó là một biến cục bộ, và nó chỉ hiển thị bên trong hàm đó. Bên ngoài hàm, nó không thể truy cập được:
def test():
age = 8
print(age)
test() # 8
print(age)
# NameError: name 'age' is not defined
Cách Chấp Nhận Đối Số Từ Dòng Lệnh Trong Python
Python cung cấp một số cách để xử lý các đối số được truyền khi chúng ta gọi chương trình từ dòng lệnh. Cho đến nay, bạn đã chạy các chương trình từ REPL, hoặc sử dụng:
python <filename>.py
Bạn có thể truyền các đối số và tùy chọn bổ sung khi làm như vậy, như thế này:
python <filename>.py <đối_số1>
python <filename>.py <đối_số1> <đối_số2>
Một cách cơ bản để xử lý các đối số đó là sử dụng module `sys` từ thư viện chuẩn. Bạn có thể lấy các đối số được truyền trong danh sách `sys.argv`:
import sys
print(len(sys.argv))
print(sys.argv)
Danh sách `sys.argv` chứa tên tệp được chạy làm mục đầu tiên, ví dụ `['main.py']`. Đây là một cách đơn giản, nhưng bạn phải làm rất nhiều việc. Bạn cần xác thực các đối số, đảm bảo kiểu của chúng chính xác, và bạn cần in phản hồi cho người dùng nếu họ không sử dụng chương trình đúng cách.
Python cung cấp một gói khác trong thư viện chuẩn để giúp bạn: `argparse`. Đầu tiên, bạn import `argparse` và bạn gọi `argparse.ArgumentParser()`, truyền mô tả chương trình của bạn:
import argparse
parser = argparse.ArgumentParser(
description='Chương trình này in tên những con chó của tôi'
)
Sau đó, bạn tiếp tục thêm các đối số bạn muốn chấp nhận. Ví dụ, trong chương trình này, chúng ta chấp nhận tùy chọn `-c` để truyền một màu, như thế này: `python program.py -c red`
import argparse
parser = argparse.ArgumentParser(
description='Chương trình này in giá trị HEX của một màu'
)
parser.add_argument('-c', '--color', metavar='color', required=True, help='màu cần tìm')
args = parser.parse_args()
print(args.color) # 'red'
Nếu đối số không được chỉ định, chương trình sẽ báo lỗi:
➜ python python program.py
usage: program.py [-h] -c color
program.py: error: the following arguments are required: -c
Bạn có thể đặt một tùy chọn để có một tập hợp các giá trị cụ thể, sử dụng `choices`:
parser.add_argument('-c', '--color', metavar='color', required=True, choices={'red','yellow'}, help='màu cần tìm')
➜ python python program.py -c blue
usage: program.py [-h] -c color
program.py: error: argument -c/--color: invalid choice: 'blue' (choose from 'yellow', 'red')
Có nhiều tùy chọn khác, nhưng đây là những điều cơ bản. Và có các gói cộng đồng cung cấp chức năng này, như Click và Python Prompt Toolkit.
Hàm Lambda Trong Python
Hàm Lambda (còn gọi là hàm ẩn danh) là những hàm nhỏ không có tên và chỉ có một biểu thức làm thân hàm của chúng. Trong Python, chúng được định nghĩa bằng từ khóa `lambda`:
lambda <đối_số> : <biểu_thức>
Thân hàm phải là một biểu thức duy nhất, một biểu thức, không phải một câu lệnh. Sự khác biệt này rất quan trọng. Một biểu thức trả về một giá trị, một câu lệnh thì không.
Ví dụ đơn giản nhất của một hàm lambda là một hàm nhân đôi giá trị của một số:
lambda num : num * 2
Hàm Lambda có thể chấp nhận nhiều đối số hơn:
lambda a, b : a * b
Hàm Lambda không thể được gọi trực tiếp, nhưng bạn có thể gán chúng cho các biến:
multiply = lambda a, b : a * b
print(multiply(2, 2)) # 4
Tiện ích của hàm lambda đến khi kết hợp với các chức năng Python khác, ví dụ kết hợp với `map()`, `filter()` và `reduce()`.
Đệ Quy Trong Python
Một hàm trong Python có thể tự gọi chính nó. Đó là những gì đệ quy là. Và nó có thể khá hữu ích trong nhiều tình huống.
Cách phổ biến để giải thích đệ quy là sử dụng phép tính giai thừa. Giai thừa của một số là số `n` nhân với `n-1`, nhân với `n-2`... và cứ thế, cho đến khi đạt đến số `1`:
3! = 3 * 2 * 1 = 6
4! = 4 * 3 * 2 * 1 = 24
5! = 5 * 4 * 3 * 2 * 1 = 120
Sử dụng đệ quy, chúng ta có thể viết một hàm tính giai thừa của bất kỳ số nào:
def factorial(n):
if n == 1: return 1
return n * factorial(n-1)
print(factorial(3)) # 6
print(factorial(4)) # 24
print(factorial(5)) # 120
Nếu bên trong hàm `factorial()` bạn gọi `factorial(n)` thay vì `factorial(n-1)`, bạn sẽ gây ra một đệ quy vô hạn. Python theo mặc định sẽ dừng đệ quy ở 1000 cuộc gọi, và khi giới hạn này đạt được, bạn sẽ nhận được lỗi `RecursionError`.
Đệ quy hữu ích ở nhiều nơi, và nó giúp chúng ta đơn giản hóa mã của mình khi không có cách tối ưu nào khác để làm điều đó, vì vậy, rất tốt khi biết kỹ thuật này.
Hàm Lồng Nhau Trong Python
Các hàm trong Python có thể được lồng vào bên trong các hàm khác. Một hàm được định nghĩa bên trong một hàm chỉ hiển thị bên trong hàm đó. Điều này hữu ích để tạo ra các tiện ích hữu ích cho một hàm, nhưng không hữu ích bên ngoài nó.
Bạn có thể hỏi: tại sao tôi phải "ẩn" hàm này, nếu nó không gây hại gì? Thứ nhất, bởi vì luôn tốt nhất là ẩn các chức năng cục bộ của một hàm, và không hữu ích ở nơi khác. Ngoài ra, bởi vì chúng ta có thể sử dụng closures (sẽ nói thêm về điều này sau).
Đây là một ví dụ:
def talk(phrase):
def say(word):
print(word)
words = phrase.split(' ')
for word in words:
say(word)
talk('I am going to buy the milk')
Nếu bạn muốn truy cập một biến được định nghĩa trong hàm bên ngoài từ hàm bên trong, bạn cần khai báo nó là `nonlocal` trước:
def count():
count = 0
def increment():
nonlocal count
count = count + 1
print(count)
increment()
count()
Điều này đặc biệt hữu ích với closures, như chúng ta sẽ thấy tiếp theo.
Closures Trong Python
Nếu bạn trả về một hàm lồng nhau từ một hàm, hàm lồng nhau đó vẫn có quyền truy cập vào các biến được định nghĩa trong hàm đó, ngay cả khi hàm đó không còn hoạt động nữa. Đây là một ví dụ bộ đếm đơn giản.
def counter():
count = 0
def increment():
nonlocal count
count = count + 1
return count
return increment
increment = counter()
print(increment()) # 1
print(increment()) # 2
print(increment()) # 3
Chúng ta trả về hàm bên trong `increment()`, và hàm đó vẫn có quyền truy cập vào trạng thái của biến `count` mặc dù hàm `counter()` đã kết thúc.
Decorators Trong Python
Decorators là một cách để thay đổi, tăng cường hoặc chỉnh sửa theo bất kỳ cách nào cách một hàm hoạt động. Decorators được định nghĩa bằng ký hiệu `@` theo sau là tên decorator, ngay trước định nghĩa hàm.
Ví dụ:
@logtime
def hello():
print('hello!')
Hàm `hello` này có decorator `logtime` được gán. Bất cứ khi nào chúng ta gọi `hello()`, decorator sẽ được gọi.
Một decorator là một hàm nhận một hàm làm tham số, bọc hàm đó trong một hàm bên trong thực hiện công việc nó phải làm và trả về hàm bên trong đó. Nói cách khác:
def logtime(func):
def wrapper():
# làm gì đó trước
val = func()
# làm gì đó sau
return val
return wrapper
Docstrings Trong Python
Tài liệu rất quan trọng, không chỉ để truyền đạt cho người khác mục tiêu của một hàm/lớp/phương thức/module là gì, mà nó còn truyền đạt cho chính bạn. Khi bạn quay lại mã của mình sau 6 hoặc 12 tháng, bạn có thể không nhớ tất cả kiến thức bạn đang nắm giữ trong đầu. Tại thời điểm đó, việc đọc mã của bạn và hiểu nó phải làm gì sẽ khó khăn hơn nhiều.
Ghi chú (comments) là một cách để giúp bạn (và những người khác):
# đây là một ghi chú
num = 1 #đây là một ghi chú khác
Một cách khác là sử dụng docstrings. Tiện ích của docstrings là chúng tuân theo các quy ước. Do đó, chúng có thể được xử lý tự động.
Đây là cách bạn định nghĩa một docstring cho một hàm:
def increment(n):
"""Tăng một số lên"""
return n + 1
Đây là cách bạn định nghĩa một docstring cho một lớp và một phương thức:
class Dog:
"""Một lớp đại diện cho một con chó"""
def __init__(self, name, age):
"""Khởi tạo một con chó mới"""
self.name = name
self.age = age
def bark(self):
"""Cho con chó sủa"""
print('WOF!')
Tài liệu hóa một module bằng cách đặt một docstring ở đầu tệp, ví dụ giả sử đây là `dog.py`:
"""Module Dog
Module này làm ... bla bla bla và cung cấp các lớp sau:
- Dog
...
"""
class Dog:
"""Một lớp đại diện cho một con chó"""
def __init__(self, name, age):
"""Khởi tạo một con chó mới"""
self.name = name
self.age = age
def bark(self):
"""Cho con chó sủa"""
print('WOF!')
Docstrings có thể kéo dài nhiều dòng:
def increment(n):
"""Tăng
một số
"""
return n + 1
Python sẽ xử lý chúng và bạn có thể sử dụng hàm toàn cục `help()` để lấy tài liệu cho một lớp/phương thức/hàm/module. Ví dụ, gọi `help(increment)` sẽ cho bạn kết quả này:
Help on function increment in module
__main__:
increment(n)
Increment
a number
Có nhiều tiêu chuẩn khác nhau để định dạng docstrings, và bạn có thể chọn tuân thủ tiêu chuẩn yêu thích của mình. Tôi thích tiêu chuẩn của Google: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings. Các tiêu chuẩn cho phép các công cụ trích xuất docstrings và tự động tạo tài liệu cho mã của bạn.
Introspection Trong Python
Các hàm, biến và đối tượng có thể được phân tích bằng cách sử dụng introspection (nội quan).
Đầu tiên, sử dụng hàm toàn cục `help()` chúng ta có thể lấy tài liệu nếu được cung cấp dưới dạng docstrings.
Sau đó, bạn có thể sử dụng `print()` để lấy thông tin về một hàm:
def increment(n):
return n + 1
print(increment)
# <function increment at 0x7f420e2973a0>
hoặc một đối tượng:
class Dog():
def bark(self):
print('WOF!')
roger = Dog()
print(roger)
# <__main__.Dog object at 0x7f42099d3340>
Hàm `type()` cung cấp cho chúng ta kiểu của một đối tượng:
print(type(increment))
# <class 'function'>
print(type(roger))
# <class '__main__.Dog'>
print(type(1))
# <class 'int'>
print(type('test'))
# <class 'str'>
Hàm toàn cục `dir()` cho phép chúng ta tìm ra tất cả các phương thức và thuộc tính của một đối tượng:
print(dir(roger))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bark']
Hàm toàn cục `id()` cho chúng ta thấy vị trí trong bộ nhớ của bất kỳ đối tượng nào:
print(id(roger)) # 140227518093024
print(id(1)) # 140227521172384
Nó có thể hữu ích để kiểm tra xem hai biến có trỏ đến cùng một đối tượng hay không.
Module thư viện chuẩn `inspect` cung cấp cho chúng ta nhiều công cụ hơn để lấy thông tin về các đối tượng, và bạn có thể kiểm tra nó tại đây: https://docs.python.org/3/library/inspect.html
Annotations Trong Python
Python là ngôn ngữ kiểu động. Chúng ta không cần phải chỉ định kiểu của một biến hoặc tham số hàm, hoặc giá trị trả về của một hàm.
Annotations (chú thích) cho phép chúng ta (tùy chọn) làm điều đó. Đây là một hàm không có chú thích:
def increment(n):
return n + 1
Đây là cùng một hàm với chú thích:
def increment(n: int) -> int:
return n + 1
Bạn cũng có thể chú thích các biến:
count: int = 0
Python sẽ bỏ qua các chú thích này. Một công cụ riêng biệt gọi là `mypy` có thể được chạy độc lập, hoặc tích hợp bởi các IDE như VS Code hoặc PyCharm để tự động kiểm tra lỗi kiểu tĩnh, trong khi bạn đang viết mã. Nó cũng sẽ giúp bạn phát hiện các lỗi không khớp kiểu trước khi chạy mã. Đây là một sự hỗ trợ tuyệt vời đặc biệt khi phần mềm của bạn trở nên lớn và bạn cần tái cấu trúc mã của mình.
Ngoại Lệ (Exceptions) Trong Python
Việc có một cách để xử lý lỗi là rất quan trọng, và Python cung cấp cho chúng ta xử lý ngoại lệ để làm điều đó. Nếu bạn bọc các dòng mã vào một khối `try:`:
try:
# một số dòng mã
Nếu xảy ra lỗi, Python sẽ cảnh báo bạn và bạn có thể xác định loại lỗi nào đã xảy ra bằng cách sử dụng các khối `except`:
try:
# một số dòng mã
except <LỖI1>:
# xử lý <LỖI1>
except <LỖI2>:
# xử lý <LỖI2>
Để bắt tất cả các ngoại lệ, bạn có thể sử dụng `except` mà không có bất kỳ loại lỗi nào:
try:
# một số dòng mã
except <LỖI1>:
# xử lý <LỖI1>
except:
# bắt tất cả các ngoại lệ khác
Khối `else` được chạy nếu không tìm thấy ngoại lệ nào:
try:
# một số dòng mã
except <LỖI1>:
# xử lý <LỖI1>
except <LỖI2>:
# xử lý <LỖI2>
else:
# không có ngoại lệ nào được ném, mã chạy thành công
Một khối `finally` cho phép bạn thực hiện một số thao tác trong mọi trường hợp, bất kể có lỗi xảy ra hay không:
try:
# một số dòng mã
except <LỖI1>:
# xử lý <LỖI1>
except <LỖI2>:
# xử lý <LỖI2>
else:
# không có ngoại lệ nào được ném, mã chạy thành công
finally:
# làm gì đó trong mọi trường hợp
Lỗi cụ thể sẽ xảy ra tùy thuộc vào thao tác bạn đang thực hiện. Ví dụ, nếu bạn đang đọc một tệp, bạn có thể nhận được `EOFError`. Nếu bạn chia một số cho số không, bạn sẽ nhận được `ZeroDivisionError`. Nếu bạn có vấn đề chuyển đổi kiểu, bạn có thể nhận được `TypeError`.
Hãy thử mã này:
result = 2 / 0
print(result)
Chương trình sẽ chấm dứt với một lỗi:
Traceback (most recent call last):
File "main.py", line 1, in <module>
result = 2 / 0
ZeroDivisionError: division by zero
và các dòng mã sau lỗi sẽ không được thực thi.
Thêm thao tác đó vào một khối `try:` cho phép chúng ta phục hồi một cách duyên dáng và tiếp tục chương trình:
try:
result = 2 / 0
except ZeroDivisionError:
print('Cannot divide by zero!')
finally:
result = 1
print(result) # 1
Bạn cũng có thể ném các ngoại lệ trong mã của riêng mình, sử dụng câu lệnh `raise`:
raise Exception('Đã xảy ra lỗi!')
Điều này ném một ngoại lệ chung, và bạn có thể chặn nó bằng cách sử dụng:
try:
raise Exception('Đã xảy ra lỗi!')
except Exception as error:
print(error)
Bạn cũng có thể định nghĩa lớp ngoại lệ của riêng mình, kế thừa từ Exception:
class DogNotFoundException(Exception):
pass
`pass` ở đây có nghĩa là "không làm gì" và chúng ta phải sử dụng nó khi chúng ta định nghĩa một lớp không có phương thức, hoặc một hàm không có mã.
try:
raise DogNotFoundException()
except DogNotFoundException:
print('Không tìm thấy chó!')
Câu Lệnh `with` Trong Python
Câu lệnh `with` rất hữu ích để đơn giản hóa việc làm việc với xử lý ngoại lệ. Ví dụ, khi làm việc với tệp, mỗi khi chúng ta mở một tệp, chúng ta phải nhớ đóng nó. `with` làm cho quá trình này minh bạch.
Thay vì viết:
filename = '/Users/flavio/test.txt'
try:
file = open(filename, 'r')
content = file.read()
print(content)
finally:
file.close()
Bạn có thể viết:
filename = '/Users/flavio/test.txt'
with open(filename, 'r') as file:
content = file.read()
print(content)
Nói cách khác, chúng ta có xử lý ngoại lệ ngầm định tích hợp sẵn, vì `close()` sẽ được gọi tự động cho chúng ta. `with` không chỉ hữu ích để làm việc với tệp. Ví dụ trên chỉ nhằm giới thiệu các khả năng của nó.
Cách Cài Đặt Các Gói Bên Thứ 3 Bằng `pip`
Thư viện chuẩn Python chứa một số lượng lớn các tiện ích giúp đơn giản hóa nhu cầu phát triển Python của chúng ta, nhưng không có gì có thể đáp ứng *mọi* thứ. Đó là lý do tại sao các cá nhân và công ty tạo ra các gói, và cung cấp chúng dưới dạng phần mềm mã nguồn mở cho toàn bộ cộng đồng.
Những module đó đều được thu thập ở một nơi duy nhất, Python Package Index có sẵn tại https://pypi.org, và chúng có thể được cài đặt trên hệ thống của bạn bằng cách sử dụng `pip`.
Có hơn 270.000 gói có sẵn miễn phí tại thời điểm viết bài này. Bạn nên có `pip` đã được cài đặt nếu bạn làm theo hướng dẫn cài đặt Python.
Cài đặt bất kỳ gói nào bằng lệnh `pip install`:
pip install <tên_gói>
hoặc, nếu bạn gặp khó khăn, bạn cũng có thể chạy nó thông qua `python -m`:
python -m pip install <tên_gói>
Ví dụ, bạn có thể cài đặt gói `requests`, một thư viện HTTP phổ biến:
pip install requests
và khi bạn làm vậy, nó sẽ có sẵn cho tất cả các script Python của bạn, bởi vì các gói được cài đặt toàn cục. Vị trí chính xác phụ thuộc vào hệ điều hành của bạn. Trên macOS, chạy Python 3.9, vị trí là `/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages`.
Nâng cấp một gói lên phiên bản mới nhất bằng cách sử dụng:
pip install –U <tên_gói>
Cài đặt một phiên bản cụ thể của một gói bằng cách sử dụng:
pip install <tên_gói>==<phiên_bản>
Gỡ cài đặt một gói bằng cách sử dụng:
pip uninstall <tên_gói>
Hiển thị chi tiết gói đã cài đặt, bao gồm phiên bản, trang web tài liệu và thông tin tác giả bằng cách sử dụng:
pip show <tên_gói>
List Comprehensions Trong Python
List comprehensions (tạo danh sách bằng biểu thức) là một cách để tạo danh sách một cách rất ngắn gọn. Giả sử bạn có một danh sách:
numbers = [1, 2, 3, 4, 5]
Bạn có thể tạo một danh sách mới bằng cách sử dụng list comprehension, bao gồm các phần tử của danh sách `numbers`, lũy thừa 2:
numbers_power_2 = [n**2 for n in numbers]
# [1, 4, 9, 16, 25]
List comprehensions là một cú pháp đôi khi được ưu tiên hơn các vòng lặp, vì nó dễ đọc hơn khi thao tác có thể được viết trên một dòng:
numbers_power_2 = []
for n in numbers:
numbers_power_2.append(n**2)
và hơn `map()`:
numbers_power_2 = list(map(lambda n : n**2, numbers))
Đa Hình (Polymorphism) Trong Python
Đa hình khái quát hóa một chức năng để nó có thể hoạt động trên các kiểu khác nhau. Đây là một khái niệm quan trọng trong lập trình hướng đối tượng. Chúng ta có thể định nghĩa cùng một phương thức trên các lớp khác nhau:
class Dog:
def eat():
print('Ăn thức ăn chó')
class Cat:
def eat():
print('Ăn thức ăn mèo')
Sau đó chúng ta có thể tạo các đối tượng và chúng ta có thể gọi phương thức `eat()` bất kể đối tượng thuộc lớp nào, và chúng ta sẽ nhận được các kết quả khác nhau:
animal1 = Dog()
animal2 = Cat()
animal1.eat()
animal2.eat()
Chúng ta đã xây dựng một giao diện tổng quát và bây giờ chúng ta không cần phải biết rằng một con vật là Mèo hay Chó.
Nạp Chồng Toán Tử (Operator Overloading) Trong Python
Nạp chồng toán tử là một kỹ thuật nâng cao mà chúng ta có thể sử dụng để làm cho các lớp có thể so sánh được và để chúng hoạt động với các toán tử Python. Hãy lấy một lớp Dog (Chó):
class Dog:
# lớp Dog
def __init__(self, name, age):
self.name = name
self.age = age
Hãy tạo 2 đối tượng Dog:
roger = Dog('Roger', 8)
syd = Dog('Syd', 7)
Chúng ta có thể sử dụng nạp chồng toán tử để thêm một cách để so sánh hai đối tượng đó, dựa trên thuộc tính `age`:
class Dog:
# lớp Dog
def __init__(self, name, age):
self.name = name
self.age = age
def __gt__(self, other):
return True if self.age > other.age else False
Bây giờ nếu bạn thử chạy `print(roger > syd)` bạn sẽ nhận được kết quả `True`.
Tương tự như cách chúng ta định nghĩa `__gt__()` (có nghĩa là lớn hơn), chúng ta có thể định nghĩa các phương thức sau:
- `__eq__()` để kiểm tra sự bằng nhau
- `__lt__()` để kiểm tra xem một đối tượng có nên được coi là nhỏ hơn đối tượng khác với toán tử `<` hay không
- `__le__()` cho nhỏ hơn hoặc bằng (`<=`)
- `__ge__()` cho lớn hơn hoặc bằng (`>=`)
- `__ne__()` cho không bằng (`!=`)
Sau đó, bạn có các phương thức để tương tác với các phép toán số học:
- `__add__()` phản ứng với toán tử `+`
- `__sub__()` phản ứng với toán tử `–`
- `__mul__()` phản ứng với toán tử `*`
- `__truediv__()` phản ứng với toán tử `/`
- `__floordiv__()` phản ứng với toán tử `//`
- `__mod__()` phản ứng với toán tử `%`
- `__pow__()` phản ứng với toán tử `**`
- `__rshift__()` phản ứng với toán tử `>>`
- `__lshift__()` phản ứng với toán tử `<<`
- `__and__()` phản ứng với toán tử `&`
- `__or__()` phản ứng với toán tử `|`
- `__xor__()` phản ứng với toán tử `^`
Có một vài phương thức nữa để làm việc với các toán tử khác, nhưng bạn đã hiểu ý tưởng rồi đấy.
Môi Trường Ảo (Virtual Environments) Trong Python
Việc có nhiều ứng dụng Python chạy trên hệ thống của bạn là điều phổ biến. Khi các ứng dụng yêu cầu cùng một module, đến một lúc nào đó bạn sẽ gặp phải tình huống khó khăn khi một ứng dụng cần một phiên bản của module, và một ứng dụng khác lại cần một phiên bản khác của cùng module đó.
Để giải quyết vấn đề này, bạn sử dụng môi trường ảo. Chúng ta sẽ sử dụng `venv`. Các công cụ khác hoạt động tương tự, như `pipenv`.
Tạo một môi trường ảo bằng cách sử dụng:
python -m venv .venv
trong thư mục nơi bạn muốn bắt đầu dự án, hoặc nơi bạn đã có một dự án hiện có. Sau đó chạy:
source .venv/bin/activate
Sử dụng `source .venv/bin/activate.fish` trên Fish shell
Thực thi chương trình sẽ kích hoạt môi trường ảo Python. Tùy thuộc vào cấu hình của bạn, bạn cũng có thể thấy lời nhắc terminal của mình thay đổi. Của tôi đã thay đổi từ:
➜ folder
thành
(.venv) ➜ folder
Bây giờ chạy `pip` sẽ sử dụng môi trường ảo này thay vì môi trường toàn cục.
Kết Luận
Cảm ơn bạn rất nhiều vì đã đọc cuốn cẩm nang này. Chúng tôi hy vọng nó sẽ truyền cảm hứng cho bạn tìm hiểu thêm về Python và giúp bạn có một khởi đầu vững chắc trên con đường lập trình của mình! Source: FreeCodeCamp.
MagicFlow | TechData.AI