# Lời nói đầu

Ngôn ngữ lập trình gần với ngôn ngữ tự nhiên nhất hiện nay

Giáo trình này là nội dung lược dịch từ khóa học Learn Python Programming được cung cấp bởi Programiz.  Về cơ bản, các bài học được giữ nguyên cấu trúc và nội dung kiến thức theo từng bài. Tuy nhiên, giáo trình có bổ sung một vài phần nhỏ, có thay đổi trình tự một vài chương mục hoặc bài học so với nguyên bản. Đặc biệt giáo trình có bổ sung các ví dụ, bài tập thực hành giúp người học tự kiểm tra và vận dụng kiến thức đã học.

Đây là tài liệu lược dịch, nhiều phần được diễn đạt lại chứ không dịch nguyên bản theo kiểu dịch thuật. Bạn đọc có thể tham khảo bản gốc tại [www.programiz.com/python-programming](https://www.programiz.com/python-programming) .

*@Dosing Project*\
*Contact: <dainganxanh@msn.com>*&#x20;

## SÁCH THAM KHẢO

Thông tin chi tiến về 2 cuốn sách trên có tại: <https://dainganxanh.com/sgk_python/>

![](/files/-MlIZXn0a2yAM2i-Mrrf)


# Chương I. SƠ LƯỢC

Trong chương này chúng ta sẽ tìm hiểu các vẫn đề rất cơ bản của Python nhưng chưa đi vào chi tiết. Nhiều vẫn đề sẽ được trình bày kỹ hơn, sâu hơn ở những chương sau.


# Bài 1. Cài đặt môi trường

## **Python là gì ?**

```python
print('Python là gì?') 
```

Python là một ngôn ngữ lập trình bậc cao do Guido van Rossum tạo ra và lần đầu ra mắt vào năm 1991. Python vừa hướng thủ tục (procedural-oriented), vừa hướng đối tượng (object-oriented) đồng thời có thể nhúng vào ứng dụng như một giao tiếp kịch bản (scripting interface).

Thế mạnh của Python là rất gần gũi với ngôn ngữ tự nhiên (tiếng Anh), cấu trúc rõ ràng, dễ đọc, dễ học. Python hiện nay là ngôn ngữ lập trình phổ biến rộng rãi ở châu Âu, châu Mỹ và được coi như **ngôn ngữ lập trình trường học**.

Python được dùng để phát triển các ứng dụng web, game, khoa học dữ liệu (tính toán, phân tích, khai thác dữ liệu), máy học và trí tuệ nhân tạo, …

## 1.    Cài Python

Tải về từ <https://www.python.org/downloads/> và tiến hành cài đặt (chọn phiên bản 3.8 trở lên).

Sau khi hoàn tất cài đặt có thể kiểm tra:

\-        Nhấn phím Windows gõ cmd à Enter

\-        Gõ: python --version à Enter

Lúc này sẽ hiển thị phiên bản Python đã cài đặt trên máy tính.

## 2.    Trình soạn thảo

Để lập trình theo một ngôn ngữ nào đó ta đều cần có chương trình cho phép gõ các câu lệnh và ra lệnh thực thi các câu lệnh đó. Trong các trường học, để lập trình với Pascal ta thường sử dụng FreePascal, với C ta thường dùng CodeBlock, … Với Python, ta có nhiều lựa chọn.&#x20;

Dưới đây là một số gợi ý cho người mới học Python:

### 1) Python Online Compiler

Search với từ khóa "Python Online Compiler" sẽ trả về kết quả với nhiều website khác nhau. Ví dụ <https://www.programiz.com/python-programming/online-compiler/> là một ứng dụng được cung cấp bởi Progamiz.

### **2)** **Notepad++, Sublime Text, ...**

Tải Notepad++ về tại đây: <https://notepad-plus-plus.org/downloads/>

Tải Sublime Text về tại đây: <https://www.sublimetext.com/3>&#x20;

Đặc điểm: Đơn giản, dễ sử dụng.

Nhược điểm: Phải cài đặt plugin, thiết lập thêm mới có thể run python code.

### **3)** **Thonny**

Tải về tại đây: <https://thonny.org/>

Thonny có giao diện đơn giản, cấu hình nhẹ (trên cùng một máy khởi động nhanh hơn nhiều so với Pycham hay Spyder). Hỗ trợ debug trực quan giúp ta dễ theo dõi và hình dung quá trình thực thi chương trình. Sử dụng thư viện / module chuẩn của Python phát hành (không bổ sung hay import sẵn module).

### **4)** **PyCharm Educational Edition**

Tải về tại đây: <https://www.jetbrains.com/pycharm-edu/>

PyCharm là môi trường phát triển tích hợp đa nền tảng (IDE) được phát triển bởi Jet Brains và được thiết kế đặc biệt cho Python. Tuy nhiên PyCharm khởi động khá nặng nề và yêu cầu làm việc với project. Như vậy, tùy nhu cầu sử dụng và kỹ năng lập trình mà chúng ta lựa chọn trình soạn thảo cho phù hợp. Đối với người mới bắt đầu học Python thì nên dùng Thonny để thực hành.

## 3. Chương trình đầu tiên

Viết chương tình đầu tiên với ngôn ngữ lập trình Python trên máy tính của bạn như sau:

Mở Notepad++ và gõ:

```python
print("Hello world !")
input()
```

Lưu với tên: Hello.py

Double click để chạy file Hello.py sẽ cho kết quả “`Hello world !`”

## Video lớp tập huấn 2021

{% embed url="<https://www.youtube.com/watch?v=C0bXLzXwTHc>" %}


# Bài 2. Từ khóa và định danh

1\. Từ khóa (keywords)

Từ khóa (keyword) là những tên dành riêng của Python, chúng được định nghĩa sẵn để sử dụng. Chúng ta không thể dùng keyword để đặt tên biến, tên hàm hoặc bất kỳ đối tượng nào trong chương trình.

Ví dụ một số keyword: True, False, await, else, import, pass, break, except, in, and, or, ….

Tất cả các keyword trong Python đều được viết thường, trừ 03 key word: True, False, None.

Thực tế thì không cần nhớ keyword vì khi gõ trình soạn thảo sẽ có gợi ý các keyword (ta tránh đặt tên đối tượng trùng với các keyword được gợi ý) và nếu đặt trùng tên keyword thì khi chạy chương trình sẽ báo lỗi.

2\. Định danh (Identifiers)

Định danh là việc đặt tên cho các đối tượng trong chương trình như tên hàm, tên biến, lớp,… Tên các đối tượng được đặt bằng các ký tự thường (a-z), ký tự in hoa (A-Z), chữ số (0-9) và dấu gạch dưới \_ . Ví dụ: bien1, lop, var\_1, …

Tên đối tượng không bắt đầu bằng chữ số. không dùng các ký tự đặc biệt như !, @, #, … và được phân biệt chữ hoa, chữ thường.

Lưu ý: Nên đặt tên đối tượng có tính gợi nhớ. Ví dụ, với một biến đếm, thay vì đặt d = 10 thì ta nên đặt là dem = 10 .

Ví dụ chương trình đơn giản:

```python
A = 2
a = 3
tong = a+A
tich = a*A
print(a)
print(A)
print("Tổng a và A là",tong)
print("Tích a và A là",tich)
input()
```


# Bài 3. Câu lệnh, khối lệnh và chú thích

## 1. Câu lệnh

Những hướng dẫn mà trình thông dịch Python có thể thực hiện được gọi là các câu lệnh. Ví dụ, a = 1 là một lệnh gán. Lệnh if, lệnh for, lệnh while,... là những loại lệnh khác mà chúng ta sẽ được tìm hiểu chi tiết trong các bài sau.

Thông thường mỗi dòng là một câu lệnh. Ví dụ, chương trình sau gồm 2 câu lệnh:

```python
a = 5
print(a)
```

Lưu ý: Khác với C++ và Pascal, Python không cần dấu ; để kết thúc câu lệnh.

Nhiều câu lệnh có thể được viết trên cùng một dòng nhưng phải được phân cách nhau bởi dấu chấm phẩy ; . Ví dụ, chương trình dưới đây gồm 3 câu lệnh:

```python
a = 1; b = 2; c = 3
```

Một câu lệnh cũng có thể viết trên nhiều dòng với điều kiện: sử dụng dấu nối dòng \ hoặc các dòng được đặt trong dấu ngoặc (), \[], {}.

Ví dụ:

```python
a = 1 + 2 + 3 + \
    4 + 5 + 6 + \
    7 + 8 + 9
```

Hoặc

```python
a = (1 + 2 + 3 +
    4 + 5 + 6 +
    7 + 8 + 9)
```

Hoặc

```python
colors = ['red',
        'blue', 'green'
        ]
```

### 1.2      Khối lệnh

Nếu Pascal, C++ hay Java dùng cặp ngoặc nhọn để bao một khối lệnh thì Python lại dùng cách thụt đầu dòng để đánh dấu khối lệnh.

Một khối lệnh (thường là khối lệnh của hàm, vòng lặp,...) bắt đầu với thụt lề và kết thúc với dòng đầu tiên không thụt lề. Thụt lề bao nhiêu là tùy ý nhưng chúng phải nhất quán trong suốt khối lệnh đó (các lệnh trong cùng một khối thì phải có độ thụt lề bằng nhau).

Ví dụ:

```python
for i in range(1,11):
    print(i)
    if i == 5:
        break
input()
```

Cũng chương trình trên nhưng nếu chúng ta viết lại như sau sẽ bị báo lỗi và chương trình không được thực thi:

```python
for i in range(1,11): 
  print(i); 
    if i == 5:
        break
input()
```

Lý do báo lỗi là do các lệnh thuộc vòng for không được thụt lề giống nhau (print và if là cùng cấp (cùng nằm trong lệnh for:) nên phải được thụt lề như nhau)

### 1.3      Chú thích trong Python

Chú thích trên một dòng được bắt đầu bằng dấu thăng #

Chú thích nhiều dòng được đặt trong cặp ba dấu nháy đơn """

Ví dụ: Cả 2 chương trình dưới đây đều in ra chữ “Hello”

```python
#This is a comment
#print out Hello
print('Hello')
input() 
```

\# input() để dừng màn hình như readline;(Pascal) hay getch();(C++)

Và

```python
"""This is also a perfect example of
multi-line comments"""
print('Hello')
input()
```

### 1.4      Docstring – Chú thích đối tượng

Docstring là viết tắt của Documentation string - chuỗi tài liệu, dùng để chú thích tóm tắt chức năng cho những đối tượng (mô đun, hàm, method,…).

Ba dấu nháy kép được sử dụng để viết docstring như ví dụ dưới đây:

```python
def nhandoi(num):
    """Hàm nhân đôi giá trị nhập vào"""
    return 2*num
```

Viết như trên, sau này ta có thể tra cứu (in ra) docstring của hàm để biết chức năng của hàm đã viết. Cách in như sau:

```python
print(nhandoi.__doc__)
```

Kết quả sẽ là: “`Hàm nhân đôi giá trị nhập vào`”<br>


# Bài 4. Nhập, xuất dữ liệu

## 1. In dữ liệu ra màn hình

Để in dữ liệu ra màn hình ta sử dụng câu lện print(). Ví dụ

```python
print('In dòng này ra màn hình ')
a = 5
print('Giá trị được gán cho a là', a)
input()
```

## 2. Nhập dữ liệu từ bàn phím

Để nhận một giá trị được nhập từ bàn phím ta sử dụng lệnh input(). Ví dụ: Viết chương trình tính tổng 2 số nguyên được nhập từ bàn phím.

```python
num1 = int(input())
num2 = int(input())
sum = num1 + num2
print(sum)
input()
```

Ghi chú: mặc định, nếu không khai báo kiểu dữ liệu nhập vào thì Python sẽ nhận giá trị nhập từ bàn phím ở dạng xâu (string). Vì vậy dể nhập số ta phải khai báo kiểu dữ liệu trước khi nhập như câu lệnh số 1 và câu lệnh số 2 ở chương trình trên.

## 3. Định dạng dữ liệu và thông báo

Hiện thông báo khi nhập và in:

```python
num1 = input('Nhập số thứ nhất: ')
num2 = input('Nhập số thứ hai: ')
sum = int(num1) + int(num2)
print('Tổng của hai số vừa nhập là:', sum)
print('Tổng của {0} và {1} là {2}'.format(num1, num2, sum))
print('Tổng của {1} và {0} là {2}'.format(num1, num2, sum))
input()
```

Hãy xem sự khác nhau giữa 3 lệnh print() trên đây khi chạy chương trình. Câu lệnh print thứ 2 và 3 có sử dụng định dạng dữ liệu để lồng vào thông báo.

Kết quả chạy chương trình:

> Nhập số thứ nhất: 5\
> Nhập số thứ hai: 9\
> Tổng của hai số vừa nhập là: 14.0\
> Tổng của 5 và 9 là 14.0\
> Tổng của 9 và 5 là 14.0


# Bài 5. Toán tử và lệnh gán

## 1. Toán tử số học

| Operator | Meaning                             | Example     |
| -------- | ----------------------------------- | ----------- |
| +        | Cộng                                | x + y+ 2    |
| -        | Trừ                                 | x - y- 2    |
| \*       | Nhân                                | x \* y      |
| /        | Chia                                | x / y       |
| %        | Lấy phần dư của phép chia (mod)     | x % y       |
| //       | Lấy phần nguyên của phép chia (div) | x // y      |
| \*\*     | Lỹ thừa                             | x\*\*y (xy) |

Ví dụ:

```python
x = 19
print('x =',x)
y = 4
print('y =',y)
print('x + y =',x+y)
print('x - y =',x-y)
print('x * y =',x*y)
print('x / y =',x/y)
print('x // y =',x//y)
print('x % y =',x%y)
print('x ** y =',x**y)
```

Output:

> x = 19\
> y = 4\
> x + y = 23\
> x - y = 15\
> x \* y = 76\
> x / y = 4.75\
> x // y = 4\
> x % y = 3\
> x \*\* y = 130321

## 2. Toán tử so sánh

| Operator | Meaning           | Example |
| -------- | ----------------- | ------- |
| >        | Lớn hơn           | x > y   |
| <        | Nhỏ hơn           | x < y   |
| ==       | Bằng              | x == y  |
| !=       | Khác              | x != y  |
| >=       | Lớn hơn hoặc bằng | x >= y  |
| <=       | Nhỏ hơn hoặc bằng | x <= y  |

Ví dụ:

```python
x = 10
y = 12
print('x > y is',x>y)
print('x < y is',x<y)
print('x == y is',x==y)
print('x != y is',x!=y)
print('x >= y is',x>=y)
print('x <= y is',x<=y)
```

Output:

> x > y is False\
> x < y is True\
> x == y is False\
> x != y is True\
> x >= y is False\
> x <= y is True

## 3. Toán tử logic

| Operator | Meaning                              | Example |
| -------- | ------------------------------------ | ------- |
| and      | Và: True khi cả hai đều True         | x and y |
| or       | Hoặc: True nếu một trong hai là True | x or y  |
| not      | Không: True khi False                | not x   |

Ví dụ:

```python
x = True
y = False
print('x and y is',x and y)
print('x or y is',x or y)
print('not x is',not x)
```

Output:

> x and y is False\
> x or y is True\
> not x is False

## 4. Toán tử bitwise

Toán tử bitwise dùng để làm việc với các toán hạng nhị phân.

Ví dụ, với x = 10 (0000 1010) và y = 4 (0000 0100)

| Operator | Meaning                        | Example                  |
| -------- | ------------------------------ | ------------------------ |
| &        | Bitwise AND                    | x & y                    |
| \|       | Bitwise OR                     | x \| y                   |
| \~       | Bitwise NOT                    | \~x                      |
| ^        | Bitwise XOR                    | x ^ y                    |
| >>       | Bitwise dịch phải              | x >> 2 (dịch phải 2 bít) |
| <<       | Bitwise dịch trái (chuyển trái | x << 2 (dịch trái 2 bít) |

```python
x = 10
print('x =',x)
y = 4
print('x',y)
print('x & y =',x&y)
print('x | y =',x|y)
print('~x =',~x)
print('x ^ y =',x^y)
print('x >> 2 =',x>>2)
print('x << 2 =',x<<2)
input()
```

Output:

> x = 10\
> x 4\
> x & y = 0\
> x | y = 14\
> \~x = -11\
> x ^ y = 14\
> x >> 2 = 2\
> x << 2 = 40

## 5. Toán tử gán

| Operator | Example   | Equivalent to |
| -------- | --------- | ------------- |
| =        | x = 5     | x = 5         |
| +=       | x += 5    | x = x + 5     |
| -=       | x -= 5    | x = x - 5     |
| \*=      | x \*= 5   | x = x \* 5    |
| /=       | x /= 5    | x = x / 5     |
| %=       | x %= 5    | x = x % 5     |
| //=      | x //= 5   | x = x // 5    |
| \*\*=    | x \*\*= 5 | x = x \*\* 5  |
| &=       | x &= 5    | x = x & 5     |
| \|=      | x \|= 5   | x = x \| 5    |
| ^=       | x ^= 5    | x = x ^ 5     |
| >>=      | x >>= 5   | x = x >> 5    |
| <<=      | x <<= 5   | x = x << 5    |


# Bài 6. Biến, Hằng

## 1. Biến và câu lệnh gán

Biến là một đối tượng trong chương trình. Mỗi biến sẽ có một vị trí riêng trong bộ nhớ để lưu trữ dữ liệu (giá trị) được gán. Biến trong Python được đặt theo nguyên tắc định danh (xem lại mục 2.2).

Câu lệnh gán giá trị cho biến là: \<tên biến> = \<giá trị gán cho biến>

Biến trong Python không cần khai báo trước, không gần khai báo kiểu dữ liệu. Khi đặt tên và gán giá trị Python tự động nhận dạng và tùy biến theo kiểu dữ liệu được gán.

Ví dụ 1:

```python
number = 10
number = 1.1
print(number)
input()
```

Ở ví dụ trên, biến được đặt tên là number, được gán giá trị là 10 sau đó lại được gán giá trị là 1.1. Như vậy sau sau 2 câu lệnh trên giá trị của biến number lưu sẽ là 1.1. Kết quả khi chạy chương trình là: 1.1.

Ví dụ 2.

```python
website = "dainganxanh.com"
print(website)
# assigning a new variable to website
website = "hocdeday.com"
print(website)
input()
```

Ở ví dụ này, biến có tên website được gán giá trị là dainganxanh.com sau đó được in ra bởi câu lệnh print(website). Sau đó lại được gán giá trị mới và thực hiện lệnh in lần 2.

Kết quả sẽ là:

> dainganxanh.com\
> hocdeday.com

Python cho phép gán nhiều giá trị cho nhiều biến trong cùng một câu lệnh gán. Ví dụ:

```python
a, b, c = 5, 3.2, "Hello"
print (a)
print (b)
print (c)
```

Các toán tử gán khác được trình bày trong mục 9.5

## 2. Hằng

Hằng là một loại biến đặc biệt, giá trị của hằng là không đổi trong suốt chương trình sau lần gán giá trị đầu tiên. Tên hằng được viết hoàn toàn bằng chữ hoa và dấu gạch dưới (nếu cần).

Ví dụ:

```python
PI = 3.14 # đây là hằng
bankinh = 20 # đây là biến
```

Trong thực tế, việc đặt tên Hằng bằng các ký tự in hoa là để phân biệt với biến chứ nó không có tác dụng ngăn cản việc gán giá trị mới cho Hằng.

Đối với các chương trình lớn, Hằng số thường được khai báo riêng trong một mô-đun. Mô-đun là một tệp riêng chứa các biến, hàm, v.v. được nhập (import) vào tệp chính.

Ví dụ:

Tạo một file mới với tên “hang.py” trong cùng thư mục với file “thuchanh.py”.

\-         Nội dung file hang.py:

```python
PI = 3.14
GRAVITY = 9.8
```

\-         Nội dung file thuchanh.py:

```python
import hang
print(hang.PI)
print(hang.GRAVITY)
input()
```

Chạy file thuchanh.py sẽ cho kết quả là:

> 3.14\
> 9.8


# Bài 7. Kiểu dữ liệu

**Bài này giới thiệu sơ lược các kiểu dữ liệu trong Python.** Trong chương IV chúng ta sẽ tìm hiểu sâu về từng kiểu dữ liệu và các thao tác xử lý đối với từng kiểu dữ liệu.

## 1. Kiểu số (numbers)

Python sử dụng các kiểu số thường dùng trong lập trình như số nguyên (int), số thực (float) và số phức (complex). Ta có thể dùng hàm type() để kiểm tra xem dữ liệu thuộc kiểu nào và dùng lện  isinstance() để xem số liệu có thuộc về một kiểu nào đó hay không.

Ví dụ:

```python
a = 5
print(a, "is of type", type(a))

a = 2.0
print(a, "is of type", type(a))

a = 1+2j
print(a, "is complex number?", isinstance(1+2j,complex))
```

Output:

> 5 is of type \<class 'int'>
>
> 2.0 is of type \<class 'float'>
>
> (1+2j) is complex number? True

### -         **Số nguyên (int)**

Số nguyên trong Python không gới hạn độ dài mà chỉ phụ thuộc vào bộ nhớ của máy.

### -         **Số thực (float)**

Số thực khác số nguyên bởi phần thập phân. Số chữ thập phân tối đa trong Python là 15 chữ số.

### -         **Số phức (complex)**

Số phức được biểu diễn dạng x + yj. Trong đó x là phần thực, y là phần ảo.

### -         **Phân số (fraction)**

Phân số thuộc thư viện faction có dạng: Fraction(\<Tử\_số>, \<Mẫu\_số>). Trước khi sử dụng cần import thư viện fraction.

Ví dụ:

```python
a = 1234567890123456789 
    # print(a) cho kết quả là 1234567890123456789
b = 0.1234567890123456789 
    # print(b) cho kết quả là 0.123456789012345678
c = 1+2j 
    # print(c) cho kết quả là 1+2j
```

Lưu ý: kết quả print(b) đã bị cắt bớt về 15 chữ số trong phần thập phân.

## 2. Kiểu xâu (str)

Trong Python, string là một dãy các ký tự Unicode. Unicode bao gồm mọi ký tự trong tất cả các ngôn ngữ và mang lại tính đồng nhất trong mã hóa.

Một xâu được đặt trong cặp nháy đơn hoặc nháy kép ', ". Xâu trên nhiều dòng được đặt trong cặp ba nháy đơn hoặc 3 nháy kép ''', """.

Ví dụ:

```python
s = 'This is a string'
print(s)
s = "This is a string"
print(s)
s = '''A multiline
string'''
print(s)
```

Output:

> This is a string
>
> This is a string
>
> A multiline
>
> string

Một xâu trong Python được đánh chỉ sổ cho từng ký tự bắt đầu từ 0. Để truy cập vào phần tử string ta sử dụng tham số chỉ số trong ngặc vuông \[]

Ví dụ:

```python
s = 'Hello world!'
print("s[4] = ", s[4])
print("s[6:11] = ", s[6:11])
```

Output:

> s\[4] =  o
>
> s\[6:11] =  world

## 3. Kiểu Danh sách (list)

Trong Python, list được biểu diễn bằng dãy các giá trị, được phân tách nhau bằng dấu phẩy, nằm trong dấu \[]. Các danh sách có thể chứa nhiều mục với kiểu khác nhau, nhưng thông thường là các mục có cùng kiểu.

List là kiểu dữ liệu tương tự kiểu mảng trong Pascal.

Kiểu list có dạng:

```python
a = [1, 2.2, 'python']
```

Các phần tử trong list có chỉ số bắt đầu từ 0. Ta có thể gọi 1 hay nhiều phần tử trong list bằng các đối số trong ngoặc \[].

Ví dụ:

```python
a = [5,10,15,20,25,30,35,40]
print("a[2] = ", a[2])
print("a[0:3] = ", a[0:3])
print("a[5:] = ", a[5:])
```

Output:

> a\[2] =  15
>
> a\[0:3] =  \[5, 10, 15]
>
> a\[5:] =  \[30, 35, 40]

Ta có thể thay đổi giá trị của phần tử trong list bằng cách gán giá trị mới vào phần tử.

Ví dụ:

```python
a = [1, 2, 3]
a[2] = 4 # gán 4 vào phần tử a[2]
print(a)
```

Output: `[1, 2, 4]`

### **List trong list**

Bạn cũng có thể tạo các list lồng nhau (danh sách chứa trong danh sách).

Với list lồng nhau ta có thể xử lý dữ liệu dạng mảng đa chiều trong C++ và Pascal.

Ví dụ:

```python
# nested list, dạng phức hợp
my_list = ["mouse", [8, 4, 6], ['a']]
# nested list, dạng mảng 2 chiều
my_list = [[1, 2, 3], [2, 3, 4]]
```

Trên đây chỉ là một số kiến thức cơ bản về list. Chúng ta tạm thời dừng lại ở đây để tiếp cận các kiến thức khác trước khi tìm hiểu sâu hơn về list trong một bài khác.

## 4. Kiểu Tuple

Kiểu tube giống như list, chỉ khác ở chỗ tuple không cho gán giá trị cho phần tử trong tuple (không cho thay đổi giá trị các phần tử của tuple).

Các phần tử của tuple được đặt trong dầu ngoặc đơn.

```python
t = (5,'program', 1+3j)
```

Truy xuất phần tử của tube bằng cách sử dụng ngoặc vuông với đối số là chỉ số phần tử:

```python
t = (5,'program', 1+3j)
print("t[1] = ", t[1])
print("t[1:3] = ", t[1:3])
```

Output:

> t\[1] =  program
>
> t\[1:3] =  ('program', (1+3j))

## 5. Kiểu Set

Set tương tự như list nhưng các phần tử là khắc biệt và không có thứ tự, không cho truy xuất từng phần tử trong set.

Ví dụ:

```python
a = {1,2,2,3,3,3}
print(a)
```

Output:

> {1, 2, 3}

## 6. Kiểu Dictionary (dict)

Dictionary là tập hợp các cặp khóa giá kèm trị không có thứ tự. Nó thường được sử dụng khi chúng ta có một số lượng lớn dữ liệu. Các dictionary được tối ưu hóa để trích xuất dữ liệu với điều kiện bạn phải biết được khóa để lấy giá trị.

Ví dụ:

```python
d = {1:'value','key':2}
print("d[1] = ", d[1]);
print("d['key'] = ", d['key']);
```

Output:

> d\[1] =  value
>
> d\['key'] =  2

## 7. Ép kiểu  - chuyển đổi kiểu dữ liệu

Chúng ta có thể chuyển đổi giữa các kiểu dữ liệu khác nhau bằng cách sử dụng hàm chuyển đổi kiểu khác nhau như int() (kiểu số nguyên), float() số thập phân, str() chuỗi,...

Ví dụ:

```python
float(5)        # chuyển 5 về kiểu số thực => 5.0
int(10.6)       # chuyển 10.6 về kiểu số nguyên => 10
int(-10.6)      # chuyển -10.6 về kiểu số nguyên => -10
float('2.5')    # chuyển xâu ‘2.5’ về kiểu số thực => 2.5
str(25)         # chuyển số 25 về kiểu xâu => ‘25’
set([1,2,3])    # chuyển list về kiểu set => {1, 2, 3}
tuple({5,6,7})  # chuyển set về kiểu tuple => (5, 6, 7)
list('hello')   # chuyển xâu về kiểu list => ['h', 'e', 'l', 'l', 'o']
dict([[1,2],[3,4]])     # chuyển list về kiểu dict => {1: 2, 3: 4}
dict([(3,26),(4,44)])   # chuyển tuple về kiểu dict => {3: 26, 4: 44}
```

Chuyển đổi về dict đòi hỏi các giá trị kiểu khác phải theo cặp.


# Bài 8. Thao tác với tệp (cơ bản)

Để thao tác với tệp (đọc hoặc ghi dữ liệu) trong Python có nhiều cách khác nhau. Trong đó sử dụng câu lện with là đơn giản và hiệu quả, ngắn gọn và an toàn. Cú pháp như sau:

```python
with open("tenfile.txt","mode") as tenbien:
   # Thực hiện các thao tác với file
```

Với câu lệnh trên file được mở để thao tác đồng thời sẽ tự động đóng file sau khi thao tác.

Các chế độ mở file (mode) thường dùng như sau:

| **Mode** | **Tác động**                                                                                                                                          |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| r        | Mở chỉ để đọc. (mặc định)                                                                                                                             |
| w        | <p>Mở để ghi, nếu đã có nội dung thì sẽ ghi đè.</p><p>Nếu file chưa tồn tại thì tạo file mới.</p>                                                     |
| x        | Tạo file độc quyền mới (exclusive creation) và ghi nội dung, nếu file đã tồn tại thì chương trình sẽ báo lỗi.                                         |
| a        | <p>Mở file chế độ ghi tiếp.</p><p>Nếu file đã tồn tại rồi thì nó sẽ ghi tiếp nội dung vào cuối file, nếu file không tồn tại thì tạo một file mới.</p> |
| t        | Mở file ở chế độ văn bản (mặc định)                                                                                                                   |
| b        | Mở file ở chế độ nhị phân                                                                                                                             |

## 1. Đọc file

Ví dụ, Ta có file ‘vao.txt’ được lưu cùng thư mục với file Python.

Mở file để đọc ta dùng phương thức **with open(‘tenfile’, ‘mode’) as tenbien**. Ta có thể gán mode hoặc bỏ qua vì Python mặc định mở file là để đọc.

**Đọc toàn bộ file**

```python
with open("vao.txt") as f:
   print(f.read())
```

**Đọc từng dòng (1 dòng)**

```python
with open("vao.txt") as f:
    print(f.readline())
```

**Đọc từng dòng (2 dòng)**

```python
with open("vao.txt") as f:
    print(f.readline())
    print(f.readline())
```

**Đọc từng dòng đến hết file**

```python
with open("vao.txt") as f:
    for i in f:
        print(i, end='')
```

**Lưu ý:** Tất cả dữ liệu đọc từ file text đều có kiểu string. Vì vậy nếu là các chữ số, khi đọc vào phải ép kiểu thành number mới có thể thực hiện các phép toán số học.

Chúng ta có thể thay đổi vị trí con trỏ tệp hiện tại bằng cách sử dụng phương thức seek (). Tương tự, phương thức tell () trả về vị trí hiện tại của con trỏ tệp (theo số byte).

Ví dụ:

```python
with open('input.txt') as f:
    print(f.read())
    print(f.tell())    
    f.seek(0)
    doc = f.read(5)
    print(doc)
    print(f.tell())
    f.seek(13)
    print(f.read())
```

**Giải thích:**\
Câu lệnh 1 để mở file đọc\
Câu lệnh 2 đọc toàn bộ file và in ra màn hình\
Câu lệnh 3: in ra màn hình thông báo vị trí con trỏ hiện thời (là vị trí cuối file)\
Câu lệnh 4: chuyển con trỏ về vị trí 0 (đầu file)\
Câu lệnh 5: đọc 5 ký tự (5 bytes) và gán vào biến doc\
Câu lệnh 6: in biến doc ra màn hình\
Câu lệnh 7: in ra màn hình thông báo vị trí con trỏ hiện thời\
Câu lệnh 8: Chuyển con trỏ đến vị trí 13\
Câu lệnh 9: đọc đến cuối file và in ra màn hình

Output:

`Learn Python together`\
`21`\
`Learn`\
`5`\
`together`

## 2. Mở file để ghi

Mở file để ghi ta dùng phương thức **with open(‘tenfile’, ‘mode’) as tenbien**. Mode thường dùng là **w** (ghi đè) hoặc **a** (ghi tiếp).

Cả mode **a** và **w** đều sẽ tạo file mới để ghi nếu file mở để ghi chưa tồn tại.

Ví dụ:

```python
with open("ra.txt",'w') as f:
    f.write("Ghi dong chu nay vao file ra.txt")
```

Lưu ý: Để ghi dữ liệu vào file text ta chuyển về kiểu xâu trước khi ghi (nếu dữ liệu là số).

Chương trình sau sẽ báo lỗi:

```python
a = "Ghi dong chu nay vao file ra.txt"
b = 994
with open("ra.txt",'w') as f:
    f.write(a + '\n')
    f.write(b)
```

Ta sửa lỗi như sau:

```python
a = "Ghi dong chu nay vao file ra.txt"
b = 994
with open("ra.txt",'w') as f:
    f.write(a + '\n')
    f.write(str(b))
```


# Bài 9. Tổng quan về Python


# Bài tập chương 1

## Bài tập 1

Bạn hãy viết chương trình tạo ra biến a = 438, b = 636 và thực hiện hiển thị ra màn hình tổng, hiệu, tích thương của a và b giống như sau:

`a + b = {P1}`\
`a - b = {P2}`\
`a * b = {P3}`\
`a / b = {P4}`

Trong đó:\
{P1} là tổng của a và b.\
{P2} là hiệu của a và b.\
{P3} là tích của a và b.\
{P4} là thương của a và b.

Bài tập&#x20;2
-------

Cho trước biến length = 7.8 và biến width = 3.5, đây là 2 biến chỉ chiều dài và chiều rộng của hình chữ nhật. Bạn hãy viết chương trình hiển thị ra màn hình diện tích và chu vi của hình chữ nhật giống như sau:

`Diện tích: {P1}`\
`Chu vi: {P2}`

Với {P1} và {P2} lần lượt là diện tích và chu vi của hình chữ nhật.

Bài tập&#x20;3
-------

Viết chương trình tính diện tích hình tam giác có chiều cao h và độ dài cạnh đáy a được nhập từ bàn phím (chiều cao và độ dài cạnh đáy của hình tam giác này là một số nguyên). Sau đó, in ra màn hình "Diện tích tam giác = {P}" với {P} là diện tích của hình tam giác.

Ví dụ nếu bạn nhập a = 10, h = 12 thì màn hình sẽ hiển thị ra:

Diện tích tam giác = 60

Bài tập&#x20;4
-------

Viết chương trình Python sử dụng các toán tử gán để thao tác với hai số tự nhiên a và Total được nhập từ bàn phím. Sau đó, in ra màn hình các giá trị của Total trên từng dòng như sau:

`b += a sẽ cho giá trị của b là: {A}`\
`b -= a sẽ cho giá trị của b là: {B}`\
`b *= a sẽ cho giá trị của b là: {C}`\
`b /= a sẽ cho giá trị của b là: {D}`\
`b **= a sẽ cho giá trị của b là: {E}`\
`b //= a sẽ cho giá trị của b là: {F}`\
`b %= a sẽ cho giá trị của b là: {G}`

Với A, B, C, D, E, F, G, H là các giá trị của Total sau khi sử dụng các toán tử tương ứng.

## Bài tập 5

Cho 4 số nguyên x, y, z và t được nhập từ file input.txt (mỗi số cách nhau bởi một khoảng trắng). \
Bạn hãy viết chương trình để kiểm tra 4 giá trị này có thoả mãn điều kiện x > y và z < t hay không. \
Ghi vào file out.txt "Result evaluation is True" nếu 4 số thoả mãn điều kiện; "Kết quả là False" không thỏa mãn điều kiện.

Ví dụ nếu file input.txt có nội dung là `5 6 8 3` thì nội dung file out.txt là:\\

`Kết quả là False`

nếu file input.txt có nội dung là `10 3 7 13` thì nội dung file out.txt là:\\

`Kết quả là True`


# Chương II. RẼ NHÁNH - LẶP


# Bài 10. Cấu trúc rẽ nhánh if…else

## 1. Câu lệnh if

Cú pháp:

```
if <điều kiện>:
    <câu/khối lệnh thực thi khi điều kiện đúng>
```

Lưu ý: Câu lệnh hoặc khối lệnh trong lệnh if phải được thụt đầu dòng như nhau.

Ví dụ:

```python

# If the number is positive, we print an appropriate message
num = 3
if num > 0:
    print(num, "là một số nguyên.")
print("Dòng này không liên quan đến lệnh if.")
num = -1
if num > 0:
    print(num, "là một số nguyên.")
print("Dòng này cũng không liên quan đến lệnh if.")# If the number is positive, we print an appropriate message
num = 3
if num > 0:
    print(num, "là một số nguyên.")
print("Dòng này không liên quan đến lệnh if.")
num = -1
if num > 0:
    print(num, "là một số nguyên.")
print("Dòng này cũng không liên quan đến lệnh if.")
```

Output:

> 3 là một số nguyên\
> Dòng này không liên quan đến lệnh if.\
> Dòng này cũng không liên quan đến lệnh if.

## 2. Câu lệnh if…else

Cú pháp:

```
if <điều kiện>:
    <câu/khối lệnh thực thi khi điều kiện đúng>
else:
    <câu/khối lệnh thực thi khi điều kiện không đúng>
```

Ví dụ:

```
num = 3
if num >= 0:
    print("Lớn hơn hoặc bằng 0.")
else:
    print("Số âm.")
```

Output: `Lớn hơn hoặc bằng 0.`

## 3. Câu lệnh if...elif...else

Cú pháp:

```python
if <điều kiện 1>:
    <câu/khối lệnh thực thi khi điều kiện 1 đúng>
if <điều kiện 2>:
    <câu/khối lệnh thực thi khi điều kiện 2 đúng>
else:
    <câu/khối lệnh thực thi khi các điều kiện trên không đúng>
```

Ví dụ:

```python
num = 3.4
if num > 0:
    print("Số dương.")
elif num == 0:
    print("Zero.")
else:
    print("Số âm.")
```

Output: `Số dương.`

## 4. Câu lệnh if lồng nhau

Ví dụ:

```python
num = float(input("Enter a number: "))
if num >= 0:
    if num == 0:
        print("Zero")
    else:
        print("Positive number")
else:
    print("Negative number")
```

Output 1:

`Enter a number: 5`\
`Positive number`

Output 2:

`Enter a number: -1`\
`Negative number`

Output 3:

`Enter a number: 0`\
`Zero`


# Bài 11. Cấu trúc lặp với for

## 1. Vòng lặp for

Cú pháp:

```python
for <biến lặp> in <biến liên tục>:
    <câu/khối lệnh thực thi bằng số phần tử của biến liên tục lần>
```

Biến liên tục có thể là list, tuple, string.

Ví dụ:

```python
numbers = [6, 5, 3, 8, 4, 2, 5, 4, 11]
sum = 0
for n in numbers:
        sum += n  # hoặc sum = sum+n
print("Tổng = ", sum)   
```

Output: `Tổng = 48`

## 2. Hàm range()

Hàm range() để tạo ra một dãy số. Ví dụ, range(10) sẽ tạo một dãy số liên tục từ 0 đến 9 (10 số).

Hàm range(số bắt đầu, số kết thúc, khoảng cách giữa hai số) được sử dụng để tạo dãy số tùy chỉnh. Nếu không đặt khoảng cách giữa hai số thì Python sẽ hiểu mặc định nó bằng 1.

Hàm range() không lưu tất cả các giá trị trong bộ nhớ mà nó lưu giá trị bắt đầu, giá trị kết thúc và khoảng cách giữa hai số từ đó tạo ra số tiếp theo trong dãy.

Để range() xuất ra tất cả các giá trị, bạn cần sử dụng đến hàm list() giống như ví dụ dưới đây:

```python
print(range(10))
print(list(range(10)))
print(list(range(2, 8)))
print(list(range(2, 20, 3))) # dãy từ 2 đến 20, mỗi phần tử cách nhau 3 đơn vị
```

Output:

> range(0, 10)\
> \[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\
> \[2, 3, 4, 5, 6, 7]\
> \[2, 5, 8, 11, 14, 17]

Dùng hàm range() trong lệnh for:

```python
# Ví dụ này có dùng hàm len() để lấy độ dài của list
biends = ['pop', 'rock', 'jazz']
for i in range(len(biends)):
        print("I like", biends[i])
```

Output:

> I like pop\
> I like rock\
> ​I like jazz

## 3. Vòng lặp for else

Ví dụ:

```python
digits = [0, 1, 5]
for i in digits:
    print(i)
else:
    print("No items left.")
```

Output:

> 0\
> 1\
> 5\
> No items left.

Sử dung break() để thoát khỏi vòng lặp

```python
ten_hs = input('Nhập tên học sinh: ')
diem = {'Nam': 90, 'Anh': 99, 'Tan': 77}
for n in diem:
    if n == ten_hs:
        print(diem[n])
        break
else:
    print('Không tìm thấy tên học sinh vừa nhập.')
input()
```

Output 1: Nhập vào: Hanh

`Không tìm thấy tên học sinh vừa nhập.`

Output 2: Nhập vào: Anh

`99`

### **Bài tập vận dụng:**

Cho file input.txt gồm 2 dòng:

Dòng 1: gồm một số nguyên (n < 100).

Dòng 2: Dãy a gồm n + 2 phần tử, mỗi phần tử là một ký tự cách nhau bởi 1 khoảng trắng.

**Yêu cầu:**

\- Đọc n phần tử đầu tiên của dãy a và ghi vào file output.txt.

\- Tính tổng các phần tử và ghi vào dòng thứ 2.

`Hướng dẫn:`\
&#x20;           `Chuyển xâu có dạng: 1 2 3 4 5 thành list có dạng [1,2,3,4,5]`\
&#x20;           `a = ‘1 2 3 4 5’`\
&#x20;           `li = list(a.split(‘ ‘)`


# Bài 12. Cấu trúc lặp với while

## 1. Vòng lặp while

Cú pháp

```python
while <điều kiện>:
    <câu/khối lệnh thực thi khi còn thỏa điều kiện>
```

Ví dụ:

```python
n = float(input("Nhập n: "))
sum = 0
i = 1
while i <= n:
    sum += i
    i +=1    # tăng biến đếm
print("Tổng: ", sum)
input()
```

Output: Bạn tự code để xem output nhé!

## 2. Vòng lặp while - else

Ví dụ:

```python
counter = 0
while counter < 3:
    print("Trong vòng lặp while")
    counter = counter + 1
else:
    print("Trong else, xảy ra khi điều kiện while không còn thỏa.")
```

Output:

`Trong vòng lặp while`\
`Trong vòng lặp while`\
`Trong vòng lặp while`\
`Trong else, xảy ra khi điều kiện while không còn thỏa.`


# Bài 13. Lệnh break và continue

Lệnh break và continue dùng để xử lý trong trường hợp muốn thoát khỏi vòng lặp với một điều kiện ngoài điều kiện chung của vòng lặp bình thường.

Ví dụ sử dụng break:

```python
for val in "Học để biết":
    if val == "b":
        break
    print(val)
print("Kết thúc.")
input()
```

Output: Chương trình gặp chữ “b” sẽ thoát khỏi vòng lặp

`H`\
`ọ`\
`c`\
`đ`\
`ể`\
`Kết thúc.`

Ví dụ sử dụng continue:

```python
for val in "Học để biết":
    if val == " ":
        continue
    print(val)
print("Kết thúc.")
input()
```

Output: chương trình gặp khoảng trắng sẽ bỏ qua để thực thi tiếp vòng lặp

`H`\
`ọ`\
`c`\
`đ`\
`ể`\
`b`\
`i`\
`ế`\
`t`\
`Kết thúc.`

Ngoài ra còn có lệnh pass dùng để thoát khỏi một hàm hay vòng lặp (không thực thi hàm hay vòng lặp đó trong chương trình.

Ví dụ:

```python
for val in sequence:
    pass
# Hoặc
def function(args):
    pass
# Hoặc
class Example:
    pass
```


# Bài tập chương 2

Bài tập&#x20;1
-------

Cho trước 3 số nguyên x, y, z được từ file. Bạn hãy viết chương trình ghi kết quả ra file output.dat theo yêu cầu sau:

Nếu x là số chẵn, kiểm tra xem y có lớn hơn hoặc bằng 20 hay không. Nếu y >= 20, ghi ra dòng chữ y is greater than or equal to 20; ngược lại, ghi ra dòng chữ y is less than 20.

Nếu x là số lẻ, kiểm tra xem z có lớn hơn hoặc bằng 30 hay không. Nếu z >= 30, ghi ra dòng chữ z is greater than or equal to 30; ngược lại, ghi ra dòng chữ z is less than 30.

Ví dụ:

Với x = 20, y = 33, z = 15 thì kết quả là `y is greater than 20`\
Vì x % 2 == 0 và y > 20\
Với x = 15, y = 23, z = 20  thì kết quả là `z is less than 30`\
Vì x % 2 != 0 và z < 30

Bài tập&#x20;2
-------

Cho số nguyên age chỉ tuổi của vật nuôi được nhập từ file input.txt, bạn hãy hiển thị ra màn hình theo yêu cầu sau:

Nếu age <= 0 thì hiển thị "This can hardly be true"\
Nếu age == 1 thì hiển thị "About 1 human year"\
Nếu age == 2 thì hiển thị "About 2 human years"\
Nếu age > 2 thì hiển thị "Over 5 human years.&#x20;

Ví dụ nếu bạn nhập age = 3 thì hiển thị "Over 5 human years"\
Nếu bạn nhập age = 1 thì hiển thị "About 1 human year"

Bài tập&#x20;3
-------

Cho số nguyên dương n được nhập từ bàn phím, bạn hãy viết chương trình hiển thị ra màn hình tổng các số từ 1 tới n. Ví dụ nếu bạn nhập n = 5 thì màn hình sẽ hiển thị ra:&#x20;15

Giải thích: 1 + 2 + 3 + 4 + 5 = 15.

Bài tập&#x20;4
-------

Cho 2 số nguyên a và b được nhập từ file input.txt (a cách b bởi một khoảng trắng), hãy viết chương trình ghi các số lẻ từ a tới b ra file out.txt.&#x20;Ví dụ nếu nội dung file input.txt là `3 9` thì nội dung file out.txt là:&#x20;`24`

Giải thích: 3 + 5 + 7 + 9 = 24.&#x20;Đầu vào luôn đảm bảo b > a.

Bài tập&#x20;5
-------

Cho chuỗi s được nhập từ file input.txt, bạn hãy viết chương trình ghi các kí tự khác kí tự 'y' trong chuỗi s ra file out.txt. Ví dụ nội dung file input.txt là "`python`" thì nội dung file out.txt là:

`p`\
`t`\
`h`\
`o`\
`n`

Bài tập&#x20;6
-------

Cho số nguyên a được nhập từ bàn phím, bãn hãy viết chương trình hiển thị ra màn tích của a với các số từ 1 đến 5.&#x20;Ví dụ nếu bạn nhập a = 10 thì màn hình sẽ hiển thị ra:

`10 * 1 = 10`\
`10 * 2 = 20`\
`10 * 3 = 30`\
`10 * 4 = 40`\
`10 * 5 = 50`

Bài tập&#x20;7
-------

Cho hai số nguyên a và b được nhâp từ file input.txt (a cách b bởi một khoảng trắng), hãy viết chương trình đếm số các số chẵn và số các số lẻ trong khoảng từ a tới b. Sau đó ghi vào file out.txt thông tin sau:

Ví dụ file input.txt là: `1 10` \
thì nội dung file out.txt là:

`Number of even numbers: 5`\
`Number of odd numbers: 5`

Bài tập&#x20;8
-------

Cho số nguyên n được nhập vào từ file in.txt, bạn hãy viết chương trình ghi ra file out.txt tổng của dãy số 1/2 + 2/3 + ... + n/n+1.&#x20;Yêu cầu chỉ hiển thị 2 số thập phân sau phẩy.

Ví dụ:&#x20;\
file in.txt là: `10` file out.txt sẽ là: `7.98`\
file in.txt là: `20` file out.txt sẽ là: `17.35`


# Chương III. HÀM & MODULE


# Bài 14. Hàm

Hàm là một nhóm các lệnh có liên quan đến nhau được dùng để thực hiện một tác vụ, nhiệm vụ cụ thể nào đó. Hàm giúp chia chương trình Python thành những phần nhỏ hơn - giúp chương trình có tổ chức và dễ quản lý hơn.

Hàm còn có một tác dụng vô cùng quan trọng nữa là tránh việc phải lặp lại code để thực thi những tác vụ tương tự nhau, giúp code gọn hơn và có thể tái sử dụng.

## 1. Cú pháp

```python
def <tenham>(<biến/danh sách biến>):
   """docstring (ghi chú về chức năng của hàm/
   hàm không nhất thiết phải có docstring)"""
   <lệnh/ khối lệnh thực hiện nhiệm vụ>
```

Ví dụ:

```python
def chao(name):
    """
    Đây là hàm in một câu chào hỏi 
    với biến nhập vào là tên người được chào
    """
    print("Hello, " + name + ". Good morning!")
chao('Dosing')         # gọi hàm

# Output: Hello, Dosing. Good morning!
```

Lệnh gọi docstring (đọc chú thích của hàm)

`print(<tên hàm>.__doc__)`

Ví dụ:

```python
print(chao.__doc__)

# Output: Đây là hàm in một câu chào hỏi với biến nhập vào là tên người được chào
```

## 2. Câu lệnh return

Lệnh return thường được dùng để thoát hàm và trở về nơi mà tại đó hàm được gọi.

```python
def trituyetdoi(num):
    """Hàm trả về giá trị tuyệt đối"""
    if num >= 0:
        return num
    else:
        return -num
print(trituyetdoi(2))
print(trituyetdoi(-4))

# Output:
#    2
#    4
```

## 3. Phạm vi biến

Biến trong hàm được gọi là biến cục bộ (local) có phạm vi trong hàm chứa biến. Biến ngoài hàm có phạm vi trong toàn chương trình được gọi là biến toàn cục (global).

Ví dụ

```python
def my_func():
        x = 10
        print("Giá trị của biến x trong hàm:", x)
x = 20
my_func()
print("Giá trị của biến x bên ngoài hàm:", x)

# Output:
#        Giá trị của biến x trong hàm: 10 
#        Giá trị của biến x ngoài hàm: 20
```

*Ở ví dụ trên, nếu không khai báo biến x = 20 ở dòng 4 thì chương trình sẽ báo lỗi.*

Để khai báo một biến trong hàm là biến toàn cục (global) thì ta dùng từ khóa global. Ví dụ như sau:

```python
def my_func():
        x = 10
        global y
        y = 15
        print("Giá trị của biến x,y trong hàm:", x, y)
x = 20
my_func()
print("Giá trị của biến x,y bên ngoài hàm:", x, y)
```

## 4. Phân loại hàm

Hàm trong Python được phân làm 2 loại gồm&#x20;

* [Hàm có sẵn (build-in) ](https://www.programiz.com/python-programming/built-in-function)
* [Hàm do người dùng xây dựng (user-defined).](https://www.programiz.com/python-programming/user-defined-function)


# Bài 15. Tham số hàm

## 1. Tham số

Xét ví dụ sau:

```python
def hello(name, msg):
    """Hàm in ra lời chào
    với tên và lời chào được nhập vào"""
    print("Xin chào", name + ', ' + msg)

hello("Python", "Có gì mới không?")
```

Output: `Xin chào Python, Có gì mới không?`

Ở ví dụ trên, hàm hello có 2 tham số là name và msg. Ở câu lệnh gọi hàm ta truyền vào tham số name = "Python", tham số msg = "Có gì mới không?"

Lời gọi hàm phải được truyền đủ tham số. Nếu ta truyền thiếu hoặc thừa tham số thì chương trình sẽ báo lỗi. Ví dụ khi ta gọi hàm như sau

```python
hello("Python")
```

Output: `TypeError: hello() missing 1 required positional argument: 'msg'`

## 2. Tham số mặc định

```python
def hello(name, msg="Có gì hay???"):
    """Hàm in ra lời chào"""
    print("Xin chào", name + ', ' + msg)

hello("Python", "Có gì mới không?")
hello("Python")    # không còn báo lỗi 
```

Output:

> Xin chào Python, Có gì mới không?> \
> Xin chào Python, Có gì hay???

Như vậy, hàm có tham số mặc định thì khi gọi hàm ta có không truyền tham số hàm sẽ lấy giá trị mặc định   để thực thi.

***Lưu ý:*** Giá trị mặc định có thể gán cho tất cả các tham số của hàm. Nhưng nếu trong hàm có một tham số không được gán giá trị mặc định thì tất cả các tham số có gán giá trị mặc định phải được đặt sau tham số không có giá trị mặc định.

```python
def hello(name="tên gì đó", msg="Có gì hay???"): # hàm này OK
    ...
    
def hello(name="tên gì đó", msg):     # hàm này sẽ báo lỗi khi được gọi 
    ...
```

## 3. Tham số keywords

```python
def hello(name, msg="Có gì hay???"):
    """Hàm in ra lời chào"""
    print("Xin chào", name + ', ' + msg)

# 2 keyword arguments
hello(name = "Python",msg = "Bạn có khỏe không?")

# 2 keyword arguments (out of order)
hello(msg = "Bạn có khỏe không?",name = "Python") 

# 1 positional, 1 keyword argument
hello("Python", msg = "Bạn có khỏe không?") 
```

Output:

> Xin chào Python, Bạn có khỏe không?> \
> Xin chào Python, Bạn có khỏe không?> \
> Xin chào Python, Bạn có khỏe không?

## 4. Tham số tùy ý

Lệnh gọi hàm không giới hạn số lượng tham số truyền vào.

```python
def hello(*Names):
    for name in Names:
        print("Hello", name)

hello("Python", "C++", "Java", "Pascal")
```

Output:

> Hello Python\
> Hello C++> \
> Hello Java> \
> Hello Pascal


# Bài 16. Đệ quy

## Hàm đệ quy

Một hàm được gọi là đệ quy khi nó tự gọi lại chính nó nhiều lần đến khi vấn đề được giải quyết.&#x20;

Một hàm đệ quy cần phải có điều kiện chấm dứt đề dừng việc tự gọi lại chính nó. Hàm đệ quy chấm dứt khi mỗi lần gọi đệ quy thì số giải pháp của vấn đề được giảm bớt và tiến gần đến điều kiện cơ sở. Một điều kiện cơ sở là điểm mà ở đó vấn đề được giải quyết và không cần đệ quy thêm lần nào nữa.

Sau đây là một ví dụ về một hàm đệ quy để tìm giai thừa của một số nguyên.

Giai thừa của một số là tích của tất cả các số nguyên từ 1 đến số đó. Ví dụ, giai thừa của 6 (được ký hiệu là 6!) Là `1 * 2 * 3 * 4 * 5 * 6 = 720`.

```python
def giaithua(x):
    """Hàm tính giai thừa của một số nguyên"""
    if x == 1:
        return 1
    else:
        return (x * giaithua(x-1))

num = 6
print(f"Giai thừa của {num} là {giaithua(num)}")
```

Khi thực hiện gọi hàm giaithua trên đây với tham số truyền vào là num= 6 thì các bước thực hiện sẽ lần lượt như sau:

> 6 \* giaithua(5)                             # gọi hàm lần thứ nhất> \
> 6 \* 5 \* giaithua(4)                       # gọi lần 2> \
> 6 \* 5 \* 4 \* giaithua(3)                 # gọi lần 3> \
> 6 \* 5 \* 4 \* 3 \* giaithua(2)           # gọi lần 4> \
> 6 \* 5 \* 4 \* 3 \* 2 \* giaithua(1)     # gọi lần 5 - gặp điều kiện cơ sở (x=1)> \
> 6 \* 5 \* 4 \* 3 \* 2 \* 1                      # giá trị trả về sau lần gọi cuối

Python mặc định độ duyệt sâu tối đa của hàm đệ quy là 1000 lần. Nếu tự gọi vượt quá số lần cho phép chương trình không trả về kết quả mà báo lỗi `RecursionError: maximum recursion depth exceeded`.

### Ví dụ 2

Tính tổng dãy số nguyên a gồm n phần tử từ 1 đến n.

```python
def tong(n):
    if n == 1:
        return 1
    else:
        return n + tong(n-1)
    
n = 6
print(tong(n))
```

### Ví dụ 3

In dãy fibonacy

```python
def fb(x):
    if (x == 0 or x == 1):
        return x;
    else:
        return fb(x-1) + fb(x-2)
    
n = 10
for i in range(n+1):
    print(fb(i), end=' ')
```

### Ưu điểm của Đệ quy&#x20;

Đệ quy làm cho mã code gọn gàng, mạch lạc .&#x20;

Một nhiệm vụ phức tạp có thể được chia thành các bài toán con đơn giản hơn bằng cách sử dụng đệ quy.&#x20;

Việc tạo trình tự với đệ quy dễ dàng hơn so với việc sử dụng một số phép lặp lồng nhau.

### Nhược điểm của Đệ quy&#x20;

Đôi khi logic đằng sau đệ quy rất khó theo dõi.&#x20;

Cuộc gọi đệ quy rất tốn kém (không hiệu quả) vì chúng chiếm nhiều bộ nhớ và thời gian.&#x20;

Các hàm đệ quy rất khó gỡ lỗi.


# Bài 17. Hàm ẩn danh

Hàm ẩn danh không dùng từ khóa def và tên hàm. Cú pháp hàm ẩn danh như sau:

```python
<tên biến> = lambda <tham số>: <biểu thức / giá trị trả về>
```

Ví dụ

```python
tong3 = lambda a, b, c : a + b + c
x = 5
y = 7
z = 3
print(tong3(x,y,z)
```

Output: `15`

Các hàm lambda có thể có nhiều tham số nhưng chỉ có một biểu thức. Các hàm Lambda có thể được sử dụng ở bất cứ nơi nào trong chương trình.

Ví dụ 2

```python
double = lambda x: x * 2
print(double(5))
```

Chương trình trên tương đương với:

```python
def double(x):
   return x * 2
print(double(5))
```

Hàm Lambda được sử dụng cùng với các hàm có sẵn như filter (), map (), v.v.&#x20;

Khi sử dụng cùng với các hàm có sẵn (build-in), lambda thường được dùng như một tham số của hàm build-in.&#x20;

Ví dụ

```python
my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(filter(lambda x: (x%2 == 0) , my_list))
print(new_list)
```

Output: `[4, 6, 8, 12]`

```python
# Program to double each item in a list using map()

my_list = [1, 5, 4, 6, 8, 11, 3, 12]

new_list = list(map(lambda x: x * 2 , my_list))

print(new_list)
```

Output: `[2, 10, 8, 12, 16, 22, 6, 24]`

Trong ví dụ trên, lambda là tham số thứ nhất của hàm filter(), map().


# Bài 18. Biến toàn cục và cục bộ

Biến cục bộ (local) là biến có hiệu lực trong nội bộ của hàm. Biến toàn cục (global) là biến có hiệu lực trong toàn chương trình - tuy nhiên nó cần được khai báo để có thể hoạt động trong thân hàm.&#x20;

## 1. Biến toàn cục (global)

Ví dụ 1

```python
x = "biến toàn cục - global"

def foo():
    print("x trong hàm:", x)

foo()
print("x ngoài hàm:", x)
```

Output:

`x trong hàm: biến toàn cục - global`\
`x ngoài hàm: biến toàn cục - global`

Xét ví dụ 2

```python
x = "biến global "

def foo():
    x = x * 2
    print(x)

foo()
```

Output: `Báo lỗi "UnboundLocalError: local variable 'x' referenced before assignment"`

Biến x được khai báo ngoài hàm và được gán giá trị là "biến global". Tuy nhiên, câu lệnh ở dòng 4 có sử dụng biến x trong hàm (lúc này x được coi làm một local và chưa được khai báo) nên phát sinh lỗi.

Để khắc phục lỗi này ta có thể khai báo biến x trong hàm là biến toàn cục với keyword global như sau:

```python
x = "biến global "

def foo():
    global x
    x = x * 2
    print(x)

foo()
```

Ouput: `biến global biến global`

## 2. Biến cục bộ (local)

Thông thường, chúng ta khai báo một biến bên trong hàm để tạo một biến cục bộ. Ví dụ như sau:

```python
def foo():
    y = "local"
    print(y)

foo()
```

Output: `local`

Tuy nhiên, chương trình sau đây sẽ báo lỗi

```python
def foo():
    y = "local"

foo()
print(y)
```

Output: `NameError: name 'y' is not defined`

Nguyên nhân: Chương trình trên gọi biến y ngoài hàm trong khi biến y là một biến local (chỉ có phạm vi hoạt động trong hàm, biến y sẽ bị hủy khi thoát khỏi hàm foo())

## 3. Biến không cục bộ (nonlocal)

Sử dụng khái niệm global, local trong trường hợp hàm trong hàm. Xét ví dụ dau:

```python
x = 5
def outer():
    x = "local"
    def inner():
        nonlocal x
        x = "nonlocal"
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print(x)
```

Output: `inner: nonlocal`\
`outer: nonlocal`\
`5`

***Giải thích:*** \
Vì inner là một hàm trong hàm nên để biến x trong hàm inner() có thể hoạt động ngoài thân hàm inner() thì phải khai báo với từ khóa nonlocal. Lúc này x trong hàm inner() chính là x của hàm outter().

## 4. Ví dụ tổng hợp

### Ví dụ 4.1

```python
x = "global "

def foo():
    global x
    y = "local"
    x = x * 2
    print(x)
    print(y)

foo()
```

Output: `global global`&#x20;\
`local`

### Ví dụ 4.2

```python
x = 5

def foo():
    x = 10
    print("local x:", x)

foo()
print("global x:", x)
```

Output: `local x: 10`\
`global x: 5`

Ở ví dụ này biến global và local cùng có tên là x. Tuy nhiên do phạm vi hoạt động của global và local là rạch ròi nên chương trình là hợp lệ và hoạt động bình thường.

### Ví dụ 4.3

Xem xét kỹ ví dụ sau để thấy phạm vi biến global, local và nonlocal

```python
x = 25
y = '- hai lăm'
def foo():
    x = 20
    y = '- hai mươi'
    def bar():
        nonlocal x
        x = 35
        
        global y
        y = '- ba lăm'

    print("\t x - y trước khi gọi hàm bar: \t", x, y)
    bar()
    print("\t x - y sau khi gọi hàm bar: \t", x, y)

print('x - y ngoài hàm,trước khi gọi hàm foo: \t', x, y)
foo()
print("x - y ngoài hàm, sau khi gọi foo: \t", x, y)
```

Output:

`x - y ngoài hàm,trước khi gọi hàm foo: 	 25 - hai lăm`\
&#x9; `x - y trước khi gọi hàm bar: 	 20 - hai mươi`\
&#x9; `x - y sau khi gọi hàm bar: 	 35 - hai mươi`\
`x - y ngoài hàm, sau khi gọi foo: 	 25 - ba lăm`

##


# Bài 19. Module

Một module (đọc là mô-đun) là một file chứa mã Python, ví dụ file vidumodule.py là một module và module này có tên là vidumodule (tên module là tên file).

Chúng ta sử dụng các module để chia nhỏ các chương trình lớn thành các tệp nhỏ để dễ quản lý và sắp xếp. Đồng thời sử dụng module cho phép ta dùng lại các hàm đã xây dựng mà không phải viết lại.

## **1. Tạo và sử dụng module**

**Bước 1.** Gõ nội dung dưới đây và lưu file với tên ***vidumodule.py*** . Lưu ý: file ***vidumodule.py*** được lưu cùng thư mục với file chương trình Python (thường là file main.py).&#x20;

```python
# Module vidu
def cong(a, b):
   """phép cộng 2 số """

   kq = a + b
   return kq
```

**Bước 2.** Sử dụng mudule vừa tạo

```python
import vidumodule

s = vidumodule.cong(4,5)
print(s)
```

Output: `9`

Chú thích: \
Câu lệnh dòng 1: Câu lệnh import để gọi (nhập) module có tên là vidumodule vào chương trình.\
Câu lệnh dòng 3: Sử dụng hàm cong() đã được định nghĩa trong module vidumodule .&#x20;

## 2. Câu lệnh import

Câu lệnh import dùng để gọi (nhập/nhúng) module vào chương trình Python. Sau khi đã import ta có thể gọi hàm trong module theo cú pháp: tên module.tên hàm.&#x20;

Có 3 cách import module như sau:

### Lệnh import theo tên module

```python
import math
print("Căn bậc hai của 4 là: ", math.sqrt(4) )
```

Chương trình trên import module math sau đó gọi hàm sqrt() đã được định nghĩa trong module math.

Output: `2.0`

### &#x20;Lệnh import đổi tên module

```python
import math as m
print("Căn bậc hai của 4 là: ", m.sqrt(4) )
```

### Câu lệnh import có chỉ định đối tượng

```python
from math import sqrt
print("Căn bậc hai của 4 là: ", sqrt(4) )
```

Câu lệnh import trong chương trình trên chỉ dùng được hàm sqrt, các hàm khác của module không dùng được.

Xem xét thêm chương trình sau:

```python
from math import sqrt, pi
print("Căn bậc hai của 4 là: ", sqrt(4) )
print('Pi = ', pi)
```

Hoặc&#x20;

```python
from math import *
print("Căn bậc hai của 4 là: ", sqrt(4) )
print('Pi = ', pi)
```

Cả 2 chương trình trên đều có Output: `Căn bậc hai của 4 là:  2.0`\
`Pi =  3.141592653589793`

## Lưu file Module ở đâu?

File module phải được lưu trữ trong phạm vi thư mục cho phép của Python thì chương trình mới có thể gọi và import vào để sử dụng.

Khi import, Python sẽ tìm kiếm file module ở nhiều nơi. Trình thông dịch sẽ tìm kiếm một mô-đun tích hợp sẵn trước. Sau đó (nếu không tìm thấy mô-đun tích hợp sẵn), Python sẽ tìm trong các thư mục được xác định trong sys.path.&#x20;

Việc tìm kiếm ưu tiên theo thứ tự sau:

* Thư mục hiện tại (thư mục lưu file chương trình chính).&#x20;
* PYTHONPATH (một biến liệt kê danh sách các thư mục).&#x20;
* Thư mục cài đặt Python.&#x20;

Ta có thể xem Python parth mặc định bằng lệnh path trong module sys

```python
import sys
print(sys.path)
```

## Trong file module có gì?

Để xem trong file module có những đối tượng nào đã được định nghĩa ta dùng câu lệnh dir(tên module). Ví dụ:

```python
import math
print(dir(math))
```

Output: `['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']`

Muốn biết đối tượng (hàm) được định nghĩa trong module có chức năng gì ta dùng phương thức gọi docstring.&#x20;

Ví dụ, ta thử xem hàm có tên factorial trong module math có chức năng gì:

```python
import math
print(math.factorial.__doc__)
```

&#x20;Output: `Find x!.`

\
`Raise a ValueError if x is negative or non-integral.`


# Bài 20. Package

Package là thư mục chứa các module và các tệp dữ liệu khác. Thư mục được gọi là Package trong Python phải chứa tệp có tên \_\_init\_\_.py . Tệp này có thể là một tệp trống hoặc ta có thể đặt mã khởi tạo cho package đó.

Thư mục có thể chứa các thư mục con và tệp; tương tự như vậy, một Package có thể có các package con và các module.

![Minh họa cấu trúc Package trong Python (nguồn programiz)](/files/-MK7ww1QRUV0NcEYxQ1j)

## Sử dụng Package

Ta có thể nhập các module từ các package bằng toán tử dấu chấm (.).&#x20;

Ví dụ: nếu chúng ta muốn nhập module **start** sau đó gọi hàm select\_difficulty() trong ví dụ trên, nó có thể được thực hiện như sau:

```python
import Game.Level.start
Game.Level.start.select_difficulty(2)
```

Cách khác, ta có thể viết lệnh gọi hàm ngắn hơn với phương thức import khác như sau:

```python
from Game.Level import start
start.select_difficulty(2)
```

Hoặc

```python
from Game.Level.start import select_difficulty
select_difficulty(2)
```


# Chương IV. KIỂU DỮ LIỆU


# Bài 21. Dữ liệu kiểu số

Python có 2 loại dữ liệu số học gồm số nguyên, số thực (dấu phẩy động) và số phức. Cùng xem qua các ví dụ dưới đây về các kiểu dữ liệu số:

```python
a = 5
print(type(a))
print(type(5.0))
c = 5 + 3j
print(c + 3)
print(isinstance(c, complex))
 
''' Output:
    <class 'int'>
    <class 'float'>
    (8+3j)
    True 
'''    
```

Số nguyên và số thực được phân biệt bằng cách có hoặc không có dấu thập phân. Ví dụ: 5 là số nguyên trong khi 5.0 là số thực. Số phức được viết dưới dạng x + yj, trong đó x là phần thực và y là phần ảo.

Số nguyên trong Python có thể có độ dài bất kỳ, số thực chỉ giới hạn đến 15 chữ số thập phân.

Trong Python, chúng ta có thể biểu diễn số nhị phân (cơ số 2), hệ thập lục phân (cơ số 16) và hệ bát phân (cơ số 8) bằng cách đặt một tiền tố một cách thích hợp theo quy ước sau:

Binary:			'0b' hoặc '0B'\
Octal:			'0o' hoặc '0O'\
Hexadecimal:	'0x' hoặc '0X'

```python
print(0b1101011)
print(0xFB + 0b10)
print(0o15)

''' Output:
    107
    253
    13
'''
```

## Chuyển đổi kiểu dữ liệu

Python tự động chuyển kiểu int sang float khi thực hiện các phép tính giữa số nguyên và thực. Ví dụ:

```python
a = 1
b = 2.0
c = a + b
print(c)

# output: 3.0 
```

Ép kiểu dữ liệu bằng cách khai báo kiểu. Ví dụ:

```python
a = int(2.3)
b = int(-2.8)
c = float(5)
d = complex('3+5j')
print(a)
print(b)
print(c)
print(d)

''' output:
    2
    -2
    5.0
    (3+5j)
'''
```

## Số thập phân

Một điều cần chú ý đối với kiểu dữ liệu float trong Python là 1.1 + 2.2 không bằng 3.3!&#x20;

```python
print(1.1+2.2==3.3)

#output: False
```

Nguyên nhân là bởi số thực được lưu trữ trong máy tính dưới dạng phân số nhị phân (0 và 1). Vì vậy hầy hết các phân số không thể biểu diễn chính xác trong máy tính.

Ví dụ, ta không thể biểu diễn chính xác phân số 1/3 bằng số thập phân vì nó sẽ là 0,33333333 ... (dài vô hạn). Tương tự, 0.1 cũng chỉ là biểu diễn gần dúng chứ không hoàn toàn đúng. Đó là lý do vì sáo 1.1+2.2 không = 3.3

Để biểu diễn và tính toán số thực một cách bình thường (như trong toán học) thì ta sử dụng module decimal

Ví dụ:

```python
from decimal import Decimal as d

print(d('1.1') + d('2.2'))
print(d('1.0') * d('2.5'))

''' output:
    3.3
    2.50
'''
```

**Khi nào cần dùng decimal thay cho float?**

* Khi ta thực hiện các ứng dụng tài chính cần biểu diễn số thập phân chính xác.&#x20;
* Khi ta muốn triển khai khái niệm về số thập phân một cách chính xác.

## Phân số

Python hỗ trợ module fractions để xử lý phân số. Có nhiều cách để biểu diễn phân số, ví dụ như sau:

```python
from fractions import Fraction as F

print(F(5))
print(F(1,3))
print(F('1.1'))

''' Output:
    5
    1/3
    11/10
'''
```

Phân số được biểu diễn bởi số nguyên cho cả tử và mẫu hoặc có thể biểu diễn bằng string. Không thể dùng số thực để biểu diễn phân số vì sẽ cho kết quả không đúng. Ví dụ:

```python
from fractions import Fraction as F

print(F(1.3))
print(F('1.3'))

''' Output:
    5854679515581645/4503599627370496
    13/10
'''
```

Dữ liệu kiểu phân số có thể dùng tất cả các toán tử cơ bản. Ví dụ:

```python
from fractions import Fraction as F

print(F(1, 3) + F(1, 3))
print(F(1, 3) * F(1, 3))
print(1 / F(5, 6))
print(F(-3, 10) > 0)
print(F(-3, 10) < 0)

''' Output:
    2/3
    1/9
    6/5
    False
    True
'''
```

## Toán học trong Python

Python cung cấp module như math và random để thực hiện các phép toán khác nhau như lượng giác, logarit, xác suất và thống kê, v.v

```python
import math as m

print(m.pi)
print(m.cos(m.pi))
print(m.exp(10))
print(m.log10(1000))
print(m.sinh(1))
print(m.factorial(6))

''' output:
    3.141592653589793
    -1.0
    22026.465794806718
    3.0
    1.1752011936438014
    720
'''
```

Xem thêm về các hàm toán học tại đây: [math](https://www.programiz.com/python-programming/modules/math)

```python
import random as rd

print(rd.randrange(10, 20))

x = ['a', 'b', 'c', 'd', 'e']

print(rd.choice(x))
rd.shuffle(x)
print(x)
print(rd.random())

# Output: Mỗi lần run sẽ ra một kết quả ngẫu nhiên khác nhau
```


# Bài 22. Dữ liệu kiểu string

Một xâu (string - chuỗi) là một dãy các ký tự. Một ký tự chỉ đơn giản là một biểu tượng (một lần nhấn phím). Ví dụ, ngôn ngữ tiếng Anh có 26 ký tự.&#x20;

Máy tính không xử lý các ký tự, chúng xử lý các số (nhị phân). Mặc dù ta có thể nhìn thấy các ký tự trên màn hình của mình, nhưng bên trong nó vẫn được lưu trữ và thao tác dưới dạng kết hợp của 0 và 1.&#x20;

Quá trình chuyển đổi ký tự thành số này được gọi là mã hóa và quá trình ngược lại là giải mã. ASCII và Unicode là các bảng mã được sử dụng phổ biến.&#x20;

Trong Python, string là một chuỗi các ký tự **Unicode**. Unicode là bảng mã có thể biểu diễn mọi ký tự trong tất cả các ngôn ngữ hiện nay trên thế giới.&#x20;

## Khởi tạo string

Các ký tự được đặt trong cặp nháy đơn, kép, ba nháy đơn được gọi là một xâu (string). Một xâu có thể có 1 hoặc nhiều ký tự. Xâu không chứa ký tự gọi là xâu rỗng.

Ví dụ:

```python
s = 'Hello'
print(s)           # Hello

s = "Hello"
print(s)           # Hello

s = '''Hello'''
print(s)           # Hello

s = """Hello, welcome to
           the world of Python"""
print(s)           
# với ba nháy đơn hoặc ba nháy kép cho phép string viết trên nhiều dòng
```

## Truy cập ký tự trong string

Ta có thể truy cập từng ký tự của chuỗi thông qua chỉ số. Chỉ số của các ký tự trong chuỗi được bắt đầu từ 0 và theo chiều ngược lại được bắt đầu từ -1.

Ví dụ:

```python
str = 'Python3'
print('str = ', str)               # str =  Python3

print('str[0] = ', str[0])         # str[0] =  P

print('str[-1] = ', str[-1])       # str[-1] =  3

print('str[1:5] = ', str[1:5])     # str[1:5] =  ytho

print('str[3:-2] = ', str[3:-2])   # str[3:-2] =  ho
```

Chỉ số chỉ có thể là số nguyên trong phạm vi từ 0 đến chiều dài xâu - 1.&#x20;

## Thêm, sửa, xóa ký tự trong string

String không cho phép sửa đổi ký tự sau khi gán. Ta chỉ có thể ghép xâu với toán tử +, \* hoặc gán lại giá trị mới cho xâu. Ta cũng có thể xóa biến xâu bằng từ khoá del.

Ví dụ:

```python
a = "Học "
b = "Python "
c = a + b
print(c)        # Học Python 
print(c*3)      # Học Python Học Python Học Python 

c = c + "rất vui"
print(c)        # Học Python rất vui

del a
print(a) # báo lỗi: NameError: name 'a' is not defined
```

Lưu ý: (1) Khi viết 2 string gần nhau sẽ cho kết quả như sử dụng phép cộng 2 string. (2) Ta có thể nối các string trên các dòng khác nhau bằng dấu ngoặc.

Ví dụ:

```python
a = "học " "python"

b = ("học "
     "Python "
     "thật vui")

print(a)          # học python
print(b)          # học python thật vui
```

## &#x20; Kiểm tra xâu con chứa trong xâu

Ta có thể kiểm tra xem một xâu con có tồn tại trong một xâu hay không bằng cách sử dụng từ khóa in.&#x20;

```python
a = "học python"
print('a' in a)        # False
print('py' in a)       # True 
print('x' not in a)    # True
```

## Lặp theo ký tự của xâu

```python
a = 'Hello World'
count = 0
for x in a:
    if(x == 'l'):
        count += 1
print(f'Tìm thấy {count} chữ l trong xâu "{a}"')
# output: Tìm thấy 3 chữ l trong xâu "Hello World"
```

## Hàm dựng sẵn cho string

Có nhiều hàm để làm việc với xâu. Các hàm thường dùng khi làm việc với xâu là hàm len() và emurate().

```python
a = 'done'

lst_a = list(enumerate(a))
dic_a = dict(enumerate(a))
len_a = len(a)

print(lst_a)    # [(0, 'd'), (1, 'o'), (2, 'n'), (3, 'e')]
print(dic_a)    # {0: 'd', 1: 'o', 2: 'n', 3: 'e'}
print(len_a)    # 4
```

## Định dạng xâu

Yêu cầu 1: Viết chương trình in ra màn hình dòng sau: `He said, "What's there?"`

Nếu ta dùng cặp nháy đơn hay nháy kép đều sẽ gặp lỗi vì bản thân câu trên đã có chứa cả nháy đơn và nháy kép.&#x20;

Cách xử lý là dùng cặp ba nháy đơn hoặc dùng ký tự "\\" để bỏ qua dấu khóa string. Ví dụ:

```python
a = '''He said, "What's there?"'''
b = 'He said, "What\'s there?"'
c = "He said, \"What's there?\""

print(a)    # He said, "What's there?"
print(b)    # He said, "What's there?"
```

Yêu cầu 2: Viết chương trình in ra màn hình dòng sau: `C:\My Documents\noname`

Trong Python, khi gặp dấu \ thì Python sẽ coi ký tự tiếp theo là một ký tự xâu bình thường và không coi nó là toán tử hay keyword nữa (trừ những toán tử, keyword đi kèm dấu \\). &#x20;

Vì vậy trong trường hợp ta muốn Python coi dấu \ là một ký tự thông thường thì ta phải thêm từ khóa r vào trường nháy mở xâu.

Ví dụ, chạy thử chương trình sau để thấy sự khác nhau giữa 2 câu lệnh

```python
print('C:\My Documents\noname')
print(r'C:\My Documents\noname')
```

### Một số thao tác khác với dấu \\

| Lệnh | Ý nghĩa                                                                    |
| ---- | -------------------------------------------------------------------------- |
| \a   | ASCII Bell (âm báo)                                                        |
| \b   | ASCII Backspace (xóa lùi)                                                  |
| \f   | ASCII Formfeed (trang mới)                                                 |
| \n   | ASCII Linefeed (dòng mới)                                                  |
| \r   | ASCII Carriage Return                                                      |
| \t   | ASCII Horizontal Tab (tab ngang)                                           |
| \v   | ASCII Vertical Tab (tab dọc)                                               |
| \ooo | Character with octal value ooo (Ký tự có giá trị bát phân là ooo)          |
| \xHH | Character with hexadecimal value HH (Ký tự có giá trị thập lục phân là HH) |

### Định dạng với f-string

Xét ví dụ dưới đây:

```python
a = 12
b = 12.3456789
c = ['hoa', 'lá', 'cành']

print("Biểu diễn nhị phân của {a} là {a:b}".format(a))
print(f"Biểu diễn dạng số mũ của {a} là {a:e}")
print(f'Số {a} có thể viết thành: {a:.4f}')
print(f'Số {b} có thể viết thành {b:.2f}')
print(f'|{c[0]:<10}|{c[2]:^20}|{c[1]:>10}|')
```

### Định dạng theo phương thức format()

```python
a = 36
b = 12.3456789
c = ['hoa', 'lá', 'cành']

print("Biểu diễn nhị phân của {0} là {0:b}".format(a))
print("Biểu diễn dạng số mũ của {0} là {0:e}".format(a))
print('Số {0} có thể viết thành: {0:.4f}'.format(a))
print('Số {0} có thể viết thành {0:.2f}'.format(b))
print('|{0:<10}|{2:^20}|{1:>10}|'.format(c[0],c[1],c[2]))
```

## Phương thức thường dùng

Python có rất nhiều phương thức làm việc với string. Một số phương pháp thường được sử dụng là  lower(), upper(), join(), split(), find(), replace(), index()...&#x20;

Ví dụ:

```python
a = 'Học Python'
a = a.upper()
print(a)            # HỌC PYTHON
a = a.lower()
print(a)            # học python
a = a.split()
print(a)            # ['học', 'python']
a = ' '.join(a)
print(a)            # học python
t = a.find('c')
print(t)            # 2
a = a.replace('học','learn')    
print(a)            # learn python
print(a.index('n')) # 4
```

Có thể tham khảo [các phương thức khác làm việc với string tại đây](https://www.programiz.com/python-programming/methods/string).


# Bài 23. Dữ liệu kiểu list

List là một trong những kiểu dữ liệu được sử dụng thường xuyên nhất và rất linh hoạt trong Python.

## Khởi tạo list

```python
# list rỗng
my_list = []

# list số nguyên
my_list = [1, 2, 3]

# list hỗn hợp
my_list = [1, "Hello", 3.4]

# list lồng nhau
my_list = ["mouse", [8, 4, 6], ['a']]
```

## Truy cập phần tử list

```python
my_list = ['H', 'ọ', 'c', ' ', 'P', 'y', 't', 'h', 'o', 'n']

print(my_list[0])        # Output: H
print(my_list[2])        # Output: c
print(my_list[4])        # Output: p

n_list = ["Happy", [2, 0, 1, 5]]    # list lồng nhau

print(n_list[0][1])        # Output: a
print(n_list[1][3])        # Output: 5
```

Lưu ý: chỉ số để truy xuất phần tử list phải là số nguyên

## Truy cập phần tử list theo chỉ số âm

```python
my_list = ['H', 'ọ', 'c', ' ', 'P', 'y', 't', 'h', 'o', 'n']

print(my_list[-1])    # Output: n
print(my_list[-5])    # Output: y
```

## Cắt đoạn list

```python
my_list = ['H', 'ọ', 'c', ' ', 'P', 'y', 't', 'h', 'o', 'n']

print(my_list[2:5])     # ['c', ' ', 'P']
print(my_list[:-5])     # ['H', 'ọ', 'c', ' ', 'P']
print(my_list[5:])     # ['y', 't', 'h', 'o', 'n']
print(my_list[:])     # ['H', 'ọ', 'c', ' ', 'P', 'y', 't', 'h', 'o', 'n']
```

Lưu ý: tham số cắt đoạn \[a:b] được lấy từ phần tử có chỉ số a đến phần tử trước chỉ số b.&#x20;

## Thêm, sửa phần tử list

### Thay đổi giá trị phần tử&#x20;

```python
a = [2, 4, 6, 8]

# đổi giá trị phần tử a[0]    
a[0] = 1            
print(a)        # [1, 4, 6, 8]

# đổi giá trị phần tử a[1] đến a[3]
a[1:4] = [3, 5, 7]  
print(a)         # [1, 3, 5, 7]
```

### Thêm phần tử vào list

Dùng append() và extend()

```python
ma = [0, 9, 1, 8, 9, 1, 8]

ma.append(4)
print(ma)         # [0, 9, 1, 8, 9, 1, 8, 4]

ma.extend([4, 7])
print(ma)         # [0, 9, 1, 8, 9, 1, 8, 4, 4, 7]
```

Dùng toán tử +, \*

```python
ma = [0, 9, 1, 8, 9, 1, 8]

ma += [4,4,7]
print(ma)         # [0, 9, 1, 8, 9, 1, 8, 4, 4, 7]

mb = 'Happy! '
print(mb * 5)     # Happy! Happy! Happy! Happy! Happy!
```

Dùng phương thức insert()

```python
ma = [1, 9]

ma.insert(1,3)
print(ma)         # [1, 3, 9]

ma[2:2] = [5, 7]
print(ma)         # [1, 3, 5, 7, 9]
```

## Xóa phần tử list

```python
ma = ['P', 'y', 't', 'h', 'o', 'n', '3']

del ma[2]    # delete one item
print(ma)    # ['P', 'y', 'h', 'o', 'n', '3']

del ma[1:5]   # delete multiple items
print(ma)     # ['P', '3']

del ma        # delete entire list
# Nếu print(ma) sẽ cho lỗi: List not defined
```

Ta có thể sử dụng các phương thức remove(), pop(), clear() để xóa phần tử list.&#x20;

```python
ma = ['P', 'y', 't', 'h', 'o', 'n', '3']

ma.remove('y')
print(ma)            # Output: ['P', 't', 'h', 'o', 'n', '3']
print(ma.pop(1))     # Output: 't'
print(ma)            # Output: ['P', 'h', 'o', 'n', '3']
print(ma.pop())      # Output: '3'
print(ma)            # Output: ['P', 'h', 'o', 'n']

ma.clear()
print(ma)            # Output: []
```

Hoặc ta có thể xóa phần tử list bằng cách gán list rỗng cho phần tử muốn xóa&#x20;

```python
ma = ['P', 'y', 't', 'h', 'o', 'n', '3']

ma[2:4] = []
print(ma)     # ['P', 'y', 'o', 'n', '3']
```

## Phương thức (list method)

Phương thức trong Python được gọi bởi dấu chấm và tên phương thức sau tên biến. Có dạng list.method(). Các phương thức xử lý list gồm:

`append() - thêm phần tử`\
`extend() - nối list`\
`insert() - chèn phần tử theo chỉ số` \
`remove() - xóa một phần tử list`\
`pop() - Xóa và trả về chỉ số phần tử vừa xóa`\
`clear() - Xóa hết các phần tử của list -> trả về list rỗng`\
`index() - Trả về chỉ số của phần tử đầu tiên có giá trị như tham số`\
`count() - Đếm số phần tử có giá trị như tham số`\
`sort() - Sắp xếp thành list tăng dần`\
`reverse() - Đảo chiều phần tử list`\
`copy() - Sao chép list`

Ví dụ:

```python
ma = [9, 1, 8, 9, 1, 8, 4]

print(ma.index(9))    # Output: 0
print(ma.count(8))    # Output: 2

ma.sort()
print(ma)    # Output: [1, 1, 4, 8, 8, 9, 9]

ma.reverse()
print(ma)    # Output: [9, 9, 8, 8, 4, 1, 1]
```

## List Comprehension

Chưa biết nên dịch thuật ngữ list comprehension là gì nhưng đây là một cách ngắn gọn và riêng có của Python để tạo một list từ list đã có. Xét một vài ví dụ để xem cách hoạt động của list comprehension dưới đây:

```python
pow2 = [2 ** x for x in range(10)]
print(pow2)    # [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

pow2 = [2 ** x for x in range(10) if x > 5]
print(pow2)    # [64, 128, 256, 512]

odd = [x for x in range(20) if x % 2 == 1]
print(odd)    # [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

a = [x+y for x in ['Python ','C '] for y in ['Language','Programming']]
print(a)    # ['Python Language', 'Python Programming', 'C Language', 'C Programming']
```

## Thao tác khác

### Kiểm tra phần tử chứa trong list

```python
ma = ['P', 'y', 't', 'h', 'o', 'n', '3']

print('h' in ma)    # Output: True

print('a' in ma)    # Output: False

print('c' not in ma)    # Output: True
```

### Lặp theo phần tử list

```python
mau = ['xanh','đỏ', 'vàng']

for x in ['apple','banana','mango']:
    print("I like",x)

for i in mau:
    print('Tôi thích màu', i)
```

Output: `I like apple`\
`I like banana`\
`I like mango`\
`Tôi thích màu xanh`\
`Tôi thích màu đỏ`\
`Tôi thích màu vàng`

## Thao tác tạo list bổ sung

Tạo list đơn có n phần tử

```python
n = 5
ma = [None]*n
print(ma)
```

Output: \[None, None, None, None, None]

Tạo nested list dạng mảng 2 chiều có c cột và d dòng

```python
c = 5
d = 3
ma = [[None for x in range(c)] for x in range(d)]
```

Output: \[\[None, None, None, None, None], \[None, None, None, None, None], \[None, None, None, None, None]]


# Bài 24. Dữ liệu kiểu tuple

Tuple trong Python tương tự như list. Sự khác biệt giữa hai kiểu dữ liệu này là tuple không thể thay đổi các phần tử khi nó đã được gán còn list thì có thể thay đổi.

## Khởi tạo tuple

```python
mb = ()    # tuple rỗng
print(mb)

mb = (1, 2, 3)    # tuple các số nguyên
print(mb)

mb = (1, "Hello", 3.4)    # tuple hỗn hợp
print(mb)

mb = ("mouse", [8, 4, 6], (1, 2, 3))     # tuple lồng nhau
print(mb)
```

Ta có thể gán phần tử cho tuple mà không cần dùng dấu ngoặc đơn.

```python
mb = 1, 2, 3
a,b,c = mb

print(a)    # 1
print(b)    # 2
print(c)    # 3

print(mb)     # (1, 2, 3)
```

**Lưu ý** khi khởi tạo tuple chỉ có một phần tử phải đặt dấu phẩy sau phần tử nếu không Python sẽ nhận diện đó là một str.

```python
mb = ("hello")
print(type(mb))  # <class 'str'> 

mb = ("hello",)
print(type(mb))  # <class 'tuple'>

# Parentheses is optional
mb = "hello",
print(type(mb))  # <class 'tuple'>

```

## Truy cập phần tử tuple

Ta sử dụng chỉ số trong cặp ngoặc \[] để truy cập phần tử tuple. Chỉ số các phần tử trong tuple bắt đầu từ 0 và chiều ngược lại bắt đầu từ -1.

Việc truy cập phần tử tuple cũng tương tự như truy cập phần tử list. Ví dụ:

```python
mt = ('P', 'y', 't', 'h', 'o', 'n', '3')

print(mt[1])   # y
print(mt[5])   # n
print(mt[-6])  # y

# tuple lồng nhau
mm = ("mouse", [8, 4, 6], (1, 2, 3))
print(mm[0][3])       # 's'
print(mm[1][1])       # 4    
```

## Cắt tuple

```python
mt = ('P', 'y', 't', 'h', 'o', 'n', '3')

print(mt[1:4])    # ('y', 't', 'h')
print(mt[:-6])    # ('P',)
print(mt[6:])     # ('3',)
print(mt[:])      # ('P', 'y', 't', 'h', 'o', 'n', '3')

print(mt[::-1])    # ('3', 'n', 'o', 'h', 't', 'y', 'P')
```

## Thêm, sửa, xóa phần tử tuple

### Thêm

Khác với list, tuple không cho phép thay đổi giá trị phần tử.&#x20;

Để thêm phần tử vào tuple ta có thể dùng toán tử cộng + để nối tuple; toán tử \* để nhân tuple. Ví dụ:

```python
a = (1, 2, 3)
aa = a + (4, 5, 6)
print(aa)             # (1, 2, 3, 4, 5, 6)

aaa = a*2
print(aaa)            # (1, 2, 3, 1, 2, 3)
```

### Sửa

Như định nghĩa ban đầu, tuple không cho phép sửa đổi phần tử khi đã gán.&#x20;

Tuy nhiên, nếu phần tử của tuple ở dạng list thì ta vẫn có thể can thiệp vào phần tử của list.

Một cách khác để sửa phần tử tuple là gán cho biến tuple một tập giá trị mới.

Ví dụ:

```python
mt = (4, 2, 3, [6, 5])

mt[3][0] = 9
print(mt)                # (4, 2, 3, [9, 5])

mt = (4, 6, 8, [6, 5])
print(mt)                # (4, 6, 8, [6, 5])
```

### Xóa

Tuple không xóa được từng phần tử mà chỉ có thể xóa hoàn toàn biến tuple

```python
mt = ('P', 'y', 't', 'h', 'o', 'n', '3')
del mt
print(mt)     # báo lỗi: NameError: name 'mt' is not defined
```

## Phương thức (tuple method)

Khác với list, tuple chỉ có 2 phương thức hoạt động đó là count() và index(). Ví dụ:

```python
mt= ('a', 'p', 'p', 'l', 'e',)

print(mt.count('p'))  # Output: 2
print(mt.index('l'))  # Output: 3
```

## Thao tác khác

### Kiểm tra phần tử chứa trong tuple

```python
mt = ('P', 'y', 't', 'h', 'o', 'n', '3')

print('t' in mt)        # True
print('z' in mt)        # False
print('g' not in mt)    # True
```

### Lặp theo phần tử tuple

```python
mt = ('xanh', 'đỏ', 'tím', 'vàng')

for x in mt:
    print("Tôi thích màu", x)
```

Output: `Tôi thích màu xanh`\
`Tôi thích màu đỏ`\
`Tôi thích màu tím`\
`Tôi thích màu vàng`

## Khi nào dùng tuple thay vì list?

* Thường ta nên dùng list cho dữ liệu đồng nhất (cùng kiểu) và dùng tuple cho dữ liệu không đồng nhất.
* Truy xuất phần tử tuple nhanh hơn list nên hiệu suất tốt hơn (chỉ đáng kể với dữ liệu lớn).
* Tuple có thể dùng làm key cho dict. list thì không.
* Nếu dữ liệu cần được đảm bảo không thay đổi thì tuple là lựa chọn (phần tử của tuple không thể thay đổi).


# Bài 25. Dữ liệu kiểu set

Set là một tập hợp các phần tử không có thứ tự. Mọi phần tử trong set là duy nhất (không trùng lặp) và bất biến (không thể thay đổi). Phần tử của set có thể có kiểu dữ liệu bất kỳ trừ list và chính nó.

## Khởi tạo set

```python
s1 = set([1,2,3,4])
s2 = {3, 4, 5}
print(s1)
print(s2)

ms = {1.0, "Hello", (1, 2, 3)}
print(ms)
```

Xét thêm các ví dụ sau để thấy set không chứa phần tử trùng lặp.

```python
s = {1, 2, 3, 4, 3, 2}
print(s)                # Output chỉ còn: {1, 2, 3, 4}

s = set([1, 2, 3, 2])   
print(s)                # {1, 2, 3}
```

Lưu ý: Ta không thể khởi tạo một set rỗng như cách làm với list và tuple. Ví dụ:

```python
s = {}
print(s)
print(type(s))    # <class 'dict'> - lúc này Python nhận kiểu dict

s = set({})
print(s)
print(type(s))    # <class 'set'>
```

## Thêm, ~~sửa~~, xóa phần tử set

### Thêm

```python
s = {1, 3}
print(s)

# s[0]  # bỏ comment dòng 4 này sẽ phát sinh lỗi
        #vì set không cho truy cập theo chỉ số

s.add(2)
print(s) # {1, 2, 3}

s.update([2, 3, 4])
print(s) # Output: {1, 2, 3, 4}

s.update([4, 5], {1, 6, 8})
print(s) # Output: {1, 2, 3, 4, 5, 6, 8}
```

### Xóa

Sử dụng phương thức discard() và remove(). Lưu ý phương thức remove() sẽ không hoạt động nếu phần tử chỉ định không tồn tại trong set. &#x20;

```python
s2 = {1, 3, 4, 5, 6}

s2.discard(4)     # xóa bỏ một phần tử bằng discard
print(s2)         # {1, 3, 5, 6}

s2.remove(6)      # xóa một phần tử bằng remove
print(s2)         # {1, 3, 5}

s2.discard(2)
print(s2)         # {1, 3, 5}
s2.remove(2)      # Báo lỗi KeyError vì phần tử không tồn tại
```

Dùng phương thức pop(), clear()

```python
s = set("HelloWorld")
print(s)          # xem kỹ output nhé !

print(s.pop())    # phần tử đầu tiên của set

s.pop()           # xóa thêm một phần tử 
print(s)    

s.clear()
print(s)          # set rỗng: set()
```

## Các phép toán trên set

Các phép toán trên set chính là các phép toán trên tập hợp. Xét ví dụ sau:

```python
A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}

U = A | B    # phép hợp
print(U)     # {1, 2, 3, 4, 5, 6, 7, 8}

I = A & B    # phép giao
print(I)     # {4, 5}

D = A-B      # phép hiệu
print(D)     # {1, 2, 3}

SD = A^B     # hiệu của hợp và giao
print(SD)    # {1, 2, 3, 6, 7, 8}
```

## Phương thức của set&#x20;

| Phương thức                                                                                                             | Chú giải                                                                |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| [add()](https://www.programiz.com/python-programming/methods/set/add)                                                   | Cộng thêm phần tử                                                       |
| [clear()](https://www.programiz.com/python-programming/methods/set/clear)                                               | Xóa rỗng set                                                            |
| [copy()](https://www.programiz.com/python-programming/methods/set/copy)                                                 | Sao chép set                                                            |
| [difference()](https://www.programiz.com/python-programming/methods/set/difference)                                     | Phần khác  (phép - )                                                    |
| [difference\_update()](https://www.programiz.com/python-programming/methods/set/difference_update)                      | Trả về set đã xóa hết các phần tử giống set khác                        |
| [discard()](https://www.programiz.com/python-programming/methods/set/discard)                                           | Xóa bỏ phần tử đầu của set                                              |
| [intersection()](https://www.programiz.com/python-programming/methods/set/intersection)                                 | Phần giao 2 set (phép &)                                                |
| [intersection\_update()](https://www.programiz.com/python-programming/methods/set/intersection_update)                  | Trả về phần giao 2 set                                                  |
| [isdisjoint()](https://www.programiz.com/python-programming/methods/set/isdisjoint)                                     | `True` nếu 2 set không có phần giao                                     |
| [issubset()](https://www.programiz.com/python-programming/methods/set/issubset)                                         | `True` nếu set chứa trong set khác                                      |
| [issuperset()](https://www.programiz.com/python-programming/methods/set/issuperset)                                     | `True` nếu set có chứa một set khác                                     |
| [pop()](https://www.programiz.com/python-programming/methods/set/pop)                                                   | Xóa phần tử đầu của set (sẽ báo lỗi nếu xóa phần tử không có trong set) |
| [remove()](https://www.programiz.com/python-programming/methods/set/remove)                                             | Xóa phần tử của set (sẽ báo lỗi nếu xóa phần tử không có trong set)     |
| [symmetric\_difference()](https://www.programiz.com/python-programming/methods/set/symmetric_difference)                | Trả về set là phần khác đối xứng của 2 xét khác                         |
| [symmetric\_difference\_update()](https://www.programiz.com/python-programming/methods/set/symmetric_difference_update) | Trả về set là phần khác đối xứng của 2 set khác                         |
| [union()](https://www.programiz.com/python-programming/methods/set/union)                                               | Trả về phần hợp 2 set (phép hợp \| )                                    |
| [update()](https://www.programiz.com/python-programming/methods/set/update)                                             | Trả về một set là hợp của 2 set                                         |

## Hàm dựng sẵn cho set

Các hàm dựng sẵn như all(), any(), enumerate(), len(), max(), min(), sorted(), sum(), v.v. thường được sử dụng với các set để thực hiện các tác vụ khác nhau.

| Function                                                                               | Description                                                                                                |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| [all()](https://www.programiz.com/python-programming/methods/built-in/all)             | `True` nếu tất cả các phần tử true (hoặc set rỗng).                                                        |
| [any()](https://www.programiz.com/python-programming/methods/built-in/any)             | `True` nếu set có phần tử True. `False` nếu set rỗng                                                       |
| [enumerate()](https://www.programiz.com/python-programming/methods/built-in/enumerate) | Trả về một đối tượng liệt kê. Nó chứa chỉ số và giá trị cho tất cả các phần tử của set dưới dạng từng cặp. |
| [len()](https://www.programiz.com/python-programming/methods/built-in/len)             | Số lượng phần tử của set                                                                                   |
| [max()](https://www.programiz.com/python-programming/methods/built-in/max)             | Phần tử lớn nhất của set                                                                                   |
| [min()](https://www.programiz.com/python-programming/methods/built-in/min)             | Phần tử nhỏ nhất của set                                                                                   |
| [sorted()](https://www.programiz.com/python-programming/methods/built-in/sorted)       | Trả về một list gồm các phần tử của set đã được sắp xếp                                                    |
| [sum()](https://www.programiz.com/python-programming/methods/built-in/sum)             | Tổng các phần tử của set                                                                                   |

## Frozenset

Frozenset có các đặc điểm của set nhưng không cho phép thêm-sửa-xóa phần tử. Nếu  tuple là dạng bất biến của list thì frozenset là dạng bất biến của set.

frozenset được khởi tạo bằng hàm frozenset() và được hỗ trợ các phương thức như: copy(), difference(), intersection(), isdisjoint(), issubset(), issuperset(), symmetric\_difference() and union().&#x20;

Dĩ nhiên là không làm việc với remove(), discard...

Ví dụ

```python
A = frozenset([1, 2, 3, 4])
B = frozenset([3, 4, 5, 6])

U = A|B
I = A&B
D = A-B
DS = A^B

print(U) # frozenset({1, 2, 3, 4, 5, 6})
print(I) # frozenset({3, 4})
print(D) # frozenset({1, 2})
print(DS) # frozenset({1, 2, 5, 6})
```

## Thao tác khác

### Kiểm tra phần tử chứa trong set

```python
s = set("apple")

print('a' in s)         # True
print('p' not in s)     # False
```

### Lặp theo phần tử set

```python
for si in set("apple"):
    print(si) # in từng ký tự trong xâu apple
```


# Bài 26. Dữ liệu kiểu dictionary

Kiểu từ điển (dict) trong Python là một tập hợp các phần tử không có thứ tự. Mỗi phần tử của dict có một cặp khóa : giá trị (key : value).

Dict được khai báo nằm trong cặp ngoặc nhọn giống như set {}. Việc truy xuất giá trị phần tử sẽ được thực hiện thông qua khóa (key).

## Khởi tạo dict

```python
d = {}

d = {1: 'apple', 2: 'ball'}

d = {'name': 'John', 1: [2, 4, 3]}

d = dict({1:'apple', 2:'ball'})

d = dict([(1,'apple'), (2,'ball')])
```

## Truy cập phần tử dict

Truy cập phần tử dict thông qua key trong cặp ngoặc vuông \[] hoặc sử dụng phương thức get().

Ví dụ:

```python
d = {'name': 'Jack', 'age': 26}

print(d['name']) # Jack

print(d.get('age')) # 26

print(d.get('address')) # None (truy cập phần tử không tồn tại)

print(d['address']) # Báo lỗi KeyError
```

## Thêm, sửa, xóa phần tử dict

### Thêm, sửa

```python
d = {'name': 'Jack', 'age': 26}

d['age'] = 27        # gán giá trị cho phần tử có khóa là age

print(d)             # {'age': 27, 'name': 'Jack'}

d['dc'] = 'Downtown' # thêm phần tử mới cho dict

print(d)             # {'dc': 'Downtown', 'age': 27, 'name': 'Jack'}
```

### Xóa

```python
# tạo dict
dbp = {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# xóa phần tử theo key, trả về giá trị tương ứng
print(dbp.pop(4))     # 16
print(dbp)            # {1: 1, 2: 4, 3: 9, 5: 25}

# xóa phần tử cuối, trả về cặp (key,value)
print(dbp.popitem())   # (5, 25)
print(dbp)             # {1: 1, 2: 4, 3: 9}

dbp.clear()            # Xóa hết phần tử
print(dbp)             # {}

del dbp                # xóa biến 
```

## Các phương thức với dict

| Method                                                                                              | Chú thích                                                                                                                                         |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| [clear()](https://www.programiz.com/python-programming/methods/dictionary/clear)                    | Xóa hết các phần tử                                                                                                                               |
| [copy()](https://www.programiz.com/python-programming/methods/dictionary/copy)                      | Trả về bản sao của dict                                                                                                                           |
| [fromkeys(seq\[, v\])](https://www.programiz.com/python-programming/methods/dictionary/fromkeys)    | Gán v làm giá trị mặc định cho các key khi khởi tạo dict. (v mặc định = `None`).                                                                  |
| [get(key\[,d\])](https://www.programiz.com/python-programming/methods/dictionary/get)               | Trả về giá trị của key. Nếu key không tồn tại thì trả về d (mặc định là `None`).                                                                  |
| [items()](https://www.programiz.com/python-programming/methods/dictionary/items)                    | Trả về cặp (key, value)                                                                                                                           |
| [keys()](https://www.programiz.com/python-programming/methods/dictionary/keys)                      | Trả về danh sách các key của dict.                                                                                                                |
| [pop(key\[,d\])](https://www.programiz.com/python-programming/methods/dictionary/pop)               | Xóa phần tử theo key và trả về giá trị phần tử đó (value) Hoặc trả về d nếu key cần xóa không tồn tại (nếu bỏ trống d thì sẽ báo lỗi `KeyError`). |
| [popitem()](https://www.programiz.com/python-programming/methods/dictionary/popitem)                | Xóa và trả về cặp (**key, value**). Lỗi `KeyError` nếu dict rỗng.                                                                                 |
| [setdefault(key\[,d\])](https://www.programiz.com/python-programming/methods/dictionary/setdefault) | Trả về giá trị tương ứng nếu key có trong dict. Nếu không sẽ chèn key với giá trị d và trả về d (mặc định là `None`).                             |
| [update(\[other\])](https://www.programiz.com/python-programming/methods/dictionary/update)         | Cập nhật từ điển với các cặp key/value từ các cặp key/value khác, ghi đè các key hiện có.                                                         |
| [values()](https://www.programiz.com/python-programming/methods/dictionary/values)                  | Trả về danh sách các value của dict.                                                                                                              |

```python
diem = {}.fromkeys(['Toán', 'Văn', 'Anh'], 0)

print(diem)         # {'Toán': 0, 'Văn': 0, 'Anh': 0}

for item in diem.items():
    print(item)
                    ''' 
                    ('Toán', 0)
                    ('Văn', 0)
                    ('Anh', 0)
                    '''
print(list(sorted(diem.keys()))) # ['Anh', 'Toán', 'Văn']
```

Dictionary Comprehension

Comprehension với dict tương tự như với list, tuple, set.&#x20;

```python
bp= {x: x*x for x in range(6)}

print(bp)
```

```python
bpl = {x: x*x for x in range(11) if x % 2 == 1}

print(bpl)
```

## Hàm dựng sẵn cho dict

Các hàm dựng sẵn thường dùng cho dict gồm all(), any(), len(), sorted(), ...&#x20;

```python
d = {0: 11, 1: 20, 3: 9, 5: 25, 7: 49, 9: 81}

print(all(d))     # False
print(any(d))     # True
print(len(d))     # 6
print(sorted(d))  # [0, 1, 3, 5, 7, 9]
```

## Thao tác khác

### Kiểm tra key chứa trong dict

Dùng từ khóa in để kiểm tra xem có tồn tại key nào đó trong dict không (không phải kiểm tra giá trị)

```python
d = {1: 1, 3: 9, 5: 25, 7: 49, 9: 81}

print(1 in d) # True
print(2 not in d) # True
print(49 in d) # False
```

### Lặp theo phần tử dict

```python
d = {1: 1, 3: 9, 5: 25, 7: 49, 9: 81}
for i in d:
    print(d[i])
```

Output: `1`\
`9`\
`25`\
`49`\
`81`


# Chương V. TỆP & THƯ MỤC


# Bài 27. Đọc và ghi file

Việc đọc và ghi tệp chúng ta đã được tìm hiểu ở bài 8 chương I. Trong bài này sẽ trình bày chi tiết hơn một số vấn đề liên quan đến việc đọc và ghi tệp trong Python.

Để thao tác với tệp (đọc hoặc ghi dữ liệu) trong Python có nhiều cách khác nhau. Trong đó sử dụng câu lện with là đơn giản và hiệu quả, ngắn gọn và an toàn. Cú pháp như sau:

```python
with open("tenfile.txt", 'mode') as tenbien:   
    # Thực hiện các thao tác với file
```

Với câu lệnh trên file được mở để thao tác đồng thời sẽ tự động đóng file sau khi thao tác.

Cách khác, ta có thể dùng cấu khối lệnh sau:

```python
try:
   f = open("test.txt",'mode')
   # Thực hiện các thao tác với file
finally:
   f.close()
```

Các chế độ mở file (mode) thường dùng như sau:

| **Mode** | **Tác động**                                                                                                                                          |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| r        | Mở chỉ để đọc. (mặc định)                                                                                                                             |
| w        | <p>Mở để ghi, nếu đã có nội dung thì sẽ ghi đè.</p><p>Nếu file chưa tồn tại thì tạo file mới.</p>                                                     |
| x        | Tạo file độc quyền mới (exclusive creation) và ghi nội dung, nếu file đã tồn tại thì chương trình sẽ báo lỗi.                                         |
| a        | <p>Mở file chế độ ghi tiếp.</p><p>Nếu file đã tồn tại rồi thì nó sẽ ghi tiếp nội dung vào cuối file, nếu file không tồn tại thì tạo một file mới.</p> |
| t        | Mở file ở chế độ văn bản (mặc định)                                                                                                                   |
| b        | Mở file ở chế độ nhị phân                                                                                                                             |

## 1. Đọc file <a href="#id-1-doc-file" id="id-1-doc-file"></a>

Ví dụ, Ta có file ‘vao.txt’ được lưu cùng thư mục với file Python.

Mở file để đọc ta dùng phương thức **`with open(‘tenfile’, ‘mode’) as tenbien`**. Ta có thể gán mode hoặc bỏ qua vì Python mặc định mở file là để đọc.

### **Đọc toàn bộ file**

```python
with open("vao.txt") as f:   
    print(f.read())
```

### **Đọc từng dòng (1 dòng)**

```python
with open("vao.txt") as f:    
    print(f.readline())
```

### **Đọc từng dòng (2 dòng)**

```python
with open("vao.txt") as f:    
    print(f.readline())    
    print(f.readline())
```

### **Đọc từng dòng đến hết file**

```python
with open("vao.txt") as f:    
    for i in f:        
        print(i, end='')
```

**Lưu ý:** Tất cả dữ liệu đọc từ file text đều có kiểu string. Vì vậy nếu là các chữ số, khi đọc vào phải ép kiểu thành number mới có thể thực hiện các phép toán số học.

Chúng ta có thể thay đổi vị trí con trỏ tệp hiện tại bằng cách sử dụng phương thức seek (). Tương tự, phương thức tell () trả về vị trí hiện tại của con trỏ tệp (theo số byte).

Ví dụ:

```python
with open('input.txt') as f:    
    print(f.read())    
    print(f.tell())        
    f.seek(0)    
    doc = f.read(5)    
    print(doc)    
    print(f.tell())    
    f.seek(13)    
    print(f.read())
```

**Giải thích:**&#x20;

Câu lệnh 1 để mở file đọc \
Câu lệnh 2 đọc toàn bộ file và in ra màn hình \
Câu lệnh 3: in ra màn hình thông báo vị trí con trỏ hiện thời (là vị trí cuối file) \
Câu lệnh 4: chuyển con trỏ về vị trí 0 (đầu file) \
Câu lệnh 5: đọc 5 ký tự (5 bytes) và gán vào biến doc \
Câu lệnh 6: in biến doc ra màn hình \
Câu lệnh 7: in ra màn hình thông báo vị trí con trỏ hiện thời \
Câu lệnh 8: Chuyển con trỏ đến vị trí 13 \
Câu lệnh 9: đọc đến cuối file và in ra màn hình

Output:

`Learn Python together` \
`21` \
`Learn` \
`5` \
`together`

## 2. Mở file để ghi <a href="#id-2-mo-file-de-ghi" id="id-2-mo-file-de-ghi"></a>

Mở file để ghi ta dùng phương thức **with open(‘tenfile’, ‘mode’) as tenbien**. Mode thường dùng là **w** (ghi đè) hoặc **a** (ghi tiếp).

Cả mode **a** và **w** đều sẽ tạo file mới để ghi nếu file mở để ghi chưa tồn tại.

Ví dụ:

```python
with open("ra.txt",'w') as f:    
    f.write("Ghi dong chu nay vao file ra.txt")
```

Lưu ý: Để ghi dữ liệu vào file text ta chuyển về kiểu xâu trước khi ghi (nếu dữ liệu là số).

Chương trình sau sẽ báo lỗi:

```python
a = "Ghi dong chu nay vao file ra.txt"
b = 994
with open("ra.txt",'w') as f:    
    f.write(a + '\n')    
    f.write(b)
```

Ta sửa lỗi như sau:

```python
a = "Ghi dong chu nay vao file ra.txt"
b = 994
with open("ra.txt",'w') as f:    
    f.write(a + '\n')    
    f.write(str(b))
```

## Đọc - Ghi file text unicode

```python
with open('tên file', 'mode', encoding = 'utf-8') as f:
    <lệnh đọc ghi>
```


# Bài 28. Quản lý file và folder

Trong bài này, ta sẽ tìm hiểu về quản lý tệp và thư mục bằng Python, cách tạo một thư mục, đổi tên, liệt kê tất cả các thư mục và làm việc với thư mục.

Python có module os cung cấp cho chúng ta nhiều phương pháp hữu ích để làm việc với folder và file.

## Làm việc với module os

### Xác định thư mục hiện tại

```python
import os

a = os.getcwd()
b = os.getcwdb()
print(a)            # in ra đường dẫn đến thư mục hiện tại (kiểu str)
print(type(a))    # <class 'str'>

print(b)            # in ra đường dẫn đến thư mục hiện tại (kiểu bytes)
print(type(b))    # <class 'bytes'>
```

### Mở thư mục khác (chuyển thư mục làm việc)

```python
import os
os.chdir('C:\\Python')     # ví dụ chuyển sang thư mục Python trong ổ C
```

### Xem nội dung thư mục&#x20;

Để xem trong thư mục có chứa gì ta dùng hàm listdir(). Hàm trả về một list là tên các file và thư mục con có trong thư mục đang làm việc.

```python
import os

noidung = os.listdir()
print(noidung)        # list liệt kê tên file và subfolder
```

### Tạo thư mục mới

```python
import os
os.mkdir('thumucmoi')
```

### Xóa thư mục hoặc file

Sử dụng phương thức remove() để xóa file và rmdir() để xóa folder (chỉ xóa được folder trống).

```python
import os
os.remove('filename.txt')     # xóa file có tên: filename.txt
os.rmdir('foldername')        # xóa folder có tên: foldername
print(os.listdir())
```

Để xóa một thư mục không trống, ta có thể sử dụng phương thức rmtree() thuộc module shutil

```python
import shutil

shutil.rmtree('fodername')
print(os.listdir())
```


# Chương VI. LỖI & NGOẠI LỆ


# Bài 29. Ngoại lệ

Lỗi và ngoại lệ trong Python

Một chương trình python sẽ dừng lại ngay khi nó gặp lỗi nếu chưa được khắc phục. Những lỗi này có thể được phân thành 2 loại:

* Lỗi cú pháp&#x20;
* Lỗi logic (Ngoại lệ)

### Lỗi cú pháp

Lỗi do không tuân theo cấu trúc (cú pháp) của ngôn ngữ được gọi là lỗi cú pháp (syntax error) hoặc lỗi phân tích cú pháp (parsing error).

Ví dụ:

```python
print(hello world!)
```

Khi chạy chương trình sẽ báo lỗi:

```python
Traceback (most recent call last):
  File "C:\...\main.py", line 1
    print(hello world!)
                    ^
SyntaxError: invalid syntax
```

Chương trình phát sinh lỗi ở dòng 1, vị trí trong hàm print (xâu "hello world!" chưa có dấu nháy mở-đóng xâu)

### Lỗi logic (ngoại lệ - exceptions)

Các lỗi xảy ra khi chạy chương trình (sau khi vượt qua kiểm tra cú pháp) được gọi là ngoại lệ (exceptions) hoặc lỗi lôgic (logical errors).

Ví dụ, lỗi sẽ phát sinh khi ta mở một file không tồn tại (FileNotFoundError),khi ta thực hiện phép chia cho 0 (ZeroDivisionError), khi ta import một module không tồn tại (ImportError),...

Bất cứ khi nào các loại lỗi logic (runtime error) xảy ra, Python sẽ phát sinh một đối tượng ngoại lệ. Nếu không được xử lý đúng cách, chương trình sẽ in thông báo lỗi cùng với một số chi tiết về lý do tại sao lỗi đó xảy ra.

Xem xét ví dụ sau:

```python
a = 1 / 0
```

Chạy chương trình sẽ nhận được thông báo sau:

```python
Traceback (most recent call last):
  File "C:\...\main.py", line 1, in <module>
    a = 1 / 0
ZeroDivisionError: division by zero
```

Ví dụ về lỗi mở file không tồn tại:

```python
open("imaginary.txt")
Traceback (most recent call last):
 File "<string>", line 301, in runcode
 File "<interactive input>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'imaginary.txt'
```

## Ngoại lệ tích hợp sẵn (built-in exceptions)

Hoạt động không hợp lệ sẽ sinh ngoại lệ. Có rất nhiều ngoại lệ tích hợp sẵn trong Python được gọi ra khi các lỗi tương ứng xảy ra. Ta có thể xem tất cả các ngoại lệ được tích hợp sẵn bằng cách sử dụng hàm local() được tích hợp sẵn như sau:

```python
print(dir(locals()['__builtins__']))
```

Một số ngoại lệ tích hợp sẵn phổ biến trong Python cùng với lỗi gây ra chúng được liệt kê dưới đây:

| **Ngoại lệ**          | **Lý do gây ra**                                                                                                                     |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| AssertionError        | Xảy ra khi câu lệnh assert thất bại.                                                                                                 |
| AttributeError        | Xảy ra khi gán thuộc tính hoặc tham chiếu thất bại.                                                                                  |
| EOFError              | Xảy ra khi hàm input () chạm vào điều kiện end-of-file.                                                                              |
| FloatingPointError    | Xảy ra khi một số thực dấy phẩy động thực thi không thành công                                                                       |
| GeneratorExit         | Xảy ra khi phương thức close() của hàm generator được gọi.                                                                           |
| ImportError           | Xảy ra khi không tìm thấy module được import.                                                                                        |
| IndexError            | Xảy ra khi một chỉ số trong chuỗi (sequence) nằm ngoài phạm vi.                                                                      |
| KeyError              | Xảy ra khi không tìm thấy khóa ánh xạ (từ điển) trong tập hợp các khóa hiện có.                                                      |
| KeyboardInterrupt     | Xảy ra khi người dùng nhấn phím ngắt (thông thường là Ctrl-C hoặc Delete).                                                           |
| MemoryError           | Xảy ra khi một operation hết bộ nhớ nhưng tình huống vẫn có thể được sửa chữa (bằng cách xóa một số đối tượng).                      |
| NameError             | Xảy ra khi không tìm thấy tên cục bộ hoặc toàn cầu của biến.                                                                         |
| NotImplementedError   | Xảy ra bằng các phương thức trừu tượng khi chúng yêu cầu các lớp dẫn xuất ghi đè phương thức.                                        |
| OSError               | Xảy ra khi một hàm trả về lỗi liên quan đến hệ thống                                                                                 |
| OverflowError         | Xảy ra khi kết quả của phép toán số học quá lớn không thể biểu diễn.                                                                 |
| ReferenceError        | Xảy ra khi một proxy tham chiếu yếu sử dụng để truy cập một thuộc tính của tham chiếu sau khi thu thập rác.                          |
| RuntimeError          | Xảy ra khi phát hiện thấy lỗi không thuộc bất kỳ danh mục nào khác.                                                                  |
| StopIteration         | Xảy ra bằng phương thức next() của một vòng lặp để báo hiệu rằng không có giá trị nào được trả về bởi iterator.                      |
| SyntaxError           | Xảy ra khi gặp lỗi cú pháp.                                                                                                          |
| IndentationError      | Xảy ra khi có lỗi thụt lề không chính xác.                                                                                           |
| TabError              | Xảy ra khi thụt lề sử dụng các tab và dấu cách không nhất quán.                                                                      |
| SystemError           | Xảy ra khi trình thông dịch tìm thấy các lỗi nội bộ nhưng tình hình không quá nghiêm trọng.                                          |
| SystemExit            | Xảy ra bởi hàm sys.exit().                                                                                                           |
| TypeError             | Xảy ra khi một hàm hoặc phép thực thi (operation) áp dụng kiểu không chính xác cho một đối tượng.                                    |
| UnboundLocalError     | Xảy ra khi tham chiếu tạo thành một biến cục bộ trong một hàm hoặc phương thức, nhưng không có giá trị nào bị ràng buộc với biến đó. |
| UnicodeError          | Xảy ra khi có lỗi liên quan đến Unicode                                                                                              |
| UnicodeEncodeError    | Xảy ra khi lỗi liên quan đến Unicode diễn ra trong quá trình mã hóa.                                                                 |
| UnicodeDecodeError    | Xảy ra khi lỗi liên quan đến Unicode diễn ra trong quá trình giải mã.                                                                |
| UnicodeTranslateError | Xảy ra khi lỗi liên quan đến Unicode trong quá trình dịch.                                                                           |
| ValueError            | Xảy ra khi một phép toán hoặc hàm nhận được một đối số có kiểu đúng nhưng giá trị không phù hợp                                      |
| ZeroDivisionError     | Xảy ra khi đối số thứ hai của phép chia hoặc phép toán modulo bằng 0.                                                                |

Chúng ta có thể xử lý các ngoại lệ bằng cách sử dụng câu lệnh try, except và finally (sẽ tìm hiểu trong bài học tiếp theo).


# Bài 30. Xử lý ngoại lệ

Trong bài này chúng ta tìm tìm hiểu cách xử lý các ngoại lệ trong chương trình Python bằng câu lệnh try, exception và finally.

## Bắt ngoại lệ

Các câu lệnh xử lý chính có thể phát sinh ngoại lệ được đặt bên trong lệnh try. Các lệnh xử lý ngoại lệ được đặt bên trong câu lệnh except.

Để lấy tên ngoại lệ ta dùng phương thức exc\_info() trong module sys. Ví dụ như sau

```python
import sys

ma = ['a', 0, 2, 5]

for i in ma:
    try:
        print("Giá trị phần tử là", i)
        r = 1/int(i)
        print("Nghịch đảo của ", i, "là", r)
    except:
        print("Phát sinh ngoại lệ:", sys.exc_info()[0])
        print("Xét phẩn tử tiếp theo:")
```

Output:

```python
Giá trị phần tử là a
Phát sinh ngoại lệ: <class 'ValueError'>
Xét phẩn tử tiếp theo:
Giá trị phần tử là 0
Phát sinh ngoại lệ: <class 'ZeroDivisionError'>
Xét phẩn tử tiếp theo:
Giá trị phần tử là 2
Nghịch đảo của  2 là 0.5
Giá trị phần tử là 5
Nghịch đảo của  5 là 0.2
```

Ở ví dụ trên, phần tử đầu tiên là 'a' không thể thực hiện phép tính r = 1/'a' nên phát sinh ngoại lệ "`ValueError`" , phần tử thứ 2 là 0 phát sinh ngoại lệ "`ZeroDivisionError`" khi thực hiện phép tính 1/0.

Vì tất cả các ngoại lệ tích hợp sẵn đều kế thừa từ class Exception trong module sys nên ta có thể viết chương trình trên theo cách sau:

```python
import sys

ma = ['a', 0, 2, 5]

for i in ma:
    try:
        print("Giá trị phần tử là", i)
        r = 1/int(i)
        print("Nghịch đảo của ", i, "là", r)
    except Exception as ex:
        print("Phát sinh ngoại lệ:", ex.__class__ )
        print("Xét phẩn tử tiếp theo:")
```

## Bắt ngoại lệ cụ thể

Ta có thể dự đoán ngoại lệ phát sinh để bắt và xử lý theo từng trường hợp.&#x20;

Chỉ định ngoại lệ cụ thể cho khối lệnh except với cú pháp như sau:

```python
try:
 # khối lệnh try
except exceptionName:
 # khối lệnh except
except exceptionName2:
 # khối lệnh except 2
```

Ví dụ&#x20;

```python
ma = ['a', 0, 2, 5]

for i in ma:
    try:
        print("Giá trị phần tử là", i)
        r = 1/int(i)
        print("Nghịch đảo của ", i, "là", r)
        print()
    except ValueError:
        print("Lỗi giá trị phần tử không phù hợp.")
        print("Xét phẩn tử tiếp theo:")
        print()
    except ZeroDivisionError:
        print("Lỗi mẫu số bằng 0.")
        print("Xét phẩn tử tiếp theo:")
        print()
```

Ta cũng có thể bắt nhiều ngoại lệ trong một lệnh except. Ví dụ:

```python
ma = ['a', 0, 2, 5]

for i in ma:
    try:
        print("Giá trị phần tử là", i)
        r = 1/int(i)
        print("Nghịch đảo của ", i, "là", r)
        print()
    except (ValueError, ZeroDivisionError):
        print("Lỗi giá trị phần tử không phù hợp.")
        print("Xét phẩn tử tiếp theo:")
        print()
```

## Câu lệnh try else

Trong một số trường hợp, ta có thể muốn chạy một khối lệnh nhất định nào đó khi khối lệnh trong try không có bất kỳ lỗi nào. Đối với những trường hợp này, ta có thể sử dụng từ khóa else với câu lệnh try.

```python
try:
    num = int(input("Nhập một số chẵn: "))
    assert num % 2 == 0
except:
    print("Không phải số chẵn!")
else:
    r = 1/num
    print(f'Số nghị đảo của {num} là {r}')
```

Chương trình trên chỉ xử lý trường hợp được coi là ngoại lệ khi số nhập vào không là số chẵn. Các ngoại lệ khác sẽ không được xử lý. Ví dụ nhập vào số 0 thì chương trình sẽ báo lỗi ZeroDivisionError.

## Câu lệnh try finally

Câu lệnh try... finally được thực thi bất kể điều gì, và thường được sử dụng để giải phóng các tài nguyên bên ngoài như kết thúc phiên làm việc kết nối với trung tâm dữ liệu từ xa thông qua mạng hoặc làm việc với tệp hoặc Giao diện người dùng...

Ví dụ: đóng file sau khi mở và thao tác với file

```python
try:
   f = open("test.txt")
   # các thao tác xử lý file
finally:
   f.close()
```


# Bài 31. Xây dựng ngoại lệ

Ngoại lệ do người dùng tự định nghĩa

Trong lập trình Python, các ngoại lệ phát sinh khi lỗi logic. Ta cũng có thể sinh các ngoại lệ theo cách thủ công bằng cách sử dụng từ khóa raise.

Hãy xét một số ví dụ dưới đây và thực hành xây dựng ngoại lệ

## Ví dụ 1

Chương trình yêu cầu nhập một số thỏa điều kiện, lặp đến khi nào người dùng nhập đúng mới thôi.

```python
while True:
    try:
        x = int(input('Nhập một số nguyên trong khoảng 1-10: '))
        if x<1 or x>10:
            raise Exception
        print(f'Bạn vừa nhập một số hợp lệ: x = {x}')
        break
    except:
        print('Số vừa nhập ngoài khoảng cho phép. Nhập lại nhé!')
```

```python
while True:
    try:
        a = int(input("Nhập số nguyên dương: "))
        if a <= 0:
                raise ValueError("Đó không phải là số nguyên dương!")
        else: break
    except ValueError as ex:
            print(ex)

print(f'Thực hiện các thao tác với {a}')
```

## Ví dụ 2

Trò chơi đoán số. \
Chương trình sẽ sinh ngẫu nhiên một số trong khoảng 1 đến 99. Người chơi nhập số từ bàn phím đến khi trùng với số được sinh ngẫu nhiên trước đó. Khi đoán đúng, chương trình sẽ thông báo chúc mừng và đưa ra số lần đã đoán.

```python
class Loi(Exception):
    """Lớp cơ sở cho những ngoại lệ khác"""
    pass

class SoNhoQua(Loi):
    """Phát sinh khi số nhập vào nhỏ quá"""
    pass

class SoLonQua(Loi):
    """Phát sinh khi số nhập vào lớn quá"""
    pass

# Sinh ngẫu nhiên một số nguyên
import random as rd
number = rd.randint(1,100)

# nhập số đoán
landoan = 0
while True:
    try:
        n = int(input("Nhập vào số nguyên: "))
        landoan +=1
        if n < number:
            raise SoNhoQua
        elif n > number:
            raise SoLonQua
        break
    except SoNhoQua:
        print("Số này chưa đủ lớn - nhập lại nhé!")
        print()
    except SoLonQua:
        print("Số này lớn quá, thử lại xem!")
        print()

print(f"Xin chúc mừng! Bạn đã đoán chính xác trong {landoan} lần đoán.")
```

## Tùy chỉnh lớp ngoại lệ (Exception Classes)

Phần này liên quan đến lớp (class) thuộc kiến thức lập trình hướng đối tượng. Ta có xem qua ví dụ và sẽ trở lại tìm hiểu sau khi học về Lớp trong lập trình hướng đối tượng ở chương sau .

Ví dụ

```python
class LuongKhongHopLe(Exception):
    """Ngoại lệ này pháp sinh khi có lỗi nhập liệu.

    Attributes:
        luong -- số liệu nhập vào có thể phát sinh lỗi
        thongbao -- giải thích lỗi
    """

    def __init__(self, luong, thongbao='''Lương nhập vào 
                        ngoài khoảng (5000, 15000)'''):
        self.luong = luong
        self.thongbao = thongbao
        super().__init__(self.thongbao)


luong = int(input("Nhập lương: "))
if not 5000 < luong < 15000:
    raise LuongKhongHopLe(luong)
```

```python
class LuongKhongHopLe(Exception):
    """Exception raised for errors in the input luong.

    Attributes:
        luong -- input luong which caused the error
        thongbao -- explanation of the error
    """

    def __init__(self, luong, thongbao="Lương không trong khoảng (5000, 15000)"):
        self.luong = luong
        self.thongbao = thongbao
        super().__init__(self.thongbao)

    def __str__(self):
        return f'{self.luong} -> {self.thongbao}'


luong = int(input("Nhập số lương: "))
if not 5000 < luong < 15000:
    raise LuongKhongHopLe(luong)
```


# Chương VII. HƯỚNG ĐỐI TƯỢNG

Từ chương này chúng ta làm quen với việc đọc tài liệu gốc (tiếng Anh) vì dù muốn hay không, để tìm hiểu sâu về lập trình thì không thể không sử dụng tiếng Anh.


# Bài 32. Lập trình hướng đối tượng

Python Object Oriented Programming

Lập trình hướng đối tượng (Object-oriented programming, viết tắt: OOP) là một kỹ thuật hỗ trợ, cho phép lập trình viên trực tiếp làm việc với các đối tượng mà họ định nghĩa lên. Hiệu quả của kĩ thuật này giúp tăng năng suất, đơn giản hoá độ phức tạp khi bảo trì cũng như mở rộng phần mềm.&#x20;

Khái niệm về OOP trong Python tập trung vào việc tạo code sử dụng lại. Khái niệm này còn được gọi là DRY (Don't Repeat Yourself). OOP trong Python tuân theo một số nguyên lý cơ bản là tính đóng gói, tính kế thừa và tính đa hình.

* Tính kế thừa (Inheritance): cho phép một lớp (class) có thể kế thừa các thuộc tính và phương thức từ các lớp khác đã được định nghĩa.
* Tính đóng gói (Encapsulation): là quy tắc yêu cầu trạng thái bên trong của một đối tượng được bảo vệ và tránh truy cập được từ code bên ngoài (tức là code bên ngoài không thể trực tiếp nhìn thấy và thay đổi trạng thái của đối tượng đó).
* Tính đa hình (Polymorphism): là khái niệm mà hai hoặc nhiều lớp có những phương thức giống nhau nhưng có thể thực thi theo những cách thức khác nhau.

Lớp (Class) và Đối tượng (Object)

Class và Object là hai khái niệm cơ bản trong lập trình hướng đối tượng.

Đối tượng (Object) là những thực thể tồn tại có hành vi.

Ví dụ đối tượng là một con chim vẹt có tên, màu sắc, trọng lượng, hành vi (tập nói, ăn, bay, nhảy...)...

Lớp (Class) là một kiểu dữ liệu đặc biệt do người dùng định nghĩa, tập hợp nhiều thuộc tính đặc trưng cho mọi đối tượng được tạo ra từ lớp đó.

Thuộc tính là các giá trị của lớp. Sau này khi các đối tượng được tạo ra từ lớp, thì thuộc tính của lớp lúc này sẽ trở thành các đặc điểm của đối tượng đó.

## Lớp (Class)

Ta có thể hình dung class giống như là một bản mẫu (template), một khuôn mẫu. Ở đó ta khai báo các thuộc tính (attribute) và phương thức (method) nhằm miêu tả để từ đó ta tạo ra được những object (đối tượng).

Cú pháp để tạo một lớp

```python
class <tên_lớp>:
    code thuộc tính và phương thức
```

Ví dụ:

```python
class ConVet:
    pass
```

Thông thường (và theo chuẩn format code Python PEP8) thì tên lớp được viết hoa chữ đầu từ (CapWord).

## Đối tượng (Object)

Một đối tượng (còn gọi là instance) là một khởi tạo của một lớp.&#x20;

Khi lớp được định nghĩa, chỉ có mô tả cho đối tượng được định nghĩa. Do đó, không có bộ nhớ hoặc bộ nhớ nào được cấp phát. Khi khai báo đối tượng bằng việc gán cho một class thì lúc này bộ nhớ được cấp phát cho đối tượng.&#x20;

Ví dụ

```python
v1 = ConVet()
```

Ví dụ 1. Tạo class và object trong Python

```python
class ConVet:
    loai = "chim"

    def __init__(self, name, age):
        self.name = name
        self.age = age

em = ConVet("Vẹt Em", 10)
anh = ConVet("Vẹt Anh", 15)

# truy xuất thuộc tính class
print(f'Vẹt thuộc loài {anh.__class__.loai}')

# truy xuất thuộc tính đối tượng
print(f"{anh.name} {anh.age} năm tuổi")
print(f"{em.name} {em.age} năm tuổi")
```

Output:

```python
Vẹt thuộc loài chim
Vẹt Anh 15 năm tuổi
Vẹt Em 10 năm tuổi
```

Trong chương trình trên, ta đã tạo một class với tên ConVet và khai báo các thuộc tính (đặc tính của một đối tượng).

Các thuộc tính được định nghĩa bên trong phương thức \_\_init\_\_ của lớp. Đây là phương thức khởi tạo (constructor) được gọi đầu tiên ngay sau khi đối tượng được tạo.

Trong chương trình, ta tạo 2 đối tượng (thể hiện) của lớp ConVet gồm ble và woo.

Ta gọi thuộc tính class bằng cú pháp: \_\_class\_\_.\<tên thuộc tính lớp> (vd. \_\_class\_\_.loai); gọi thuộc tính đối tượng với cú pháp: \<tên đối tượng>.\<tên thuộc tính> (vd: blu.name, blu.age).

## Phương thức (Methods)

Các phương thức là các hàm được định nghĩa bên trong phần thân của một lớp. Chúng được sử dụng để xác định các hành vi của một đối tượng.

Ví dụ

```python
class ConVet:
	# thuộc tính
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    # phương thức
    def sing(self, song):
        return f"{self.name} hát bài {song}"

    def dance(self):
        return f"{self.name} đang nhảy múa"

# khởi tạo đối tượng
em = ConVet("Vẹt em", 10)

# gọi phương thức
print(em.sing("AAAAAAAAAAAA"))
print(em.dance())
```

Output:

```python
Vẹt em hát bài AAAAAAAAAAAA
Vẹt em đang nhảy múa
```

## Kế thừa (Inheritance)

Kế thừa là một cách tạo một lớp mới để sử dụng các chi tiết của một lớp hiện có mà không cần sửa đổi nó. Lớp mới được hình thành là một lớp dẫn xuất (hoặc lớp con). Tương tự, lớp hiện có là một lớp cơ sở (hoặc lớp cha).

Ví dụ

```python
# parent class (lớp cha)
class Chim:
    
    def __init__(self):
        print("Chim được khởi tạo")

    def boi(self):
        print("Bơi nhanh ")

    def bay(self):
        print("Chim hầu hết là biết bay")    

    def noio(self):
    	print('Hoang dã')

# child class (lớp con)
class CanhCut(Chim):

    def __init__(self):
        # gọi hàm super()
        super().__init__()
        print("Cánh cụt được khởi tạo")

    def chay(self):
        print("Chạy nhanh ")

    def bay(self):
        print("Chim cánh cụt thì không biết bay nhé")        

chimA = Chim()
chimA.bay()
chimA.boi()
print()

chimZ = CanhCut()
chimZ.boi()
chimZ.chay()
chimZ.noio()
chimZ.bay()
```

Output:

```python
Chim được khởi tạo
Chim hầu hết là biết bay
Bơi nhanh 

Chim được khởi tạo
Cánh cụt được khởi tạo
Bơi nhanh 
Chạy nhanh 
Hoang dã
Chim cánh cụt thì không biết bay nhé
```

Trong ví dụ trên ta dễ thấy lớp CanhCut thừa kế từ lớp Chim. Qua đó, đối tượng chimZ có thể gọi phương tức boi() và noio() trong khi lớp CanhCut không hề khai báo.

## Đóng gói (Encapsulation)

Ta có thể hạn chế quyền truy cập vào các phương thức (methods) và biến (variables) trong class. Điều này ngăn dữ liệu khỏi bị sửa đổi trực tiếp và được gọi là 'đóng gói'. Trong Python, ta biểu thị các thuộc tính riêng (private attributes) bằng cách sử dụng dấu 2 dấu gạch dưới làm tiền tố " \_\_ ".

Ví dụ 4. Đóng gói dữ diệu

```python
class Computer:

    def __init__(self):
        self.__giabaogia = 900

    def dinhgia(self, price):
        self.__giabaogia = price        

    def baogia(self):
        print(f"Giá bán: {self.__giabaogia}")


c = Computer()
c.baogia()

# thử thay đổi giá 
c.__giabaogia = 2000
c.baogia()

# đổi giá bằng phương thức
c.dinhgia(1000)
c.baogia()
```

Output:

```python
Giá bán: 900
Giá bán: 900
Giá bán: 1000
```

## Tính đa hình (Polymorphism)

Đa hình là một khả năng (trong OOP) sử dụng một giao diện chung cho nhiều biểu mẫu (kiểu dữ liệu).

Giả sử, chúng ta cần tô màu cho một hình dạng, có nhiều lựa chọn hình dạng (hình chữ nhật, hình vuông, hình tròn). Tuy nhiên, chúng ta có thể sử dụng cùng một phương pháp để tô màu bất kỳ hình dạng nào. Khái niệm này được gọi là Đa hình.

Ví dụ 5. Sử dụng tính đa hành

```python
class Vet:

    def fly(self):
        print("Vẹt có thể bay")
    
    def swim(self):
        print("Vẹt không biết bơi")

class Canhcut:

    def fly(self):
        print("Chim cánh cụt không thể bay")
    
    def swim(self):
        print("Chim cánh cụt bơi rất nhanh")

# common interface
def kt_bay(bird):
    bird.fly()

def kt_boi(b):
	b.swim()

#instantiate objects
con1 = Vet()
con2 = Canhcut()

# passing the object
kt_bay(con1)
kt_bay(con2)
kt_boi(con1)
kt_boi(con2)
```

Output:

```python
Vẹt có thể bay
Chim cánh cụt không thể bay
Vẹt không biết bơi
Chim cánh cụt bơi rất nhanh
```

Để sử dụng tính đa hình, ta đã tạo các 'giao diện chung', đó là hàm kt\_bay(), kt\_boi() và gọi phương thức cùng có ở các đối tượng.&#x20;

## Đáng chú ý với OOP

Lập trình hướng đối tượng làm cho chương trình dễ hiểu và hiệu quả.

Vì lớp có thể chia sẻ được, mã có thể được sử dụng lại.

Dữ liệu an toàn và bảo mật với dữ liệu trừu tượng.

Tính đa hình cho phép cùng một giao diện cho các đối tượng khác nhau, do đó các lập trình viên có thể viết mã hiệu quả.


# Bài 33. Đối tượng và Lớp

Python Objects and Classes

Đối tượng (Object) chỉ đơn giản là một tập hợp các dữ liệu (các biến) và các phương thức (các hàm) hoạt động trên các dữ liệu đó. Và, lớp (class) là một kế hoạch chi tiết cho đối tượng.

Ta có thể hình dung class như một bản phác thảo (nguyên mẫu) của một ngôi nhà. Nó chứa tất cả các chi tiết về sàn nhà, cửa ra vào, cửa sổ,... Dựa trên những mô tả này, chúng ta sẽ xây (hoặc in 3D) các ngôi nhà và nhà ở đây chính là đối tượng.

### Khai báo một Class <a href="#define" id="define"></a>

Cú pháp khai báo (định nghĩa) một Class như sau

```python
class TenClass:
    '''Đây là docstring để ghi chú cho class'''
    # các câu lệnh trong thân class 
```

Một class tạo ra một không gian tên cục bộ (local namespace) mới nơi tất cả các thuộc tính của nó được định nghĩa. Các thuộc tính có thể là dữ liệu hoặc hàm.

Ngoài ra còn có các thuộc tính đặc biệt được bắt đầu bằng dấu gạch dưới kép \_\_**. Ví dụ:** `__doc__`  cung cấp cho chúng ta docstring của class.

Ngay sau khi chúng ta định nghĩa một Class, một đối tượng lớp mới có cùng tên cũng được tạo. Đối tượng lớp này cho phép chúng ta truy cập các thuộc tính khác nhau cũng như khởi tạo các đối tượng mới của lớp đó.

```python
class Hocsinh:
    "Đây là một class có tên là Hocsinh"
    tuoi = 10

    def chao(self):
        print('Xin chào!')


# Output: 10
print(Hocsinh.tuoi)

# Output: <function Hocsinh.chao>
print(Hocsinh.chao)

# Output: 'Đây là một class có tên là Hocsinh'
print(Hocsinh.__doc__)
```

### Tạo đối tượng (Object) <a href="#create" id="create"></a>

Đối tượng trong class có thể được sử dụng để truy cập các thuộc tính khác nhau và tạo các instance mới của lớp đó. Thủ tục để tạo một đối tượng tương tự như cách chúng ta gọi hàm.

```python
hs = Hocsinh()
```

Thao tác trên sẽ tạo một đối tượng mới có tên là hs. Chúng ta có thể truy cập các thuộc tính của đối tượng bằng tiền tố tên đối tượng.&#x20;

Các thuộc tính có thể là dữ liệu hoặc phương thức. Các phương thức của một đối tượng là các hàm tương ứng của lớp đó.

Xem ví dụ sau:

```python
class Hocsinh:
    tuoi = 10

    def chao(self):
        print('Xin chào!')


# Tạo một đối tượng mới theo class Hocsinh
hs = Hocsinh()

# Output: <function Hocsinh.chao at ... >
print(Hocsinh.chao)

# Output: <bound method Hocsinh.chao of <__main__.Hocsinh object at ...>>
print(hs.chao)

# Gọi phương thức chao()
# Output: Xin chào!
hs.chao()
```

**Output**

```python
<function Hocsinh.chao at 0x00000133B8A57EE0>
<bound method Hocsinh.chao of <__main__.Hocsinh object at 0x00000133B8A45FD0>>
Xin chào!
```

Tham số self của hàm trong class là tham số mặc định ngầm gán bởi chính đối tượng mỗi khi đối tượng được gọi. Việc gọi phương thức chao() thông qua đối tượng  `hs.chao()` tương đương với gọi phương thức chao() từ Class `Hocsinh.chao(hs)`.

### Constructors  <a href="#constructor" id="constructor"></a>

Hàm trong Class được bắt đầu với dấu gạch dưới kép `__` là các hàm đặc biệt, mang các ý nghĩa đặc biệt.&#x20;Một trong đó là hàm `__init__()`. Hàm này được gọi bất cứ khi nào khởi tạo một đối tượng, một biến mới trong class và được gọi là **constructor** trong lập trình hướng đối tượng.

```python
class SoPhuc:

    def __init__(self, r=0, i=0):
        self.thuc = r
        self.ao = i

    def hienthi(self):
        print(f'{self.thuc}+{self.ao}j')


# Tạo đối tượng 
num1 = SoPhuc(2, 3)

# Gọi phương thức hienthi()
# Output: 2+3j
num1.hienthi()

# Tạo đối tượng SoPhuc khác
# thêm thuộc tính 'attr'
num2 = SoPhuc(5)
num2.attr = 10

# Output: (5, 0, 10)
print((num2.thuc, num2.ao, num2.attr))

# thuộc tính 'attr' được tạo cho đối tượng num2 nhưng không tồn tại cho num1
# AttributeError: 'SoPhuc' object has no attribute 'attr'
# print(num1.attr)
```

**Output**

```
2+3j
(5, 0, 10)
```

### Xóa bỏ thuộc tính và đối tượng

Any attribute of an object can be deleted anytime, using the `del` statement. Try the following on the Python shell to see the output.

```python
class SoPhuc:
    def __init__(self, r=0, i=0):
        self.thuc = r
        self.ao = i

    def hienthi(self):
        print(f'{self.thuc}+{self.ao}j')


# Tạo đối tượng 
num1 = SoPhuc(2, 3)

# Gọi phương thức hienthi()
# Output: 2+3j
num1.hienthi()

# Tạo đối tượng SoPhuc khác
# thêm thuộc tính 'attr'
num2 = SoPhuc(5)
num2.attr = 10

# Output: (5, 0, 10)
print((num2.thuc, num2.ao, num2.attr))

# thuộc tính 'attr' được tạo cho đối tượng num2 nhưng không tồn tại cho num1
# AttributeError: 'SoPhuc' object has no attribute 'attr'
# print(num1.attr)

# xóa phần ảo của đối tượng num1
del num1.ao 
# Lệnh sau sẽ gây lỗi vì không tồn tại thuộc tính 'ao'
# 'SoPhuc' object has no attribute 'ao'
print(num1.hienthi())


del SoPhuc.hienthi
# AttributeError: 'SoPhuc' object has no attribute 'hienthi'
print(num1.hienthi())

```

Ta có thể xóa bằng chính tên đối tượng với câu lệnh `del`.

```python
c1 = SoPhuc(1,3)
del c1
# NameError: name 'c1' is not defined
print(c1)
```


# Bài 34. Kế thừa

Python Inheritance

### Kế thừa trong Python (Inheritance) <a href="#what" id="what"></a>

Kế thừa là một tính năng mạnh mẽ trong lập trình hướng đối tượng.&#x20;

Kế thừa (Inheritance) cho phép một lớp (class) có thể kế thừa các thuộc tính và phương thức từ các lớp khác đã được định nghĩa. Lớp đã có gọi là lớp cha (base class hoặc parent class), lớp mới phát sinh gọi là lớp con (child class hoặc derived class). Lớp con kế thừa tất cả thành phần của lớp cha, có thể mở rộng các thành phần kế thừa và bổ sung thêm các thành phần mới.

#### Cú pháp khai báo <a href="#syntax" id="syntax"></a>

```python
class LopCha:
  # Các thành phần trong lớp cha
class LopCon(LopCha):
  # Các thành phần trong lớp con
```

#### Ví dụ về kế thừa trong Python <a href="#example" id="example"></a>

Xét ví dụ sau

```python
class Dagiac:
    def __init__(self, socanh):
        self.n = socanh
        self.canh = [0 for i in range(socanh)]

    def nhapcanh(self):
        self.canh = [float(input(f"Nhập cạnh {i+1}: ")) for i in range(self.n)]

    def xemcanh(self):
        for i in range(self.n):
            #print("Cạnh",i+1,"là",self.canh[i])
            print(f'Cạnh {i+1} là {self.canh[i]}')
            
class Tamgiac(Dagiac):
    def __init__(self):
        Dagiac.__init__(self,3)

    def dientichTG(self):
        a, b, c = self.canh
        s = (a + b + c) / 2
        dt = (s*(s-a)*(s-b)*(s-c)) ** 0.5
        print(f'Diện tích của tam giác là {dt:.2f}')            

tg = Tamgiac()

tg.nhapcanh()

tg.xemcanh()

tg.dientichTG()
```

```python
Nhập cạnh 1: 5
Nhập cạnh 2: 3
Nhập cạnh 3: 4

Cạnh 1 là 5.0
Cạnh 2 là 3.0
Cạnh 3 là 4.0
Diện tích của tam giác là 6.00
```

Ở lớp Dagiac có các thuộc tính gồm số cạnh và một list lưu độ dài mỗi cạnh. Các phương thức nhapcanh() và xemcanh() để nhập độ dài cạnh và hiển thị các cạnh đã nhập.

Ta xây dựng class Tamgiac thừa kế từ lớp Dagiac. Theo đó lớp Tamgiac thừa kế tất cả các thuộc tính, phương thức từ lớp cha là class Dagiac. Vì vậy, dùng lớp Tamgiac không có phương thức nhapcanh() và xemcanh() nhưng ta vẫn có thể gọi và sử dụng trong chương trình.

### Method Overriding in Python <a href="#method" id="method"></a>

Ở ví dụ trên ta thấy phương thức \_\_init\_\_() được khai báo ở cả 2 class (Dagiac và Tamgiac). Trong trường hợp này phương thức \_\_init\_\_() của lớp con sẽ ghi đề phương thức cùng tên của lớp cha. Nghĩa là phương thức instructor của Tamgiac ghi đè lên instructor của Dagiac.

Thông thường, việc ghi đè chỉ được dùng khi ta cần định nghĩa lại hoặc khai báo mới trong con so với lớp cha. Trường hợp không có gì khác thì gọi phương thức từ lớp cha để kế thừa (dùng lệnh gọi `Dagiac.__init__()` trong `__init__()` của `Tamgiac`).

Cách tốt hơn và thường được dùng hơn khi gọi kế thừa \_\_init\_\_ từ lớp cha người ta dùng hàm có sẵn super(). Thay vì gọi  `Dagiac.__init__(self, 3)` thì ta có thể gọi`super().__init__(3)` .

### Kiểm tra quan hệ 2 lớp

Hàm `isinstance()` và `issubclass()` được dùng để kiểm tra mối quan hệ của hai lớp và instance.

Hàm `issubclass(A, B)` trả về `True` nếu class A là lớp con của class B.

Hàm `isinstance(a,B)` trả về `True` nếu đối tượng a là một thể hiện (instance) của class B hoặc một class con của lớp B.

Ví dụ

```python
# True
print(isinstance(tg,Tamgiac))
# True
print(isinstance(tg,Dagiac))
# True
print(issubclass(Tamgiac,Dagiac))
# False
print(issubclass(Dagiac,Tamgiac))
```


# Bài 35. Đa kế thừa

Python Multiple Inheritance

* Ông cho cha kế thừa, cha cho con kế thừa thì con được kế thừa từ cả cha và ông.
* Một "người" có thể được kế thừa trực tiếp từ nhiều "người".

### Đa kế thừa trong Python (Multiple Inheritance) <a href="#inheritance" id="inheritance"></a>

Một lớp có thể được bắt nguồn từ nhiều hơn một lớp cơ sở (lớp cha) và như thế được gọi là đa kế thừa.

Trong đa kế thừa, các tính năng của tất cả các lớp cơ sở (lớp cha) được kế thừa vào lớp dẫn xuất (lướp con). Cú pháp của đa kế thừa tương tự như kế thừa đơn.

#### Ví dụ

```python
class Lopcha1:
    pass

class Lopcha2:
    pass

class Lopcondakethua(Lopcha1, Lopcha2):
    pass
```

Biểu đồ minh họa đa kế thừa từ 2 class.

![Multiple Inheritance in Python](https://cdn.programiz.com/sites/tutorial2program/files/MultipleInheritance.jpg)

### Kế thừa đa cấp trong Python (Multilevel Inheritance) <a href="#multilevel" id="multilevel"></a>

We can also inherit from a derived class. This is called multilevel inheritance. It can be of any depth in Python.

Ta có thể kế thừa từ một lớp dẫn xuất (lớp con) và như vậy được gọi là kế thừa đa cấp. Kế thừa đa cấp không giới hạn bao nhiêu cấp.

Trong kế thừa đa cấp, các tính năng của lớp cơ sở và lớp dẫn xuất được kế thừa vào lớp dẫn xuất mới.

Ví dụ

```python
class Base:
    pass

class Derived1(Base):
    pass

class Derived2(Derived1):
    pass
```

Ở ví dụ này, lớp Derived1 có nguồn gốc từ lớp Cơ sở và lớp Derived2 có nguồn gốc từ lớp Derived1.

![Multilevel Inheritance in Python](https://cdn.programiz.com/sites/tutorial2program/files/MultilevelInheritance.jpg)

### Thứ tự truy xuất phương thức (Method Resolution Order)

Mọi lớp trong Python đều có nguồn gốc từ lớp đối tượng. Nó là kiểu cơ sở nhất trong Python.

Vì vậy, về mặt kỹ thuật, tất cả các lớp khác, được tạo sẵn hoặc do người dùng định nghĩa, đều là các lớp dẫn xuất và tất cả các đối tượng là các thể hiện của lớp đối tượng.

```python
# Output: True
print(issubclass(list,object))

# Output: True
print(isinstance(5.5,object))

# Output: True
print(isinstance("Hello",object))
```

Trong kịch bản đa kế thừa, bất kỳ thuộc tính cần được truy xuất nào, đầu tiên sẽ được tìm kiếm trong lớp hiện tại. Nếu không tìm thấy, tìm kiếm tiếp tục vào lớp cha đầu tiên và từ trái qua phải.

Ở ví dụ đầu bài, lớp `Lopcondakethua` có thứ tự ưu tiên truy xuất như sau: \
\[`Lopcondakethua`, `Lopcha1`, `Lopcha2`, `object`].&#x20;

Để biết thứ tự truy xuất ta có thể dùng phương thức `__mro__` hoặc `mro()`. `__mro__` trả về một tuple còn `mro()` trả về một list

```python
class Lopcha1:
    pass

class Lopcha2:
    pass

class Lopcondakethua(Lopcha1, Lopcha2):
    pass

print(Lopcondakethua.__mro__)    
print(Lopcondakethua.mro())
```

Output:

```python
(<class '__main__.Lopcondakethua'>, 
<class '__main__.Lopcha1'>, 
<class '__main__.Lopcha2'>, 
<class 'object'>)

[<class '__main__.Lopcondakethua'>, 
<class '__main__.Lopcha1'>, 
<class '__main__.Lopcha2'>, 
<class 'object'>]
```

Dưới đây là một ví dụ thừa kế phức tạp và hiển thị trực quan của nó cùng với MRO.

![Multiple Inheritance Visualization](https://cdn.programiz.com/sites/tutorial2program/files/MRO.jpg)

```python
# Demonstration of MRO

class X:
    pass

class Y:
    pass

class Z:
    pass

class A(X, Y):
    pass

class B(Y, Z):
    pass

class M(B, A, Z):
    pass

print(M.mro())
```

**Output**

```python
[<class '__main__.M'>,
 <class '__main__.B'>,
 <class '__main__.A'>,
 <class '__main__.X'>,
 <class '__main__.Y'>,
 <class '__main__.Z'>,
 <class 'object'>]
```


# Bài 36. Nạp chồng toán tử

Python Operator Overloading

**You can change the meaning of an operator in Python depending upon the operands used. In this tutorial, you will learn how to use operator overloading in Python Object Oriented Programming.**

**Ta có thể thay đổi ý nghĩa của một toán tử trong Python tùy thuộc vào các toán hạng được sử dụng. Trong bài này ta sẽ học cách sử dụng nạp chồng toán tử trong Lập trình hướng đối tượng Python.**

### Nạp chồng toán tử **trong** Python

[Python operators](https://www.programiz.com/python-programming/operators) work for built-in classes. But the same operator behaves differently with different types. For example, the `+` operator will perform arithmetic addition on two numbers, merge two lists, or concatenate two strings.

Các toán tử Python hoạt động đối với các lớp dựng sẵn. Nhưng cùng một toán tử hoạt động khác nhau với các kiểu khác nhau. Ví dụ: toán tử + sẽ thực hiện phép cộng số học trên hai số, hợp nhất hai danh sách hoặc nối hai chuỗi.

This feature in Python that allows the same operator to have different meaning according to the context is called operator overloading.

Tính năng này trong Python cho phép cùng một toán tử có nghĩa khác nhau tùy theo ngữ cảnh được gọi là nạp chồng toán tử

So what happens when we use them with objects of a user-defined class? Let us consider the following class, which tries to simulate a point in 2-D coordinate system.

Vậy điều gì sẽ xảy ra khi chúng ta sử dụng chúng với các đối tượng của một lớp do người dùng định nghĩa? Chúng ta hãy xem xét lớp sau, lớp này mô phỏng một điểm trong hệ tọa độ 2-D.

```python
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y


p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
```

**Output**

```python
Traceback (most recent call last):
  File "<string>", line 9, in <module>
    print(p1+p2)
TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
```

Here, we can see that a `TypeError` was raised, since Python didn't know how to add two `Point` objects together.

However, we can achieve this task in Python through operator overloading. But first, let's get a notion about special functions.

### Python Special Functions <a href="#special" id="special"></a>

Class functions that begin with double underscore `__` are called special functions in Python.

These functions are not the typical functions that we define for a class. The `__init__()` function we defined above is one of them. It gets called every time we create a new object of that class.

There are numerous other special functions in Python. Visit [Python Special Functions](http://docs.python.org/3/reference/datamodel.html#special-method-names) to learn more about them.

Using special functions, we can make our class compatible with built-in functions.

```python
>>> p1 = Point(2,3)
>>> print(p1)
<__main__.Point object at 0x00000000031F8CC0>
```

Suppose we want the `print()` function to print the coordinates of the `Point` object instead of what we got. We can define a `__str__()` method in our class that controls how the object gets printed. Let's look at how we can achieve this:

```python
class Point:
    def __init__(self, x = 0, y = 0):
        self.x = x
        self.y = y
    
    def __str__(self):
        return "({0},{1})".format(self.x,self.y)
```

Now let's try the `print()` function again.

```python
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0}, {1})".format(self.x, self.y)


p1 = Point(2, 3)
print(p1)
```

**Output**

```
(2, 3)
```

That's better. Turns out, that this same method is invoked when we use the built-in function `str()` or `format()`.

```python
>>> str(p1)
'(2,3)'

>>> format(p1)
'(2,3)'
```

So, when you use `str(p1)` or `format(p1)`, Python internally calls the `p1.__str__()` method. Hence the name, special functions.

Now let's go back to operator overloading.

### Overloading the + Operator <a href="#overload-plus" id="overload-plus"></a>

To overload the `+` operator, we will need to implement `__add__()` function in the class. With great power comes great responsibility. We can do whatever we like, inside this function. But it is more sensible to return a `Point` object of the coordinate sum.

```python
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x, self.y)

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)
```

Now let's try the addition operation again:

```python
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x, self.y)

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Point(x, y)


p1 = Point(1, 2)
p2 = Point(2, 3)

print(p1+p2)
```

**Output**

```
(3,5)
```

What actually happens is that, when you use `p1 + p2`, Python calls `p1.__add__(p2)` which in turn is `Point.__add__(p1,p2)`. After this, the addition operation is carried out the way we specified.

Similarly, we can overload other operators as well. The special function that we need to implement is tabulated below.

| Operator            | Expression | Internally            |
| ------------------- | ---------- | --------------------- |
| Addition            | `p1 + p2`  | `p1.__add__(p2)`      |
| Subtraction         | `p1 - p2`  | `p1.__sub__(p2)`      |
| Multiplication      | `p1 * p2`  | `p1.__mul__(p2)`      |
| Power               | `p1 ** p2` | `p1.__pow__(p2)`      |
| Division            | `p1 / p2`  | `p1.__truediv__(p2)`  |
| Floor Division      | `p1 // p2` | `p1.__floordiv__(p2)` |
| Remainder (modulo)  | `p1 % p2`  | `p1.__mod__(p2)`      |
| Bitwise Left Shift  | `p1 << p2` | `p1.__lshift__(p2)`   |
| Bitwise Right Shift | `p1 >> p2` | `p1.__rshift__(p2)`   |
| Bitwise AND         | `p1 & p2`  | `p1.__and__(p2)`      |
| Bitwise OR          | `p1 \| p2` | `p1.__or__(p2)`       |
| Bitwise XOR         | `p1 ^ p2`  | `p1.__xor__(p2)`      |
| Bitwise NOT         | `~p1`      | `p1.__invert__()`     |

### Overloading Comparison Operators <a href="#overload-comparison" id="overload-comparison"></a>

Python does not limit operator overloading to arithmetic operators only. We can overload comparison operators as well.

Suppose we wanted to implement the less than symbol `<` symbol in our `Point` class.

Let us compare the magnitude of these points from the origin and return the result for this purpose. It can be implemented as follows.

```python
# overloading the less than operator
class Point:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __str__(self):
        return "({0},{1})".format(self.x, self.y)

    def __lt__(self, other):
        self_mag = (self.x ** 2) + (self.y ** 2)
        other_mag = (other.x ** 2) + (other.y ** 2)
        return self_mag < other_mag

p1 = Point(1,1)
p2 = Point(-2,-3)
p3 = Point(1,-1)

# use less than
print(p1<p2)
print(p2<p3)
print(p1<p3)
```

**Output**

```
True
False
False
```

Similarly, the special functions that we need to implement, to overload other comparison operators are tabulated below.

| Operator                 | Expression | Internally      |
| ------------------------ | ---------- | --------------- |
| Less than                | `p1 < p2`  | `p1.__lt__(p2)` |
| Less than or equal to    | `p1 <= p2` | `p1.__le__(p2)` |
| Equal to                 | `p1 == p2` | `p1.__eq__(p2)` |
| Not equal to             | `p1 != p2` | `p1.__ne__(p2)` |
| Greater than             | `p1 > p2`  | `p1.__gt__(p2)` |
| Greater than or equal to | `p1 >= p2` | `p1.__ge__(p2)` |


# Chương VIII. NGÀY - GIỜ


# Python datetime

**In this article, you will learn to manipulate date and time in Python with the help of examples.**

Python has a module named **datetime** to work with dates and times. Let's create a few simple programs related to date and time before we dig deeper.

#### Example 1: Get Current Date and Time

```
import datetime

datetime_object = datetime.datetime.now()
print(datetime_object)
```

When you run the program, the output will be something like:

```
2018-12-19 09:26:03.478039
```

Here, we have imported **datetime** module using `import datetime` statement.

One of the classes defined in the `datetime` module is `datetime` class. We then used `now()` method to create a `datetime` object containing the current local date and time.

#### Example 2: Get Current Date

```

import datetime

date_object = datetime.date.today()
print(date_object)
```

When you run the program, the output will be something like:

```
2018-12-19
```

In this program, we have used `today()` method defined in the `date` class to get a `date` object containing the current local date.

**What's inside datetime?**

We can use [dir()](https://www.programiz.com/python-programming/methods/built-in/dir) function to get a list containing all attributes of a module.

```
import datetime

print(dir(datetime))
```

When you run the program, the output will be:

```

['MAXYEAR', 'MINYEAR', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_divide_and_round', 'date', 'datetime', 'datetime_CAPI', 'time', 'timedelta', 'timezone', 'tzinfo']
```

Commonly used classes in the datetime module are:

* date Class
* time Class
* datetime Class
* timedelta Class

### datetime.date Class

You can instantiate `date` objects from the `date` class. A date object represents a date (year, month and day).

#### Example 3: Date object to represent a date

```

import datetime

d = datetime.date(2019, 4, 13)
print(d)
```

When you run the program, the output will be:

```
2019-04-13
```

If you are wondering, `date()` in the above example is a constructor of the `date` class. The constructor takes three arguments: year, month and day.

The variable a is a `date` object.

We can only import `date` class from the `datetime` module. Here's how:

```

from datetime import date

a = date(2019, 4, 13)
print(a)
```

#### Example 4: Get current date

You can create a `date` object containing the current date by using a classmethod named `today()`. Here's how:

```

from datetime import date

today = date.today()

print("Current date =", today)
```

#### Example 5: Get date from a timestamp

We can also create `date` objects from a timestamp. A Unix timestamp is the number of seconds between a particular date and January 1, 1970 at UTC. You can convert a timestamp to date using `fromtimestamp()` method.

```

from datetime import date

timestamp = date.fromtimestamp(1326244364)
print("Date =", timestamp)
```

When you run the program, the output will be:

```
Date = 2012-01-11
```

#### Example 6: Print today's year, month and day

We can get year, month, day, day of the week etc. from the date object easily. Here's how:

```

from datetime import date

# date object of today's date
today = date.today() 

print("Current year:", today.year)
print("Current month:", today.month)
print("Current day:", today.day)
```

### datetime.time

A time object instantiated from the `time` class represents the local time.

#### Example 7: Time object to represent time

```

from datetime import time

# time(hour = 0, minute = 0, second = 0)
a = time()
print("a =", a)

# time(hour, minute and second)
b = time(11, 34, 56)
print("b =", b)

# time(hour, minute and second)
c = time(hour = 11, minute = 34, second = 56)
print("c =", c)

# time(hour, minute, second, microsecond)
d = time(11, 34, 56, 234566)
print("d =", d)
```

When you run the program, the output will be:

```

a = 00:00:00
b = 11:34:56
c = 11:34:56
d = 11:34:56.234566
```

#### Example 8: Print hour, minute, second and microsecond

Once you create a `time` object, you can easily print its attributes such as hour, minute etc.

```

from datetime import time

a = time(11, 34, 56)

print("hour =", a.hour)
print("minute =", a.minute)
print("second =", a.second)
print("microsecond =", a.microsecond)
```

When you run the example, the output will be:

```

hour = 11
minute = 34
second = 56
microsecond = 0
```

Notice that we haven't passed microsecond argument. Hence, its default value `0` is printed.

### datetime.datetime

The `datetime` module has a class named `dateclass` that can contain information from both **date** and **time** objects.

#### Example 9: Python datetime object

```

from datetime import datetime

#datetime(year, month, day)
a = datetime(2018, 11, 28)
print(a)

# datetime(year, month, day, hour, minute, second, microsecond)
b = datetime(2017, 11, 28, 23, 55, 59, 342380)
print(b)
```

When you run the program, the output will be:

```

2018-11-28 00:00:00
2017-11-28 23:55:59.342380
```

The first three arguments year, month and day in the `datetime()` constructor are mandatory.

#### Example 10: Print year, month, hour, minute and timestamp

```

from datetime import datetime

a = datetime(2017, 11, 28, 23, 55, 59, 342380)
print("year =", a.year)
print("month =", a.month)
print("hour =", a.hour)
print("minute =", a.minute)
print("timestamp =", a.timestamp())
```

When you run the program, the output will be:

```

year = 2017
month = 11
day = 28
hour = 23
minute = 55
timestamp = 1511913359.34238
```

### datetime.timedelta

A `timedelta` object represents the difference between two dates or times.

#### Example 11: Difference between two dates and times

```

from datetime import datetime, date

t1 = date(year = 2018, month = 7, day = 12)
t2 = date(year = 2017, month = 12, day = 23)
t3 = t1 - t2
print("t3 =", t3)

t4 = datetime(year = 2018, month = 7, day = 12, hour = 7, minute = 9, second = 33)
t5 = datetime(year = 2019, month = 6, day = 10, hour = 5, minute = 55, second = 13)
t6 = t4 - t5
print("t6 =", t6)

print("type of t3 =", type(t3)) 
print("type of t6 =", type(t6))  
```

When you run the program, the output will be:

```

t3 = 201 days, 0:00:00
t6 = -333 days, 1:14:20
type of t3 = <class 'datetime.timedelta'>
type of t6 = <class 'datetime.timedelta'>
```

Notice, both t3 and t6 are of `<class 'datetime.timedelta'>` type.

#### Example 12: Difference between two timedelta objects

```

from datetime import timedelta

t1 = timedelta(weeks = 2, days = 5, hours = 1, seconds = 33)
t2 = timedelta(days = 4, hours = 11, minutes = 4, seconds = 54)
t3 = t1 - t2

print("t3 =", t3)
```

When you run the program, the output will be:

```

t3 = 14 days, 13:55:39
```

Here, we have created two `timedelta` objects t1 and t2, and their difference is printed on the screen.

#### Example 13: Printing negative timedelta object

```

from datetime import timedelta

t1 = timedelta(seconds = 33)
t2 = timedelta(seconds = 54)
t3 = t1 - t2

print("t3 =", t3)
print("t3 =", abs(t3))
```

When you run the program, the output will be:

```

t3 = -1 day, 23:59:39
t3 = 0:00:21
```

#### Example 14: Time duration in seconds

You can get the total number of seconds in a timedelta object using `total_seconds()` method.

```

from datetime import timedelta

t = timedelta(days = 5, hours = 1, seconds = 33, microseconds = 233423)
print("total seconds =", t.total_seconds())
```

When you run the program, the output will be:

```

total seconds = 435633.233423
```

You can also find sum of two dates and times using `+` operator. Also, you can multiply and divide a `timedelta` object by integers and floats.

### Python format datetime

The way date and time is represented may be different in different places, organizations etc. It's more common to use `mm/dd/yyyy` in the US, whereas `dd/mm/yyyy` is more common in the UK.

Python has `strftime()` and `strptime()` methods to handle this.

#### Python strftime() - datetime object to string

The `strftime()` method is defined under classes `date`, `datetime` and `time`. The method creates a formatted string from a given `date`, `datetime` or `time` object.

#### Example 15: Format date using strftime()

```

from datetime import datetime

# current date and time
now = datetime.now()

t = now.strftime("%H:%M:%S")
print("time:", t)

s1 = now.strftime("%m/%d/%Y, %H:%M:%S")
# mm/dd/YY H:M:S format
print("s1:", s1)

s2 = now.strftime("%d/%m/%Y, %H:%M:%S")
# dd/mm/YY H:M:S format
print("s2:", s2)
```

When you run the program, the output will be something like:

```

time: 04:34:52
s1: 12/26/2018, 04:34:52
s2: 26/12/2018, 04:34:52
```

Here, `%Y`, `%m`, `%d`, `%H` etc. are format codes. The `strftime()` method takes one or more format codes and returns a formatted string based on it.

In the above program, t, s1 and s2 are strings.

* `%Y` - year \[0001,..., 2018, 2019,..., 9999]
* `%m` - month \[01, 02, ..., 11, 12]
* `%d` - day \[01, 02, ..., 30, 31]
* `%H` - hour \[00, 01, ..., 22, 23
* `%M` - minute \[00, 01, ..., 58, 59]
* `%S` - second \[00, 01, ..., 58, 59]

To learn more about `strftime()` and format codes, visit: [Python strftime()](https://www.programiz.com/python-programming/datetime/strftime).

#### Python strptime() - string to datetime

The `strptime()` method creates a `datetime` object from a given string (representing date and time).

#### Example 16: strptime()

```

from datetime import datetime

date_string = "21 June, 2018"
print("date_string =", date_string)

date_object = datetime.strptime(date_string, "%d %B, %Y")
print("date_object =", date_object)
```

When you run the program, the output will be:

```

date_string = 21 June, 2018
date_object = 2018-06-21 00:00:00
```

The `strptime()` method takes two arguments:

1. a string representing date and time
2. format code equivalent to the first argument

By the way, `%d`, `%B` and `%Y` format codes are used for day, month(full name) and year respectively.

Visit [Python strptime()](https://www.programiz.com/python-programming/datetime/strptime) to learn more.

### Handling timezone in Python

Suppose, you are working on a project and need to display date and time based on their timezone. Rather than trying to handle timezone yourself, we suggest you to use a third-party [pytZ module](http://pytz.sourceforge.net/).

```

from datetime import datetime
import pytz

local = datetime.now()
print("Local:", local.strftime("%m/%d/%Y, %H:%M:%S"))


tz_NY = pytz.timezone('America/New_York') 
datetime_NY = datetime.now(tz_NY)
print("NY:", datetime_NY.strftime("%m/%d/%Y, %H:%M:%S"))

tz_London = pytz.timezone('Europe/London')
datetime_London = datetime.now(tz_London)
print("London:", datetime_London.strftime("%m/%d/%Y, %H:%M:%S"))
```

When you run the program, the output will be something like:

```

Local time: 2018-12-20 13:10:44.260462
America/New_York time: 2018-12-20 13:10:44.260462
Europe/London time: 2018-12-20 13:10:44.260462	
```

Here, datetime\_NY and datetime\_London are datetime objects containing the current date and time of their respective timezone.


# Python strftime()

**In this article, you will learn to convert date, time and datetime objects to its equivalent string (with the help of examples)**

The `strftime()` method returns a string representing date and time using [date](https://www.programiz.com/python-programming/datetime#date), [time](https://www.programiz.com/python-programming/datetime#time) or [datetime](https://www.programiz.com/python-programming/datetime#datetime) object.

### Example 1: datetime to string using strftime()

The program below converts a `datetime` object containing current date and time to different string formats.

```

from datetime import datetime

now = datetime.now() # current date and time

year = now.strftime("%Y")
print("year:", year)

month = now.strftime("%m")
print("month:", month)

day = now.strftime("%d")
print("day:", day)

time = now.strftime("%H:%M:%S")
print("time:", time)

date_time = now.strftime("%m/%d/%Y, %H:%M:%S")
print("date and time:",date_time)	
```

When you run the program, the output will something like be:

```

year: 2018
month: 12
day: 24
time: 04:59:31
date and time: 12/24/2018, 04:59:31
```

Here, year, day, time and date\_time are strings, whereas now is a `datetime` object.

### How strftime() works?

In the above program, `%Y`, `%m`, `%d` etc. are format codes. The `strftime()` method takes one or more format codes as an argument and returns a formatted string based on it.

1. We imported `datetime` class from the `datetime` module. It's because the object of `datetime` class can access `strftime()` method.\
   \
   ![Import datetime module in Python](https://cdn.programiz.com/sites/tutorial2program/files/import-datetime.jpg)\
   \
   &#x20;
2. The `datetime` object containing current date and time is stored in now variable.\
   \
   ![datetime object containing current date and time](https://cdn.programiz.com/sites/tutorial2program/files/current-date-time.jpg)\
   \
   &#x20;
3. The `strftime()` method can be used to create formatted strings.\
   \
   ![Python strftime() example](https://cdn.programiz.com/sites/tutorial2program/files/python-strftime-format-1.jpg)\
   \
   &#x20;
4. The string you pass to the `strftime()` method may contain more than one format codes.\
   \
   ![Python strftime() example](https://cdn.programiz.com/sites/tutorial2program/files/python-strftime-format-2.jpg)\
   &#x20;

### Example 2: Creating string from a timestamp

```

from datetime import datetime

timestamp = 1528797322
date_time = datetime.fromtimestamp(timestamp)

print("Date time object:", date_time)

d = date_time.strftime("%m/%d/%Y, %H:%M:%S")
print("Output 2:", d)	

d = date_time.strftime("%d %b, %Y")
print("Output 3:", d)

d = date_time.strftime("%d %B, %Y")
print("Output 4:", d)

d = date_time.strftime("%I%p")
print("Output 5:", d)
```

When you run the program, the output will be:

```
Date time object: 2018-06-12 09:55:22
Output 2: 06/12/2018, 09:55:22
Output 3: 12 Jun, 2018
Output 4: 12 June, 2018
Output 5: 09AM
```

### Format Code List

The table below shows all the codes that you can pass to the `strftime()` method.

| **Directive** | **Meaning**                                                                                                                                      | **Example**              |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ |
| `%a`          | Abbreviated weekday name.                                                                                                                        | Sun, Mon, ...            |
| `%A`          | Full weekday name.                                                                                                                               | Sunday, Monday, ...      |
| `%w`          | Weekday as a decimal number.                                                                                                                     | 0, 1, ..., 6             |
| `%d`          | Day of the month as a zero-padded decimal.                                                                                                       | 01, 02, ..., 31          |
| `%-d`         | Day of the month as a decimal number.                                                                                                            | 1, 2, ..., 30            |
| `%b`          | Abbreviated month name.                                                                                                                          | Jan, Feb, ..., Dec       |
| `%B`          | Full month name.                                                                                                                                 | January, February, ...   |
| `%m`          | Month as a zero-padded decimal number.                                                                                                           | 01, 02, ..., 12          |
| `%-m`         | Month as a decimal number.                                                                                                                       | 1, 2, ..., 12            |
| `%y`          | Year without century as a zero-padded decimal number.                                                                                            | 00, 01, ..., 99          |
| `%-y`         | Year without century as a decimal number.                                                                                                        | 0, 1, ..., 99            |
| `%Y`          | Year with century as a decimal number.                                                                                                           | 2013, 2019 etc.          |
| `%H`          | Hour (24-hour clock) as a zero-padded decimal number.                                                                                            | 00, 01, ..., 23          |
| `%-H`         | Hour (24-hour clock) as a decimal number.                                                                                                        | 0, 1, ..., 23            |
| `%I`          | Hour (12-hour clock) as a zero-padded decimal number.                                                                                            | 01, 02, ..., 12          |
| `%-I`         | Hour (12-hour clock) as a decimal number.                                                                                                        | 1, 2, ... 12             |
| `%p`          | Locale’s AM or PM.                                                                                                                               | AM, PM                   |
| `%M`          | Minute as a zero-padded decimal number.                                                                                                          | 00, 01, ..., 59          |
| `%-M`         | Minute as a decimal number.                                                                                                                      | 0, 1, ..., 59            |
| `%S`          | Second as a zero-padded decimal number.                                                                                                          | 00, 01, ..., 59          |
| `%-S`         | Second as a decimal number.                                                                                                                      | 0, 1, ..., 59            |
| `%f`          | Microsecond as a decimal number, zero-padded on the left.                                                                                        | 000000 - 999999          |
| `%z`          | UTC offset in the form +HHMM or -HHMM.                                                                                                           |                          |
| `%Z`          | Time zone name.                                                                                                                                  |                          |
| `%j`          | Day of the year as a zero-padded decimal number.                                                                                                 | 001, 002, ..., 366       |
| `%-j`         | Day of the year as a decimal number.                                                                                                             | 1, 2, ..., 366           |
| `%U`          | Week number of the year (Sunday as the first day of the week). All days in a new year preceding the first Sunday are considered to be in week 0. | 00, 01, ..., 53          |
| `%W`          | Week number of the year (Monday as the first day of the week). All days in a new year preceding the first Monday are considered to be in week 0. | 00, 01, ..., 53          |
| `%c`          | Locale’s appropriate date and time representation.                                                                                               | Mon Sep 30 07:06:05 2013 |
| `%x`          | Locale’s appropriate date representation.                                                                                                        | 09/30/13                 |
| `%X`          | Locale’s appropriate time representation.                                                                                                        | 07:06:05                 |
| `%%`          | A literal '%' character.                                                                                                                         | %                        |

### Example 3: Locale's appropriate date and time

```

from datetime import datetime

timestamp = 1528797322
date_time = datetime.fromtimestamp(timestamp)

d = date_time.strftime("%c")
print("Output 1:", d)	

d = date_time.strftime("%x")
print("Output 2:", d)

d = date_time.strftime("%X")
print("Output 3:", d)
```

When you run the program, the output will be:

```

Output 1: Tue Jun 12 09:55:22 2018
Output 2: 06/12/18
Output 3: 09:55:22
```

Format codes `%c`, `%x` and `%X` are used for locale's appropriate date and time representation.

We also recommend you to check [Python strptime()](https://www.programiz.com/python-programming/datetime/strptime). The `strptime()` method creates a `datetime` object from a string.


# Python strptime()

**In this article, you will learn to create a datetime object from a string (with the help of examples).**

The `strptime()` method creates a [datetime](https://www.programiz.com/python-programming/datetime#datetime) object from the given string.

**Note:** You cannot create `datetime` object from every string. The string needs to be in a certain format.

### Example 1: string to datetime object

```

from datetime import datetime

date_string = "21 June, 2018"

print("date_string =", date_string)
print("type of date_string =", type(date_string))

date_object = datetime.strptime(date_string, "%d %B, %Y")

print("date_object =", date_object)
print("type of date_object =", type(date_object))
```

When you run the program, the output will be:

```

date_string = 21 June, 2018
type of date_string = <class 'str'>
date_object = 2018-06-21 00:00:00
type of date_object = <class 'datetime.datetime'>
```

#### How strptime() works?

The `strptime()` class method takes two arguments:

* string (that be converted to datetime)
* format code

Based on the string and format code used, the method returns its equivalent `datetime` object.

In the above example:

![How strptime() works in Python?](https://cdn.programiz.com/sites/tutorial2program/files/python-strptime.jpg)

Here,

* `%d` - Represents the day of the month. **Example:** 01, 02, ..., 31
* `%B` - Month's name in full. **Example:** January, February etc.
* `%Y` - Year in four digits. **Example:** 2018, 2019 etc.

### Example 2: string to datetime object

```

from datetime import datetime

dt_string = "12/11/2018 09:15:32"

# Considering date is in dd/mm/yyyy format
dt_object1 = datetime.strptime(dt_string, "%d/%m/%Y %H:%M:%S")
print("dt_object1 =", dt_object1)

# Considering date is in mm/dd/yyyy format
dt_object2 = datetime.strptime(dt_string, "%m/%d/%Y %H:%M:%S")
print("dt_object2 =", dt_object2)
```

When you run the program, the output will be:

```
dt_object1 = 2018-11-12 09:15:32
dt_object2 = 2018-12-11 09:15:32
```

### Format Code List

The table below shows all the format codes that you can use.

| **Directive** | **Meaning**                                                                                                                                      | **Example**              |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ |
| `%a`          | Abbreviated weekday name.                                                                                                                        | Sun, Mon, ...            |
| `%A`          | Full weekday name.                                                                                                                               | Sunday, Monday, ...      |
| `%w`          | Weekday as a decimal number.                                                                                                                     | 0, 1, ..., 6             |
| `%d`          | Day of the month as a zero-padded decimal.                                                                                                       | 01, 02, ..., 31          |
| `%-d`         | Day of the month as a decimal number.                                                                                                            | 1, 2, ..., 30            |
| `%b`          | Abbreviated month name.                                                                                                                          | Jan, Feb, ..., Dec       |
| `%B`          | Full month name.                                                                                                                                 | January, February, ...   |
| `%m`          | Month as a zero-padded decimal number.                                                                                                           | 01, 02, ..., 12          |
| `%-m`         | Month as a decimal number.                                                                                                                       | 1, 2, ..., 12            |
| `%y`          | Year without century as a zero-padded decimal number.                                                                                            | 00, 01, ..., 99          |
| `%-y`         | Year without century as a decimal number.                                                                                                        | 0, 1, ..., 99            |
| `%Y`          | Year with century as a decimal number.                                                                                                           | 2013, 2019 etc.          |
| `%H`          | Hour (24-hour clock) as a zero-padded decimal number.                                                                                            | 00, 01, ..., 23          |
| `%-H`         | Hour (24-hour clock) as a decimal number.                                                                                                        | 0, 1, ..., 23            |
| `%I`          | Hour (12-hour clock) as a zero-padded decimal number.                                                                                            | 01, 02, ..., 12          |
| `%-I`         | Hour (12-hour clock) as a decimal number.                                                                                                        | 1, 2, ... 12             |
| `%p`          | Locale’s AM or PM.                                                                                                                               | AM, PM                   |
| `%M`          | Minute as a zero-padded decimal number.                                                                                                          | 00, 01, ..., 59          |
| `%-M`         | Minute as a decimal number.                                                                                                                      | 0, 1, ..., 59            |
| `%S`          | Second as a zero-padded decimal number.                                                                                                          | 00, 01, ..., 59          |
| `%-S`         | Second as a decimal number.                                                                                                                      | 0, 1, ..., 59            |
| `%f`          | Microsecond as a decimal number, zero-padded on the left.                                                                                        | 000000 - 999999          |
| `%z`          | UTC offset in the form +HHMM or -HHMM.                                                                                                           |                          |
| `%Z`          | Time zone name.                                                                                                                                  |                          |
| `%j`          | Day of the year as a zero-padded decimal number.                                                                                                 | 001, 002, ..., 366       |
| `%-j`         | Day of the year as a decimal number.                                                                                                             | 1, 2, ..., 366           |
| `%U`          | Week number of the year (Sunday as the first day of the week). All days in a new year preceding the first Sunday are considered to be in week 0. | 00, 01, ..., 53          |
| `%W`          | Week number of the year (Monday as the first day of the week). All days in a new year preceding the first Monday are considered to be in week 0. | 00, 01, ..., 53          |
| `%c`          | Locale’s appropriate date and time representation.                                                                                               | Mon Sep 30 07:06:05 2013 |
| `%x`          | Locale’s appropriate date representation.                                                                                                        | 09/30/13                 |
| `%X`          | Locale’s appropriate time representation.                                                                                                        | 07:06:05                 |
| `%%`          | A literal '%' character.                                                                                                                         | %                        |

#### ValueError in strptime()

If the string (first argument) and the format code (second argument) passed to the `strptime()` doesn't match, you will get `ValueError`. For example:

```

from datetime import datetime

date_string = "12/11/2018"
date_object = datetime.strptime(date_string, "%d %m %Y")

print("date_object =", date_object)
```

If you run this program, you will get an error.

```
ValueError: time data '12/11/2018' does not match format '%d %m %Y'
```


# Current date and time

**In this article, you will learn to get today's date and current date and time in Python. We will also format the date and time in different formats using strftime() method.**

There are a number of ways you can take to get the current date. We will use the `date` class of the [datetime](https://www.programiz.com/python-programming/datetime) module to accomplish this task.

### Example 1: Python get today's date

```
from datetime import date

today = date.today()
print("Today's date:", today)
```

Here, we imported the `date` class from the `datetime` module. Then, we used the `date.today()` method to get the current local date.

By the way, `date.today()` returns a `date` object, which is assigned to the today variable in the above program. Now, you can use the [strftime()](https://www.programiz.com/python-programming/datetime/strftime) method to create a string representing date in different formats.

### Example 2: Current date in different formats

```
from datetime import date

today = date.today()

# dd/mm/YY
d1 = today.strftime("%d/%m/%Y")
print("d1 =", d1)

# Textual month, day and year	
d2 = today.strftime("%B %d, %Y")
print("d2 =", d2)

# mm/dd/y
d3 = today.strftime("%m/%d/%y")
print("d3 =", d3)

# Month abbreviation, day and year	
d4 = today.strftime("%b-%d-%Y")
print("d4 =", d4)
```

When you run the program, the output will be something like:

```
d1 = 16/09/2019
d2 = September 16, 2019
d3 = 09/16/19
d4 = Sep-16-2019
```

If you need to get the current date and time, you can use `datetime` class of the `datetime` module.

### Example 3: Get the current date and time

```
from datetime import datetime

# datetime object containing current date and time
now = datetime.now()
 
print("now =", now)

# dd/mm/YY H:M:S
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
print("date and time =", dt_string)	
```

Here, we have used `datetime.now()` to get the current date and time. Then, we used `strftime()` to create a string representing date and time in another format.


# Get Current time

**In this article, you will learn to get current time of your locale as well as different time zones in Python.**

There are a number of ways you can take to get current time in Python.

### Example 1: Current time using datetime object

```
from datetime import datetime

now = datetime.now()

current_time = now.strftime("%H:%M:%S")
print("Current Time =", current_time)
```

**Output**

```
Current Time = 07:41:19
```

In the above example, we have imported `datetime` class from the [datetime](https://www.programiz.com/python-programming/datetime) module. Then, we used `now()` method to get a `datetime` object containing current date and time.

Using [datetime.strftime()](https://www.programiz.com/python-programming/datetime/strftime) method, we then created a string representing current time.

If you need to create a `time` object containing current time, you can do something like this.

```
from datetime import datetime

now = datetime.now().time() # time object

print("now =", now)
print("type(now) =", type(now))	
```

**Output**

```
now = 07:43:37.457423
type(now) = <class 'datetime.time'>
```

### Example 2: Current time using time module

You can also get the current time using time module.

```
import time

t = time.localtime()
current_time = time.strftime("%H:%M:%S", t)
print(current_time)
```

**Output**

```
07:46:58
```

### Example 3: Current time of a timezone

If you need to find current time of a certain timezone, you can use [pytZ module](http://pytz.sourceforge.net/).

```
from datetime import datetime
import pytz

tz_NY = pytz.timezone('America/New_York') 
datetime_NY = datetime.now(tz_NY)
print("NY time:", datetime_NY.strftime("%H:%M:%S"))

tz_London = pytz.timezone('Europe/London')
datetime_London = datetime.now(tz_London)
print("London time:", datetime_London.strftime("%H:%M:%S"))
```

**Output**

```
NY time: 03:45:16
London time: 08:45:16
```


# Timestamp to datetime

**In this article, you will learn to convert timestamp to datetime object and datetime object to timestamp (with the help of examples).**

It's pretty common to store date and time as a timestamp in a database. A Unix timestamp is the number of seconds between a particular date and January 1, 1970 at UTC.

### Example 1: Python timestamp to datetime

```
from datetime import datetime

timestamp = 1545730073
dt_object = datetime.fromtimestamp(timestamp)

print("dt_object =", dt_object)
print("type(dt_object) =", type(dt_object))
```

When you run the program, the output will be:

```
dt_object = 2018-12-25 09:27:53
type(dt_object) = <class 'datetime.datetime'>
```

Here, we have imported `datetime` class from the [datetime](https://www.programiz.com/python-programming/datetime) module. Then, we used `datetime.fromtimestamp()` classmethod which returns the local date and time (datetime object). This object is stored in dt\_object variable.

**Note:** You can easily create a string representing date and time from a `datetime` object using [strftime()](https://www.programiz.com/python-programming/datetime/strftime) method.

### Example 2: Python datetime to timestamp

You can get timestamp from a datetime object using `datetime.timestamp()` method.

```
from datetime import datetime

# current date and time
now = datetime.now()

timestamp = datetime.timestamp(now)
print("timestamp =", timestamp)
```


# Python time Module

**In this article, we will explore time module in detail. We will learn to use different time-related functions defined in the time module with the help of examples.**

Python has a module named `time` to handle time-related tasks. To use functions defined in the module, we need to import the module first. Here's how:

```
import time
```

Here are commonly used time-related functions.

#### Python time.time()

The `time()` function returns the number of seconds passed since epoch.

For Unix system, `January 1, 1970, 00:00:00` at **UTC** is epoch (the point where time begins).

```
import time
seconds = time.time()
print("Seconds since epoch =", seconds)	
```

#### Python time.ctime()

The `time.ctime()` function takes seconds passed since epoch as an argument and returns a string representing local time.

```
import time

# seconds passed since epoch
seconds = 1545925769.9618232
local_time = time.ctime(seconds)
print("Local time:", local_time)	
```

If you run the program, the output will be something like:

```
Local time: Thu Dec 27 15:49:29 2018
```

#### Python time.sleep()

The `sleep()` function suspends (delays) execution of the current thread for the given number of seconds.

```
import time

print("This is printed immediately.")
time.sleep(2.4)
print("This is printed after 2.4 seconds.")
```

To learn more, visit: [Python sleep()](https://www.programiz.com/python-programming/time/sleep).

Before we talk about other time-related functions, let's explore `time.struct_time` class in brief.

### time.struct\_time Class

Several functions in the `time` module such as `gmtime()`, `asctime()` etc. either take `time.struct_time` object as an argument or return it.

Here's an example of `time.struct_time` object.

```
time.struct_time(tm_year=2018, tm_mon=12, tm_mday=27, 
                    tm_hour=6, tm_min=35, tm_sec=17, 
                    tm_wday=3, tm_yday=361, tm_isdst=0)
```

| Index | Attribute  | Values                      |
| ----- | ---------- | --------------------------- |
| 0     | `tm_year`  | 0000, ...., 2018, ..., 9999 |
| 1     | `tm_mon`   | 1, 2, ..., 12               |
| 2     | `tm_mday`  | 1, 2, ..., 31               |
| 3     | `tm_hour`  | 0, 1, ..., 23               |
| 4     | `tm_min`   | 0, 1, ..., 59               |
| 5     | `tm_sec`   | 0, 1, ..., 61               |
| 6     | `tm_wday`  | 0, 1, ..., 6; Monday is 0   |
| 7     | `tm_yday`  | 1, 2, ..., 366              |
| 8     | `tm_isdst` | 0, 1 or -1                  |

The values (elements) of the `time.struct_time` object are accessible using both indices and attributes.

#### Python time.localtime()

The `localtime()` function takes the number of seconds passed since epoch as an argument and returns `struct_time` in **local time**.

```
import time

result = time.localtime(1545925769)
print("result:", result)
print("\nyear:", result.tm_year)
print("tm_hour:", result.tm_hour)
```

When you run the program, the output will be something like:

```
result: time.struct_time(tm_year=2018, tm_mon=12, tm_mday=27, tm_hour=15, tm_min=49, tm_sec=29, tm_wday=3, tm_yday=361, tm_isdst=0)

year: 2018
tm_hour: 15
```

If no argument or `None` is passed to `localtime()`, the value returned by `time()` is used.

#### Python time.gmtime()

The `gmtime()` function takes the number of seconds passed since epoch as an argument and returns `struct_time` in **UTC**.

```
import time

result = time.gmtime(1545925769)
print("result:", result)
print("\nyear:", result.tm_year)
print("tm_hour:", result.tm_hour)
```

When you run the program, the output will be:

```
result = time.struct_time(tm_year=2018, tm_mon=12, tm_mday=28, tm_hour=8, tm_min=44, tm_sec=4, tm_wday=4, tm_yday=362, tm_isdst=0)

year = 2018
tm_hour = 8
```

If no argument or `None` is passed to `gmtime()`, the value returned by `time()` is used.

#### Python time.mktime()

The `mktime()` function takes `struct_time` (or a tuple containing 9 elements corresponding to `struct_time`) as an argument and returns the seconds passed since epoch in local time. Basically, it's the inverse function of `localtime()`.

```
import time

t = (2018, 12, 28, 8, 44, 4, 4, 362, 0)

local_time = time.mktime(t)
print("Local time:", local_time)
```

The example below shows how `mktime()` and `localtime()` are related.

```
import time

seconds = 1545925769

# returns struct_time
t = time.localtime(seconds)
print("t1: ", t)

# returns seconds from struct_time
s = time.mktime(t)
print("\s:", seconds)
```

When you run the program, the output will be something like:

```
t1:  time.struct_time(tm_year=2018, tm_mon=12, tm_mday=27, tm_hour=15, tm_min=49, tm_sec=29, tm_wday=3, tm_yday=361, tm_isdst=0)

s: 1545925769.0
```

#### Python time.asctime()

The `asctime()` function takes `struct_time` (or a tuple containing 9 elements corresponding to `struct_time`) as an argument and returns a string representing it. Here's an example:

```
import time

t = (2018, 12, 28, 8, 44, 4, 4, 362, 0)

result = time.asctime(t)
print("Result:", result)
```

When you run the program, the output will be:

```
Result: Fri Dec 28 08:44:04 2018
```

#### Python time.strftime()

The `strftime()` function takes `struct_time` (or tuple corresponding to it) as an argument and returns a string representing it based on the format code used. For example,

```
import time

named_tuple = time.localtime() # get struct_time
time_string = time.strftime("%m/%d/%Y, %H:%M:%S", named_tuple)

print(time_string)
```

When you run the program, the output will be something like:

```
12/28/2018, 09:47:41
```

Here, `%Y`, `%m`, `%d`, `%H` etc. are format codes.

* `%Y` - year \[0001,..., 2018, 2019,..., 9999]
* `%m` - month \[01, 02, ..., 11, 12]
* `%d` - day \[01, 02, ..., 30, 31]
* `%H` - hour \[00, 01, ..., 22, 23
* `%M` - minutes \[00, 01, ..., 58, 59]
* `%S` - second \[00, 01, ..., 58, 61]

To learn more, visit: [time.strftime()](https://docs.python.org/3/library/time.html#time.strftime).

#### Python time.strptime()

The `strptime()` function parses a string representing time and returns `struct_time`.

```
import time

time_string = "21 June, 2018"
result = time.strptime(time_string, "%d %B, %Y")

print(result)
```

When you run the program, the output will be:

```
time.struct_time(tm_year=2018, tm_mon=6, tm_mday=21, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=172, tm_isdst=-1)
```

[Previous Tutorial:](https://www.programiz.com/python-programming/datetime/timestamp-datetime)


# Python sleep()

**The sleep() function suspends (waits) execution of the current thread for a given number of seconds.**

Python has a module named [time](https://www.programiz.com/python-programming/time) which provides several useful functions to handle time-related tasks. One of the popular functions among them is `sleep()`.

The `sleep()` function suspends execution of the current thread for a given number of seconds.

### Example 1: Python sleep()

```

import time

print("Printed immediately.")
time.sleep(2.4)
print("Printed after 2.4 seconds.")
```

Here's how this program works:

* `"Printed immediately"` is printed
* Suspends (Delays) execution for 2.4 seconds.
* `"Printed after 2.4 seconds"` is printed.

As you can see from the above example, `sleep()` takes a floating-point number as an argument.

**Before Python 3.5**, the actual suspension time may be less than the argument specified to the `time()` function.

**Since Python 3.5**, the suspension time will be at least the seconds specified.

### Example 2: Python create a digital clock

```
import time

while True:
  localtime = time.localtime()
  result = time.strftime("%I:%M:%S %p", localtime)
  print(result)
  time.sleep(1)
```

In the above program, we computed and printed the current local time inside the infinite [while loop](https://www.programiz.com/python-programming/while-loop). Then, the program waits for 1 second. Again, the current local time is computed and printed. This process goes on.

When you run the program, the output will be something like:

```
02:10:50 PM
02:10:51 PM
02:10:52 PM
02:10:53 PM
02:10:54 PM
... .. ...
```

Here is a slightly modified better version of the above program.

```

import time

while True:
  localtime = time.localtime()
  result = time.strftime("%I:%M:%S %p", localtime)
  print(result, end="", flush=True)
  print("\r", end="", flush=True)
  time.sleep(1)
```

To learn more, visit [digital clock in Python shell](https://stackoverflow.com/questions/37515587/run-a-basic-digital-clock-in-the-python-shell).

### Multithreading in Python

Before talking about `sleep()` in multithreaded programs, let's talk about processes and threads.

A computer program is a collection of instructions. A process is the execution of those instructions.

A thread is a subset of the process. A process can have one or more threads.

#### Example 3: Python multithreading

All the programs above in this article are single-threaded programs. Here's an example of a multithreaded Python program.

```

import threading 
  
def print_hello_three_times():
  for i in range(3):
    print("Hello")
  
def print_hi_three_times(): 
    for i in range(3): 
      print("Hi") 

t1 = threading.Thread(target=print_hello_three_times)  
t2 = threading.Thread(target=print_hi_three_times)  

t1.start()
t2.start()
```

When you run the program, the output will be something like:

```

Hello
Hello
Hi
Hello
Hi
Hi
```

The above program has two threads t1 and t2. These threads are run using `t1.start()` and `t2.start()` statements.

Note that, t1 and t2 run concurrently and you might get different output.

Visit this page to learn more about [Multithreading in Python](https://stackoverflow.com/questions/2846653/how-to-use-threading-in-python).

### time.sleep() in multithreaded programs

The `sleep()` function suspends execution of the current thread for a given number of seconds.

In case of single-threaded programs, `sleep()` suspends execution of the thread and process. However, the function suspends a thread rather than the whole process in multithreaded programs.

#### Example 4: sleep() in a multithreaded program

```
import threading 
import time
  
def print_hello():
  for i in range(4):
    time.sleep(0.5)
    print("Hello")
  
def print_hi(): 
    for i in range(4): 
      time.sleep(0.7)
      print("Hi") 

t1 = threading.Thread(target=print_hello)  
t2 = threading.Thread(target=print_hi)  
t1.start()
t2.start()
```

The above program has two threads. We have used `time.sleep(0.5)` and `time.sleep(0.75)` to suspend execution of these two threads for 0.5 seconds and 0.7 seconds respectively.


# Chương IX. CHỦ ĐỀ NÂNG CAO


# Python Iterators

**Iterators are objects that can be iterated upon. In this tutorial, you will learn how iterator works and how you can build your own iterator using \_\_iter\_\_ and \_\_next\_\_ methods.**

### Iterators in Python <a href="#what" id="what"></a>

Iterators are everywhere in Python. They are elegantly implemented within `for` loops, comprehensions, generators etc. but are hidden in plain sight.

Iterator in Python is simply an [object](https://www.programiz.com/python-programming/class) that can be iterated upon. An object which will return data, one element at a time.

Technically speaking, a Python **iterator object** must implement two special methods, `__iter__()` and `__next__()`, collectively called the **iterator protocol**.

An object is called **iterable** if we can get an iterator from it. Most built-in containers in Python like: [list](https://www.programiz.com/python-programming/list), [tuple](https://www.programiz.com/python-programming/tuple), [string](https://www.programiz.com/python-programming/string) etc. are iterables.

The `iter()` function (which in turn calls the `__iter__()` method) returns an iterator from them.

### Iterating Through an Iterator <a href="#iterating" id="iterating"></a>

We use the `next()` function to manually iterate through all the items of an iterator. When we reach the end and there is no more data to be returned, it will raise the `StopIteration` Exception. Following is an example.

```
# define a list
my_list = [4, 7, 0, 3]

# get an iterator using iter()
my_iter = iter(my_list)

# iterate through it using next()

# Output: 4
print(next(my_iter))

# Output: 7
print(next(my_iter))

# next(obj) is same as obj.__next__()

# Output: 0
print(my_iter.__next__())

# Output: 3
print(my_iter.__next__())

# This will raise error, no items left
next(my_iter)
```

**Output**

```
4
7
0
3
Traceback (most recent call last):
  File "<string>", line 24, in <module>
    next(my_iter)
StopIteration
```

A more elegant way of automatically iterating is by using the [for loop](https://www.programiz.com/python-programming/for-loop). Using this, we can iterate over any object that can return an iterator, for example list, string, file etc.

```
>>> for element in my_list:
...     print(element)
...     
4
7
0
3
```

### Working of for loop for Iterators <a href="#for-loop" id="for-loop"></a>

As we see in the above example, the `for` loop was able to iterate automatically through the list.

In fact the `for` loop can iterate over any iterable. Let's take a closer look at how the `for` loop is actually implemented in Python.

```
for element in iterable:
    # do something with element
```

Is actually implemented as.

```
# create an iterator object from that iterable
iter_obj = iter(iterable)

# infinite loop
while True:
    try:
        # get the next item
        element = next(iter_obj)
        # do something with element
    except StopIteration:
        # if StopIteration is raised, break from loop
        break
```

So internally, the `for` loop creates an iterator object, `iter_obj` by calling `iter()` on the iterable.

Ironically, this `for` loop is actually an infinite [while loop](https://www.programiz.com/python-programming/while-loop).

Inside the loop, it calls `next()` to get the next element and executes the body of the `for` loop with this value. After all the items exhaust, `StopIteration` is raised which is internally caught and the loop ends. Note that any other kind of exception will pass through.

### Building Custom Iterators <a href="#build-own" id="build-own"></a>

Building an iterator from scratch is easy in Python. We just have to implement the `__iter__()` and the `__next__()` methods.

The `__iter__()` method returns the iterator object itself. If required, some initialization can be performed.

The `__next__()` method must return the next item in the sequence. On reaching the end, and in subsequent calls, it must raise `StopIteration`.

Here, we show an example that will give us the next power of 2 in each iteration. Power exponent starts from zero up to a user set number.

If you do not have any idea about object-oriented programming, visit [Python Object-Oriented Programming](https://python/%20object%20oriented%20programming/).

```
class PowTwo:
    """Class to implement an iterator
    of powers of two"""

    def __init__(self, max=0):
        self.max = max

    def __iter__(self):
        self.n = 0
        return self

    def __next__(self):
        if self.n <= self.max:
            result = 2 ** self.n
            self.n += 1
            return result
        else:
            raise StopIteration


# create an object
numbers = PowTwo(3)

# create an iterable from the object
i = iter(numbers)

# Using next to get to the next iterator element
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
```

**Output**

```
1
2
4
8
Traceback (most recent call last):
  File "/home/bsoyuj/Desktop/Untitled-1.py", line 32, in <module>
    print(next(i))
  File "<string>", line 18, in __next__
    raise StopIteration
StopIteration
```

We can also use a `for` loop to iterate over our iterator class.

```
>>> for i in PowTwo(5):
...     print(i)
...     
1
2
4
8
16
32
```

### Python Infinite Iterators <a href="#infinite" id="infinite"></a>

It is not necessary that the item in an iterator object has to be exhausted. There can be infinite iterators (which never ends). We must be careful when handling such iterators.

Here is a simple example to demonstrate infinite iterators.

The [built-in function](https://www.programiz.com/python-programming/built-in-function) `iter()` function can be called with two arguments where the first argument must be a callable object (function) and second is the sentinel. The iterator calls this function until the returned value is equal to the sentinel.

```
>>> int()
0

>>> inf = iter(int,1)
>>> next(inf)
0
>>> next(inf)
0
```

We can see that the `int()` function always returns 0. So passing it as `iter(int,1)` will return an iterator that calls `int()` until the returned value equals 1. This never happens and we get an infinite iterator.

We can also build our own infinite iterators. The following iterator will, theoretically, return all the odd numbers.

```
class InfIter:
    """Infinite iterator to return all
        odd numbers"""

    def __iter__(self):
        self.num = 1
        return self

    def __next__(self):
        num = self.num
        self.num += 2
        return num
```

A sample run would be as follows.

```
>>> a = iter(InfIter())
>>> next(a)
1
>>> next(a)
3
>>> next(a)
5
>>> next(a)
7
```

And so on...

Be careful to include a terminating condition, when iterating over these types of infinite iterators.

The advantage of using iterators is that they save resources. Like shown above, we could get all the odd numbers without storing the entire number system in memory. We can have infinite items (theoretically) in finite memory.

There's an easier way to create iterators in Python. To learn more visit: [Python generators using yield](https://www.programiz.com/python-programming/generator).


# Python Generators

**In this tutorial, you'll learn how to create iterations easily using Python generators, how it is different from iterators and normal functions, and why you should use it.**

### Generators in Python <a href="#what" id="what"></a>

There is a lot of work in building an [iterator](https://www.programiz.com/python-programming/iterator) in Python. We have to implement a class with `__iter__()` and `__next__()` method, keep track of internal states, and raise `StopIteration` when there are no values to be returned.

This is both lengthy and counterintuitive. Generator comes to the rescue in such situations.

Python generators are a simple way of creating iterators. All the work we mentioned above are automatically handled by generators in Python.

Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).

### Create Generators in Python <a href="#create" id="create"></a>

It is fairly simple to create a generator in Python. It is as easy as defining a normal function, but with a `yield` statement instead of a `return` statement.

If a function contains at least one `yield` statement (it may contain other `yield` or `return` statements), it becomes a generator function. Both `yield` and `return` will return some value from a function.

The difference is that while a `return` statement terminates a function entirely, `yield` statement pauses the function saving all its states and later continues from there on successive calls.

### Differences between Generator function and Normal function <a href="#difference" id="difference"></a>

Here is how a generator function differs from a normal [function](https://www.programiz.com/python-programming/function).

* Generator function contains one or more `yield` statements.
* When called, it returns an object (iterator) but does not start execution immediately.
* Methods like `__iter__()` and `__next__()` are implemented automatically. So we can iterate through the items using `next()`.
* Once the function yields, the function is paused and the control is transferred to the caller.
* Local variables and their states are remembered between successive calls.
* Finally, when the function terminates, `StopIteration` is raised automatically on further calls.

Here is an example to illustrate all of the points stated above. We have a generator function named `my_gen()` with several `yield` statements.

```
# A simple generator function
def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n
```

An interactive run in the interpreter is given below. Run these in the Python shell to see the output.

```
>>> # It returns an object but does not start execution immediately.
>>> a = my_gen()

>>> # We can iterate through the items using next().
>>> next(a)
This is printed first
1
>>> # Once the function yields, the function is paused and the control is transferred to the caller.

>>> # Local variables and theirs states are remembered between successive calls.
>>> next(a)
This is printed second
2

>>> next(a)
This is printed at last
3

>>> # Finally, when the function terminates, StopIteration is raised automatically on further calls.
>>> next(a)
Traceback (most recent call last):
...
StopIteration
>>> next(a)
Traceback (most recent call last):
...
StopIteration
```

One interesting thing to note in the above example is that the value of variable n is remembered between each call.

Unlike normal functions, the local variables are not destroyed when the function yields. Furthermore, the generator object can be iterated only once.

To restart the process we need to create another generator object using something like `a = my_gen()`.

One final thing to note is that we can use generators with [for loops](https://www.programiz.com/python-programming/for-loop) directly.

This is because a `for` loop takes an iterator and iterates over it using `next()` function. It automatically ends when `StopIteration` is raised. Check here to [know how a for loop is actually implemented in Python](https://www.programiz.com/python-programming/iterator#for-loop-working).

```
# A simple generator function
def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n


# Using for loop
for item in my_gen():
    print(item)
```

When you run the program, the output will be:

```
This is printed first
1
This is printed second
2
This is printed at last
3
```

### Python Generators with a Loop <a href="#with-loop" id="with-loop"></a>

The above example is of less use and we studied it just to get an idea of what was happening in the background.

Normally, generator functions are implemented with a loop having a suitable terminating condition.

Let's take an example of a generator that reverses a string.

```
def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1, -1, -1):
        yield my_str[i]


# For loop to reverse the string
for char in rev_str("hello"):
    print(char)
```

**Output**

```
o
l
l
e
h
```

In this example, we have used the `range()` function to get the index in reverse order using the for loop.

**Note**: This generator function not only works with strings, but also with other kinds of iterables like [list](https://www.programiz.com/python-programming/list), [tuple](https://www.programiz.com/python-programming/tuple), etc.

### Python Generator Expression <a href="#expression" id="expression"></a>

Simple generators can be easily created on the fly using generator expressions. It makes building generators easy.

Similar to the lambda functions which create [anonymous functions](https://www.programiz.com/python-programming/anonymous-function), generator expressions create anonymous generator functions.

The syntax for generator expression is similar to that of a [list comprehension in Python](https://www.programiz.com/python-programming/list#list-comprehension). But the square brackets are replaced with round parentheses.

The major difference between a list comprehension and a generator expression is that a list comprehension produces the entire list while the generator expression produces one item at a time.

They have lazy execution ( producing items only when asked for ). For this reason, a generator expression is much more memory efficient than an equivalent list comprehension.

```
# Initialize the list
my_list = [1, 3, 6, 10]

# square each term using list comprehension
list_ = [x**2 for x in my_list]

# same thing can be done using a generator expression
# generator expressions are surrounded by parenthesis ()
generator = (x**2 for x in my_list)

print(list_)
print(generator)
```

**Output**

```
[1, 9, 36, 100]
<generator object <genexpr> at 0x7f5d4eb4bf50>
```

We can see above that the generator expression did not produce the required result immediately. Instead, it returned a generator object, which produces items only on demand.

Here is how we can start getting items from the generator:

```
# Initialize the list
my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)
print(next(a))

print(next(a))

print(next(a))

print(next(a))

next(a)
```

When we run the above program, we get the following output:

```
1
9
36
100
Traceback (most recent call last):
  File "<string>", line 15, in <module>
StopIteration
```

Generator expressions can be used as function arguments. When used in such a way, the round parentheses can be dropped.

```
>>> sum(x**2 for x in my_list)
146

>>> max(x**2 for x in my_list)
100
```

### Use of Python Generators <a href="#use" id="use"></a>

There are several reasons that make generators a powerful implementation.

#### 1. Easy to Implement

Generators can be implemented in a clear and concise way as compared to their iterator class counterpart. Following is an example to implement a sequence of power of 2 using an iterator class.

```
class PowTwo:
    def __init__(self, max=0):
        self.n = 0
        self.max = max

    def __iter__(self):
        return self

    def __next__(self):
        if self.n > self.max:
            raise StopIteration

        result = 2 ** self.n
        self.n += 1
        return result
```

The above program was lengthy and confusing. Now, let's do the same using a generator function.

```
def PowTwoGen(max=0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1
```

Since generators keep track of details automatically, the implementation was concise and much cleaner.

#### 2. Memory Efficient

A normal function to return a sequence will create the entire sequence in memory before returning the result. This is an overkill, if the number of items in the sequence is very large.

Generator implementation of such sequences is memory friendly and is preferred since it only produces one item at a time.

#### 3. Represent Infinite Stream

Generators are excellent mediums to represent an infinite stream of data. Infinite streams cannot be stored in memory, and since generators produce only one item at a time, they can represent an infinite stream of data.

The following generator function can generate all the even numbers (at least in theory).

```
def all_even():
    n = 0
    while True:
        yield n
        n += 2
```

#### 4. Pipelining Generators

Multiple generators can be used to pipeline a series of operations. This is best illustrated using an example.

Suppose we have a generator that produces the numbers in the Fibonacci series. And we have another generator for squaring numbers.

If we want to find out the sum of squares of numbers in the Fibonacci series, we can do it in the following way by pipelining the output of generator functions together.

```
def fibonacci_numbers(nums):
    x, y = 0, 1
    for _ in range(nums):
        x, y = y, x+y
        yield x

def square(nums):
    for num in nums:
        yield num**2

print(sum(square(fibonacci_numbers(10))))
```

**Output**

```
4895
```

This pipelining is efficient and easy to read (and yes, a lot cooler!).


# Python Closures

**In this tutorial, you'll learn about Python closure, how to define a closure, and the reasons you should use it.**

### Nonlocal variable in a nested function <a href="#nonlocal" id="nonlocal"></a>

Before getting into what a closure is, we have to first understand what a nested function and nonlocal variable is.

A function defined inside another function is called a nested function. Nested functions can access variables of the enclosing scope.

In Python, these non-local variables are read-only by default and we must declare them explicitly as non-local (using [nonlocal keyword](https://www.programiz.com/python-programming/keyword-list#nonlocal)) in order to modify them.

Following is an example of a nested function accessing a non-local variable.

```
def print_msg(msg):
    # This is the outer enclosing function

    def printer():
        # This is the nested function
        print(msg)

    printer()

# We execute the function
# Output: Hello
print_msg("Hello")
```

**Output**

```
Hello
```

We can see that the nested `printer()` function was able to access the non-local msg variable of the enclosing function.

### Defining a Closure Function <a href="#define" id="define"></a>

In the example above, what would happen if the last line of the function `print_msg()` returned the `printer()` function instead of calling it? This means the function was defined as follows:

```
def print_msg(msg):
    # This is the outer enclosing function

    def printer():
        # This is the nested function
        print(msg)

    return printer  # returns the nested function


# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()
```

**Output**

```
Hello
```

That's unusual.

The `print_msg()` function was called with the string `"Hello"` and the returned function was bound to the name another. On calling `another()`, the message was still remembered although we had already finished executing the `print_msg()` function.

This technique by which some data (`"Hello` in this case) gets attached to the code is called **closure in Python**.

This value in the enclosing scope is remembered even when the variable goes out of scope or the function itself is removed from the current namespace.

Try running the following in the Python shell to see the output.

```
>>> del print_msg
>>> another()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined
```

Here, the returned function still works even when the original function was deleted.

### When do we have closures? <a href="#when" id="when"></a>

As seen from the above example, we have a closure in Python when a nested function references a value in its enclosing scope.

The criteria that must be met to create closure in Python are summarized in the following points.

* We must have a nested function (function inside a function).
* The nested function must refer to a value defined in the enclosing function.
* The enclosing function must return the nested function.

### When to use closures? <a href="#use" id="use"></a>

So what are closures good for?

Closures can avoid the use of global values and provides some form of data hiding. It can also provide an object oriented solution to the problem.

When there are few methods (one method in most cases) to be implemented in a class, closures can provide an alternate and more elegant solution. But when the number of attributes and methods get larger, it's better to implement a class.

Here is a simple example where a closure might be more preferable than defining a class and making objects. But the preference is all yours.

```
def make_multiplier_of(n):
    def multiplier(x):
        return x * n
    return multiplier


# Multiplier of 3
times3 = make_multiplier_of(3)

# Multiplier of 5
times5 = make_multiplier_of(5)

# Output: 27
print(times3(9))

# Output: 15
print(times5(3))

# Output: 30
print(times5(times3(2)))
```

**Output**

```
27
15
30
```

[Python Decorators](https://www.programiz.com/python-programming/decorator) make an extensive use of closures as well.

On a concluding note, it is good to point out that the values that get enclosed in the closure function can be found out.

All function objects have a `__closure__` attribute that returns a tuple of cell objects if it is a closure function. Referring to the example above, we know `times3` and `times5` are closure functions.

```
>>> make_multiplier_of.__closure__
>>> times3.__closure__
(<cell at 0x0000000002D155B8: int object at 0x000000001E39B6E0>,)
```

The cell object has the attribute cell\_contents which stores the closed value.

```
>>> times3.__closure__[0].cell_contents
3
>>> times5.__closure__[0].cell_contents
5
```


# Python Decorators

**A decorator takes in a function, adds some functionality and returns it. In this tutorial, you will learn how you can create a decorator and why you should use it.**

### Decorators in Python <a href="#what" id="what"></a>

Python has an interesting feature called **decorators** to add functionality to an existing code.

This is also called **metaprogramming** because a part of the program tries to modify another part of the program at compile time.

### Prerequisites for learning decorators <a href="#pre" id="pre"></a>

In order to understand about decorators, we must first know a few basic things in Python.

We must be comfortable with the fact that everything in Python (Yes! Even classes), are [objects](https://www.programiz.com/python-programming/class). Names that we define are simply identifiers bound to these objects. [Functions](https://www.programiz.com/python-programming/function) are no exceptions, they are objects too (with attributes). Various different names can be bound to the same function object.

Here is an example.

```
def first(msg):
    print(msg)


first("Hello")

second = first
second("Hello")
```

**Output**

```
Hello
Hello
```

When you run the code, both functions `first` and `second` give the same output. Here, the names `first` and `second` refer to the same function object.

Now things start getting weirder.

Functions can be passed as arguments to another function.

If you have used functions like `map`, `filter` and `reduce` in Python, then you already know about this.

Such functions that take other functions as arguments are also called **higher order functions**. Here is an example of such a function.

```
def inc(x):
    return x + 1


def dec(x):
    return x - 1


def operate(func, x):
    result = func(x)
    return result
```

We invoke the function as follows.

```
>>> operate(inc,3)
4
>>> operate(dec,3)
2
```

Furthermore, a function can return another function.

```
def is_called():
    def is_returned():
        print("Hello")
    return is_returned


new = is_called()

# Outputs "Hello"
new()
```

**Output**

```
Hello
```

Here, `is_returned()` is a nested function which is defined and returned each time we call `is_called()`.

Finally, we must know about C[losures in Python](https://www.programiz.com/python-programming/closure).

### Getting back to Decorators <a href="#get" id="get"></a>

Functions and methods are called **callable** as they can be called.

In fact, any object which implements the special `__call__()` method is termed callable. So, in the most basic sense, a decorator is a callable that returns a callable.

Basically, a decorator takes in a function, adds some functionality and returns it.

```
def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner


def ordinary():
    print("I am ordinary")
```

When you run the following codes in shell,

```
>>> ordinary()
I am ordinary

>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary
```

In the example shown above, `make_pretty()` is a decorator. In the assignment step:

```
pretty = make_pretty(ordinary)
```

The function `ordinary()` got decorated and the returned function was given the name `pretty`.

We can see that the decorator function added some new functionality to the original function. This is similar to packing a gift. The decorator acts as a wrapper. The nature of the object that got decorated (actual gift inside) does not alter. But now, it looks pretty (since it got decorated).

Generally, we decorate a function and reassign it as,

```
ordinary = make_pretty(ordinary).
```

This is a common construct and for this reason, Python has a syntax to simplify this.

We can use the `@` symbol along with the name of the decorator function and place it above the definition of the function to be decorated. For example,

```
@make_pretty
def ordinary():
    print("I am ordinary")
```

is equivalent to

```
def ordinary():
    print("I am ordinary")
ordinary = make_pretty(ordinary)
```

This is just a syntactic sugar to implement decorators.

### Decorating Functions with Parameters <a href="#decorating" id="decorating"></a>

The above decorator was simple and it only worked with functions that did not have any parameters. What if we had functions that took in parameters like:

```
def divide(a, b):
    return a/b
```

This function has two parameters, a and b. We know it will give an error if we pass in b as 0.

```
>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
```

Now let's make a decorator to check for this case that will cause the error.

```
def smart_divide(func):
    def inner(a, b):
        print("I am going to divide", a, "and", b)
        if b == 0:
            print("Whoops! cannot divide")
            return

        return func(a, b)
    return inner


@smart_divide
def divide(a, b):
    print(a/b)
```

This new implementation will return `None` if the error condition arises.

```
>>> divide(2,5)
I am going to divide 2 and 5
0.4

>>> divide(2,0)
I am going to divide 2 and 0
Whoops! cannot divide
```

In this manner, we can decorate functions that take parameters.

A keen observer will notice that parameters of the nested `inner()` function inside the decorator is the same as the parameters of functions it decorates. Taking this into account, now we can make general decorators that work with any number of parameters.

In Python, this magic is done as `function(*args, **kwargs)`. In this way, `args` will be the [tuple](https://www.programiz.com/python-programming/tuple) of positional arguments and `kwargs` will be the [dictionary](https://www.programiz.com/python-programming/dictionary) of keyword arguments. An example of such a decorator will be:

```
def works_for_all(func):
    def inner(*args, **kwargs):
        print("I can decorate any function")
        return func(*args, **kwargs)
    return inner
```

### Chaining Decorators in Python <a href="#chaining" id="chaining"></a>

Multiple decorators can be chained in Python.

This is to say, a function can be decorated multiple times with different (or same) decorators. We simply place the decorators above the desired function.

```
def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner


def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner


@star
@percent
def printer(msg):
    print(msg)


printer("Hello")
```

**Output**

```
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
```

The above syntax of,

```
@star
@percent
def printer(msg):
    print(msg)
```

is equivalent to

```
def printer(msg):
    print(msg)
printer = star(percent(printer))
```

The order in which we chain decorators matter. If we had reversed the order as,

```
@percent
@star
def printer(msg):
    print(msg)
```

The output would be:

```
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
Hello
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
```


# Python @property decorator

**In this tutorial, you will learn about Python @property decorator; a pythonic way to use getters and setters in object-oriented programming.**

Python programming provides us with a built-in `@property` decorator which makes usage of getter and setters much easier in Object-Oriented Programming.

Before going into details on what `@property` decorator is, let us first build an intuition on why it would be needed in the first place.

### Class Without Getters and Setters <a href="#without" id="without"></a>

Let us assume that we decide to make a [class](https://www.programiz.com/python-programming/class) that stores the temperature in degrees Celsius. It would also implement a method to convert the temperature into degrees Fahrenheit. One way of doing this is as follows:

```
class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
```

We can make objects out of this class and manipulate the `temperature` attribute as we wish:

```
# Basic method of setting and getting attributes in Python
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32


# Create a new object
human = Celsius()

# Set the temperature
human.temperature = 37

# Get the temperature attribute
print(human.temperature)

# Get the to_fahrenheit method
print(human.to_fahrenheit())
```

**Output**

```
37
98.60000000000001
```

The extra decimal places when converting into Fahrenheit is due to the floating point arithmetic error. To learn more, visit [Python Floating Point Arithmetic Error](https://www.programiz.com/python-programming/numbers#dec).

Whenever we assign or retrieve any object attribute like `temperature` as shown above, Python searches it in the object's built-in `__dict__` dictionary attribute.

```
>>> human.__dict__
{'temperature': 37}
```

Therefore, `man.temperature` internally becomes `man.__dict__['temperature']`.

### Using Getters and Setters <a href="#using" id="using"></a>

Suppose we want to extend the usability of the Celsius class defined above. We know that the temperature of any object cannot reach below -273.15 degrees Celsius (Absolute Zero in Thermodynamics)

Let's update our code to implement this value constraint.

An obvious solution to the above restriction will be to hide the attribute `temperature` (make it private) and define new getter and setter methods to manipulate it. This can be done as follows:

```
# Making Getters and Setter methods
class Celsius:
    def __init__(self, temperature=0):
        self.set_temperature(temperature)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    # getter method
    def get_temperature(self):
        return self._temperature

    # setter method
    def set_temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible.")
        self._temperature = value
```

As we can see, the above method introduces two new `get_temperature()` and `set_temperature()` methods.

Furthermore, `temperature` was replaced with `_temperature`. An underscore `_` at the beginning is used to denote private variables in Python.

Now, let's use this implementation:

```
# Making Getters and Setter methods
class Celsius:
    def __init__(self, temperature=0):
        self.set_temperature(temperature)

    def to_fahrenheit(self):
        return (self.get_temperature() * 1.8) + 32

    # getter method
    def get_temperature(self):
        return self._temperature

    # setter method
    def set_temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible.")
        self._temperature = value


# Create a new object, set_temperature() internally called by __init__
human = Celsius(37)

# Get the temperature attribute via a getter
print(human.get_temperature())

# Get the to_fahrenheit method, get_temperature() called by the method itself
print(human.to_fahrenheit())

# new constraint implementation
human.set_temperature(-300)

# Get the to_fahreheit method
print(human.to_fahrenheit())
```

**Output**

```
37
98.60000000000001
Traceback (most recent call last):
  File "<string>", line 30, in <module>
  File "<string>", line 16, in set_temperature
ValueError: Temperature below -273.15 is not possible.
```

This update successfully implemented the new restriction. We are no longer allowed to set the temperature below -273.15 degrees Celsius.

**Note**: The private variables don't actually exist in Python. There are simply norms to be followed. The language itself doesn't apply any restrictions.

```
>>> human._temperature = -300
>>> human.get_temperature()
-300
```

However, the bigger problem with the above update is that all the programs that implemented our previous class have to modify their code from `obj.temperature` to `obj.get_temperature()` and all expressions like `obj.temperature = val` to `obj.set_temperature(val)`.

This refactoring can cause problems while dealing with hundreds of thousands of lines of codes.

All in all, our new update was not backwards compatible. This is where `@property` comes to rescue.

### The property Class <a href="#property-class" id="property-class"></a>

A pythonic way to deal with the above problem is to use the `property` class. Here is how we can update our code:

```
# using property class
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    # getter
    def get_temperature(self):
        print("Getting value...")
        return self._temperature

    # setter
    def set_temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible")
        self._temperature = value

    # creating a property object
    temperature = property(get_temperature, set_temperature)
```

We added a `print()` function inside `get_temperature()` and `set_temperature()` to clearly observe that they are being executed.

The last line of the code makes a property object `temperature`. Simply put, property attaches some code (`get_temperature` and `set_temperature`) to the member attribute accesses (`temperature`).

Let's use this update code:

```
# using property class
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    # getter
    def get_temperature(self):
        print("Getting value...")
        return self._temperature

    # setter
    def set_temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273.15 is not possible")
        self._temperature = value

    # creating a property object
    temperature = property(get_temperature, set_temperature)


human = Celsius(37)

print(human.temperature)

print(human.to_fahrenheit())

human.temperature = -300
```

**Output**

```
Setting value...
Getting value...
37
Getting value...
98.60000000000001
Setting value...
Traceback (most recent call last):
  File "<string>", line 31, in <module>
  File "<string>", line 18, in set_temperature
ValueError: Temperature below -273 is not possible
```

As we can see, any code that retrieves the value of `temperature` will automatically call `get_temperature()` instead of a dictionary (\_\_dict\_\_) look-up. Similarly, any code that assigns a value to `temperature` will automatically call `set_temperature()`.

We can even see above that `set_temperature()` was called even when we created an object.

```
>>> human = Celsius(37)
Setting value...
```

**Can you guess why?**

The reason is that when an object is created, the `__init__()` method gets called. This method has the line `self.temperature = temperature`. This expression automatically calls `set_temperature()`.

Similarly, any access like `c.temperature` automatically calls `get_temperature()`. This is what property does. Here are a few more examples.

```
>>> human.temperature
Getting value
37
>>> human.temperature = 37
Setting value

>>> c.to_fahrenheit()
Getting value
98.60000000000001
```

By using `property`, we can see that no modification is required in the implementation of the value constraint. Thus, our implementation is backward compatible.

**Note**: The actual temperature value is stored in the private `_temperature` variable. The `temperature` attribute is a property object which provides an interface to this private variable.

### The @property Decorator <a href="#decorator" id="decorator"></a>

In Python, `property()` is a built-in function that creates and returns a `property` object. The syntax of this function is:

```
property(fget=None, fset=None, fdel=None, doc=None)
```

where,

* `fget` is function to get value of the attribute
* `fset` is function to set value of the attribute
* `fdel` is function to delete the attribute
* `doc` is a string (like a comment)

As seen from the implementation, these function arguments are optional. So, a property object can simply be created as follows.

```
>>> property()
<property object at 0x0000000003239B38>
```

A property object has three methods, `getter()`, `setter()`, and `deleter()` to specify `fget`, `fset` and `fdel` at a later point. This means, the line:

```
temperature = property(get_temperature,set_temperature)
```

can be broken down as:

```
# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)
```

These two pieces of codes are equivalent.

Programmers familiar with [Python Decorators](https://www.programiz.com/python-programming/decorator) can recognize that the above construct can be implemented as decorators.

We can even not define the names `get_temperature` and `set_temperature` as they are unnecessary and pollute the class namespace.

For this, we reuse the `temperature` name while defining our getter and setter functions. Let's look at how to implement this as a decorator:

```
# Using @property decorator
class Celsius:
    def __init__(self, temperature=0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value...")
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature below -273 is not possible")
        self._temperature = value


# create an object
human = Celsius(37)

print(human.temperature)

print(human.to_fahrenheit())

coldest_thing = Celsius(-300)
```

**Output**

```
Setting value...
Getting value...
37
Getting value...
98.60000000000001
Setting value...
Traceback (most recent call last):
  File "<string>", line 29, in <module>
  File "<string>", line 4, in __init__
  File "<string>", line 18, in temperature
ValueError: Temperature below -273 is not possible
```

The above implementation is simple and efficient. It is the recommended way to use `property`.


# Python RegEx

**In this tutorial, you will learn about regular expressions (RegEx), and use Python's re module to work with RegEx (with the help of examples).**

A **Re**gular **Ex**pression (RegEx) is a sequence of characters that defines a search pattern. For example,

```
^a...s$
```

The above code defines a RegEx pattern. The pattern is: **any five letter string starting with a and ending with s**.

A pattern defined using RegEx can be used to match against a string.

| Expression  | String   | Matched? |
| ----------- | -------- | -------- |
| `^a...s$`   | `abs`    | No match |
| `alias`     | Match    |          |
| `abyss`     | Match    |          |
| `Alias`     | No match |          |
| `An abacus` | No match |          |

Python has a module named `re` to work with RegEx. Here's an example:

```
import re

pattern = '^a...s$'
test_string = 'abyss'
result = re.match(pattern, test_string)

if result:
  print("Search successful.")
else:
  print("Search unsuccessful.")	
```

Here, we used `re.match()` function to search pattern within the test\_string. The method returns a match object if the search is successful. If not, it returns `None`.

There are other several functions defined in the re module to work with RegEx. Before we explore that, let's learn about regular expressions themselves.

If you already know the basics of RegEx, jump to [Python RegEx](https://www.programiz.com/python-programming/regex#python-regex).

### Specify Pattern Using RegEx

To specify regular expressions, metacharacters are used. In the above example, `^` and `$` are metacharacters.

#### MetaCharacters

Metacharacters are characters that are interpreted in a special way by a RegEx engine. Here's a list of metacharacters:

**\[]** **.** **^** **$** **\*** **+** **?** **{}** **()** **\\** **|**

**`[]` - Square brackets**

Square brackets specifies a set of characters you wish to match.

| Expression  | String    | Matched? |
| ----------- | --------- | -------- |
| `[abc]`     | `a`       | 1 match  |
| `ac`        | 2 matches |          |
| `Hey Jude`  | No match  |          |
| `abc de ca` | 5 matches |          |

Here, `[abc]` will match if the string you are trying to match contains any of the `a`, `b` or `c`.

You can also specify a range of characters using `-` inside square brackets.

* `[a-e]` is the same as `[abcde]`.
* `[1-4]` is the same as `[1234]`.
* `[0-39]` is the same as `[01239]`.

You can complement (invert) the character set by using caret `^` symbol at the start of a square-bracket.

* `[^abc]` means any character except a or b or c.
* `[^0-9]` means any non-digit character.

`.` - **Period**

A period matches any single character (except newline `'\n'`).

| Expression | String                            | Matched? |
| ---------- | --------------------------------- | -------- |
| `..`       | `a`                               | No match |
| `ac`       | 1 match                           |          |
| `acd`      | 1 match                           |          |
| `acde`     | 2 matches (contains 4 characters) |          |

`^` - **Caret**

The caret symbol `^` is used to check if a string **starts with** a certain character.

| Expression | String                                             | Matched? |
| ---------- | -------------------------------------------------- | -------- |
| `^a`       | `a`                                                | 1 match  |
| `abc`      | 1 match                                            |          |
| `bac`      | No match                                           |          |
| `^ab`      | `abc`                                              | 1 match  |
| `acb`      | No match (starts with `a` but not followed by `b`) |          |

`$` - **Dollar**

The dollar symbol `$` is used to check if a string **ends with** a certain character.

| Expression | String   | Matched? |
| ---------- | -------- | -------- |
| `a$`       | `a`      | 1 match  |
| `formula`  | 1 match  |          |
| `cab`      | No match |          |

`*` - **Star**

The star symbol `*` matches **zero or more occurrences** of the pattern left to it.

| Expression | String                                | Matched? |
| ---------- | ------------------------------------- | -------- |
| `ma*n`     | `mn`                                  | 1 match  |
| `man`      | 1 match                               |          |
| `maaan`    | 1 match                               |          |
| `main`     | No match (`a` is not followed by `n`) |          |
| `woman`    | 1 match                               |          |

`+` - **Plus**

The plus symbol `+` matches **one or more occurrences** of the pattern left to it.

| Expression | String                            | Matched?                    |
| ---------- | --------------------------------- | --------------------------- |
| `ma+n`     | `mn`                              | No match (no `a` character) |
| `man`      | 1 match                           |                             |
| `maaan`    | 1 match                           |                             |
| `main`     | No match (a is not followed by n) |                             |
| `woman`    | 1 match                           |                             |

`?` - **Question Mark**

The question mark symbol `?` matches **zero or one occurrence** of the pattern left to it.

| Expression | String                                 | Matched? |
| ---------- | -------------------------------------- | -------- |
| `ma?n`     | `mn`                                   | 1 match  |
| `man`      | 1 match                                |          |
| `maaan`    | No match (more than one `a` character) |          |
| `main`     | No match (a is not followed by n)      |          |
| `woman`    | 1 match                                |          |

`{}` - **Braces**

Consider this code: `{n,m}`. This means at least n, and at most m repetitions of the pattern left to it.

| Expression    | String                             | Matched? |
| ------------- | ---------------------------------- | -------- |
| `a{2,3}`      | `abc dat`                          | No match |
| `abc daat`    | 1 match (at `daat`)                |          |
| `aabc daaat`  | 2 matches (at `aabc` and `daaat`)  |          |
| `aabc daaaat` | 2 matches (at `aabc` and `daaaat`) |          |

Let's try one more example. This RegEx `[0-9]{2, 4}` matches at least 2 digits but not more than 4 digits

| Expression      | String                         | Matched?                       |
| --------------- | ------------------------------ | ------------------------------ |
| `[0-9]{2,4}`    | `ab123csde`                    | 1 match (match at `ab123csde`) |
| `12 and 345673` | 3 matches (`12`, `3456`, `73`) |                                |
| `1 and 2`       | No match                       |                                |

`|` - **Alternation**

Vertical bar `|` is used for alternation (`or` operator).

| Expression | String                   | Matched? |
| ---------- | ------------------------ | -------- |
| `a\|b`     | `cde`                    | No match |
| `ade`      | 1 match (match at `ade`) |          |
| `acdbea`   | 3 matches (at `acdbea`)  |          |

Here, `a|b` match any string that contains either a or b

`()` - **Group**

Parentheses `()` is used to group sub-patterns. For example, `(a|b|c)xz` match any string that matches either a or b or c followed by xz

| Expression    | String                       | Matched? |
| ------------- | ---------------------------- | -------- |
| `(a\|b\|c)xz` | `ab xz`                      | No match |
| `abxz`        | 1 match (match at `abxz`)    |          |
| `axz cabxz`   | 2 matches (at `axzbc cabxz`) |          |

`\` - **Backslash**

Backlash `\` is used to escape various characters including all metacharacters. For example,

`\$a` match if a string contains `$` followed by `a`. Here, `$` is not interpreted by a RegEx engine in a special way.

If you are unsure if a character has special meaning or not, you can put `\` in front of it. This makes sure the character is not treated in a special way.

**Special Sequences**

Special sequences make commonly used patterns easier to write. Here's a list of special sequences:

`\A` - Matches if the specified characters are at the start of a string.

| Expression   | String    | Matched? |
| ------------ | --------- | -------- |
| `\Athe`      | `the sun` | Match    |
| `In the sun` | No match  |          |

`\b` - Matches if the specified characters are at the beginning or end of a word.

| Expression      | String     | Matched? |
| --------------- | ---------- | -------- |
| `\bfoo`         | `football` | Match    |
| `a football`    | Match      |          |
| `afootball`     | No match   |          |
| `foo\b`         | `the foo`  | Match    |
| `the afoo test` | Match      |          |
| `the afootest`  | No match   |          |

`\B` - Opposite of `\b`. Matches if the specified characters are **not** at the beginning or end of a word.

| Expression      | String     | Matched? |
| --------------- | ---------- | -------- |
| `\Bfoo`         | `football` | No match |
| `a football`    | No match   |          |
| `afootball`     | Match      |          |
| `foo\B`         | `the foo`  | No match |
| `the afoo test` | No match   |          |
| `the afootest`  | Match      |          |

`\d` - Matches any decimal digit. Equivalent to `[0-9]`

| Expression | String   | Matched?                |
| ---------- | -------- | ----------------------- |
| `\d`       | `12abc3` | 3 matches (at `12abc3`) |
| `Python`   | No match |                         |

`\D` - Matches any non-decimal digit. Equivalent to `[^0-9]`

| Expression | String     | Matched?                  |
| ---------- | ---------- | ------------------------- |
| `\D`       | `1ab34"50` | 3 matches (at `1ab34"50`) |
| `1345`     | No match   |                           |

`\s` - Matches where a string contains any whitespace character. Equivalent to `[ \t\n\r\f\v]`.

| Expression    | String         | Matched? |
| ------------- | -------------- | -------- |
| `\s`          | `Python RegEx` | 1 match  |
| `PythonRegEx` | No match       |          |

`\S` - Matches where a string contains any non-whitespace character. Equivalent to `[^ \t\n\r\f\v]`.

| Expression | String   | Matched?             |
| ---------- | -------- | -------------------- |
| `\S`       | `a b`    | 2 matches (at `a b`) |
|            | No match |                      |

`\w` - Matches any alphanumeric character (digits and alphabets). Equivalent to `[a-zA-Z0-9_]`. By the way, underscore `_` is also considered an alphanumeric character.

| Expression | String     | Matched?                  |
| ---------- | ---------- | ------------------------- |
| `\w`       | `12&": ;c` | 3 matches (at `12&": ;c`) |
| `%"> !`    | No match   |                           |

`\W` - Matches any non-alphanumeric character. Equivalent to `[^a-zA-Z0-9_]`

| Expression | String   | Matched?             |
| ---------- | -------- | -------------------- |
| `\W`       | `1a2%c`  | 1 match (at `1a2%c`) |
| `Python`   | No match |                      |

`\Z` - Matches if the specified characters are at the end of a string.

| Expression                  | String          | Matched? |
| --------------------------- | --------------- | -------- |
| `Python\Z`                  | `I like Python` | 1 match  |
| `I like Python Programming` | No match        |          |
| `Python is fun.`            | No match        |          |

**Tip:** To build and test regular expressions, you can use RegEx tester tools such as [regex101](https://regex101.com/). This tool not only helps you in creating regular expressions, but it also helps you learn it.

Now you understand the basics of RegEx, let's discuss how to use RegEx in your Python code.

### Python RegEx

Python has a module named `re` to work with regular expressions. To use it, we need to import the module.

```
import re
```

The module defines several functions and constants to work with RegEx.

### re.findall()

The `re.findall()` method returns a list of strings containing all matches.

#### Example 1: re.findall()

```

# Program to extract numbers from a string

import re

string = 'hello 12 hi 89. Howdy 34'
pattern = '\d+'

result = re.findall(pattern, string) 
print(result)

# Output: ['12', '89', '34']
```

If the pattern is not found, `re.findall()` returns an empty list.

### re.split()

The `re.split` method splits the string where there is a match and returns a list of strings where the splits have occurred.

#### Example 2: re.split()

```

import re

string = 'Twelve:12 Eighty nine:89.'
pattern = '\d+'

result = re.split(pattern, string) 
print(result)

# Output: ['Twelve:', ' Eighty nine:', '.']
```

If the pattern is not found, `re.split()` returns a list containing the original string.

You can pass `maxsplit` argument to the `re.split()` method. It's the maximum number of splits that will occur.

```

import re

string = 'Twelve:12 Eighty nine:89 Nine:9.'
pattern = '\d+'

# maxsplit = 1
# split only at the first occurrence
result = re.split(pattern, string, 1) 
print(result)

# Output: ['Twelve:', ' Eighty nine:89 Nine:9.']
```

By the way, the default value of `maxsplit` is 0; meaning all possible splits.

### re.sub()

The syntax of `re.sub()` is:

```
re.sub(pattern, replace, string)
```

The method returns a string where matched occurrences are replaced with the content of replace variable.

#### Example 3: re.sub()

```

# Program to remove all whitespaces
import re

# multiline string
string = 'abc 12\
de 23 \n f45 6'

# matches all whitespace characters
pattern = '\s+'

# empty string
replace = ''

new_string = re.sub(pattern, replace, string) 
print(new_string)

# Output: abc12de23f456
```

If the pattern is not found, `re.sub()` returns the original string.

You can pass count as a fourth parameter to the `re.sub()` method. If omited, it results to 0. This will replace all occurrences.

```

import re

# multiline string
string = 'abc 12\
de 23 \n f45 6'

# matches all whitespace characters
pattern = '\s+'
replace = ''

new_string = re.sub(r'\s+', replace, string, 1) 
print(new_string)

# Output:
# abc12de 23
# f45 6
```

### re.subn()

The `re.subn()` is similar to `re.sub()` expect it returns a tuple of 2 items containing the new string and the number of substitutions made.

#### Example 4: re.subn()

```

# Program to remove all whitespaces
import re

# multiline string
string = 'abc 12\
de 23 \n f45 6'

# matches all whitespace characters
pattern = '\s+'

# empty string
replace = ''

new_string = re.subn(pattern, replace, string) 
print(new_string)

# Output: ('abc12de23f456', 4)
```

### re.search()

The `re.search()` method takes two arguments: a pattern and a string. The method looks for the first location where the RegEx pattern produces a match with the string.

If the search is successful, `re.search()` returns a match object; if not, it returns `None`.

```
match = re.search(pattern, str)
```

#### Example 5: re.search()

```

import re

string = "Python is fun"

# check if 'Python' is at the beginning
match = re.search('\APython', string)

if match:
  print("pattern found inside the string")
else:
  print("pattern not found")  

# Output: pattern found inside the string
```

Here, match contains a match object.

### Match object

You can get methods and attributes of a match object using [dir()](https://www.programiz.com/python-programming/methods/built-in/dir) function.

Some of the commonly used methods and attributes of match objects are:

#### match.group()

The `group()` method returns the part of the string where there is a match.

#### Example 6: Match object

```

import re

string = '39801 356, 2102 1111'

# Three digit number followed by space followed by two digit number
pattern = '(\d{3}) (\d{2})'

# match variable contains a Match object.
match = re.search(pattern, string) 

if match:
  print(match.group())
else:
  print("pattern not found")

# Output: 801 35
```

Here, match variable contains a match object.

Our pattern `(\d{3}) (\d{2})` has two subgroups `(\d{3})` and `(\d{2})`. You can get the part of the string of these parenthesized subgroups. Here's how:

```
>>> match.group(1)
'801'

>>> match.group(2)
'35'
>>> match.group(1, 2)
('801', '35')

>>> match.groups()
('801', '35')
```

#### match.start(), match.end() and match.span()

The `start()` function returns the index of the start of the matched substring. Similarly, `end()` returns the end index of the matched substring.

```
>>> match.start()
2
>>> match.end()
8
```

The `span()` function returns a tuple containing start and end index of the matched part.

```
>>> match.span()
(2, 8)
```

#### match.re and match.string

The `re` attribute of a matched object returns a regular expression object. Similarly, `string` attribute returns the passed string.

```
>>> match.re
re.compile('(\\d{3}) (\\d{2})')

>>> match.string
'39801 356, 2102 1111'
```

We have covered all commonly used methods defined in the `re` module. If you want to learn more, visit [Python 3 re module](https://docs.python.org/3/library/re.html).

#### Using r prefix before RegEx

When r or R prefix is used before a regular expression, it means raw string. For example, `'\n'` is a new line whereas `r'\n'` means two characters: a backslash `\` followed by `n`.

Backlash `\` is used to escape various characters including all metacharacters. However, using r prefix makes `\` treat as a normal character.

#### Example 7: Raw string using r prefix

```

import re

string = '\n and \r are escape sequences.'

result = re.findall(r'[\n\r]', string) 
print(result)

# Output: ['\n', '\r']
```

[Previous Tutorial:](https://www.programiz.com/python-programming/property)


# Python Examples

{% embed url="<https://www.programiz.com/python-programming/examples>" %}


# PHỤ LỤC - GHI CHÉP

Ghi chép trong quá trình vừa biên soạn,vừa thực hành


# Hàm map()

Hàm map() trong Python sẽ duyệt và thực hiện thao tác với từng phần tử của mảng (list, set, tuple) và trả về kết quả sau khi thao tác.&#x20;

map() có thể dùng thay vòng lặp for, while trong nhiều trường hợp. Xét các ví dụ sau

Ví dụ 1

Dùng for:

```python
def binhphuong(n):
    return n*n

a = (2, 5, 3, 100)
kq = []
for i in a:
    kq.append(binhphuong(i))

print(kq)
```

Dùng map()

```python
def binhphuong(n):
    return n*n

a = (2, 5, 3, 100)
kq = map(binhphuong, a)

print(list(kq))
```

Ví dụ 2. Dùng lambda trong map()

```python
a = (1, 2, 3, 4)
kq = map(lambda x: x*x, a)

kq = tuple(kq)
print(kq)
```

Ví dụ 3. Dùng map tính toán phần tử mảng

```python
num1 = [4, 5, 6]
num2 = [5, 6, 7]

cong = map(lambda n1, n2: n1+n2, num1, num2)
nhan = map(lambda n1, n2: n1*n2, num1, num2)

print(list(cong))
print(list(nhan))
```

Ví dụ 4. Chuyển đổi kiểu dữ liệu phần tử mảng và tính toán

```python
ch1 = ['4', '5', '6']
num2 = [5, 6, 7]

num1 = list(map(int, ch1))     # thao tác ép kiểu phần tử list

cong = map(lambda n1, n2: n1+n2, num1, num2)
print(list(cong))
```


# Cài Sublime Text để code Python

### Cài đặt để run Python

Mở SublimeText chọn từ menu:

`Tools -> Build System -> New Build System...`

Gõ đoạn code sau:

```
{
    "cmd": ["c:/Python/python.exe", "-u", "$file"],
    "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
    "selector": "source.python",
    "env": {"PYTHONIOENCODING": "utf-8"}, 
}
```

***Lưu ý:***

* Đường dẫn đến file python.exe phải chính xác theo máy cụ thể (lúc cài đặt Python)
* Dòng `env": {"PYTHONIOENCODING": "utf-8"}` là để giúp Sublime Text có thể run Python với Unicode.

Bước tiếp theo là lưu file với tên tùy chọn theo cấu trúc: `<Tên tùy chọn>.sublime-build`

Ví dụ: `Python 3x.sublime-build`

Lúc này ta sẽ thấy trong list các trình biên dịch được liệt kê sẽ có thêm `Python 3x`. Check chọn để build code Python.

![Ảnh chụp màn hình](/files/-MLQfdkEt8ghcniknkk4)


# Ghi chép - ghi chú

Ghi chép trong quá trình biên soạn tài liệu

## Chuyển đổi và in hệ cơ số decimal, binary, oct, hex

```python
n  = 1986
print (f"decimal: {n:d}")
print(f"oct:\t {n:o}")
print(f"hex:\t {n:x}")
print(f"binary:\t {n:b}")
```

Output:

```
decimal: 1986
oct:     3702
hex:     7c2
binary:  11111000010
```

## Tách dữ liệu từ một dòng nhập

```python
# a = input()
a = 'Hello! Nice to see you!'
x, *y = a.split()
# Hello!
print(x)
# ['Nice', 'to', 'see', 'you!'] 
print(y)
```

```python
a = 'Learn Python'
x, y = a.split()
# Learn
print(x)
# Python 
print(y)
```

## Chèn ký tự vào xâu bằng rjust(), ljust() và center()

Canh lề ký tự khi in

```python
# rjust(), ljust() và center() chèn thêm vào xâu n-len(s) ký tự
s = "banana"
xr = s.rjust(20, "-")
xl = s.ljust(20, '-')
xc = s.center(20,'-')
# Output: --------------banana
print(xr)
# Output: banana--------------
print(xl)
# Output: -------banana-------
print(xc)
```

## Bung nén tham số tuple, dict

```python
# Function argument unpacking

def myfunc(x, y, z):
    print(x, y, z)

a = (12, 40, 99)
b = {'x': 31, 'z': 'MA', 'y': 88}

myfunc(*a)
myfunc(**b)
```

## Lấy chỉ số list (list index)

```python
lst = [1,2,3]
print(lst.index(3))
# out: 2
```

## Đảo ngược list, string

```python
s = 'abcd'
ms = list(s)
ss = s[::-1]
mss = ms[::-1]

print(ss)        # dcba
print(ms)        # ['a', 'b', 'c', 'd']
print(mss)       # ['d', 'c', 'b', 'a']
```

## Cắt xâu string, list (slide)

```python
s = '123456789'
ss = list(s)

print(s[:2])
print(s[-2:])
print(s[1:-2])

print(ss[:2])
print(ss[-2:])
print(ss[1:-2])
```

## Remove phần tử trùng lặp trong list

Đơn giản chỉ bằng cách ép kiểu set và trả lại list

```python
a = [1,1,2,3,4,5,4,5,4]
a = list(set(a))
print(a)

# Output: [1, 2, 3, 4, 5]
```

## Ép kiểu phần tử list

Chuyển các phần tử của list từ str sang int và ngược lại. [Xem thêm bài map() ](/phu-luc/ham-map)

```python
a = ['1','2','3','4','5']
b = ['1','2','3','4','5']
a = list(map(int,a))           # cách 1
b = [int(i) for i in b]        # Cách 2
print(a, b) 

# Output: [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
```

## Phân biệt list, tuple, set, dict

Khác biệt giữa List, Dict, Tuple, Set trong Python

| Tên   | Đặc trưng                    | Sửa phần tử | Sắp xếp | Đặt trong dấu | Khởi tạo |
| ----- | ---------------------------- | ----------- | ------- | ------------- | -------- |
| List  | Chứa bất kì kiểu dữ liệu nào | x           | x       | \[]           | list()   |
| Tuple | Giá trị không thể thay đổi   |             | x       | ()            | tuple()  |
| Set   | Giá trị là duy nhất          | x           |         | {}            | set()    |
| Dict  | Key: Value                   | x           |         | {}            | dict()   |

## Colab Notebook

{% embed url="<https://colab.research.google.com/drive/1ZvsCX1WmCbCAX2NreN0FY8Gc8g2tPyVr?usp=sharing>" %}


# Mảng 2 chiều

Một ví dụ về đọc dữ liệu từ file và xử lý mảng 2 chiều

Cho file mang2.in như sau:

```
4 5
11 8 88 6
20 3 80 60
30 5 99 123
42 7 20 10
55 4 33 2020
```

Trong đó: Dòng thứ nhất là 2 số nguyên dương m, n cách nhau bở một khoảng trắng. n dòng tiếp theo, mỗi dòng gồm m số nguyên cách nhau bởi một khoảng trắng tạo thành mảng ma có m cột, n dòng.

(mảng ma gồm 4 cột 5 dòng)

### Yêu cầu:

a. Đọc dữ liệu từ file mang2.in và in ra màn hình mảng ma.

b. Tính tổng đường chéo của mảng gồm m dòng đầu tiên của mảng ma. (tong = 11+3+99+10 = 123)

## Bài làm

```python
with open('mang2.IN') as fi:
    m,n = list(map(int,fi.readline().split()))
    ma = []
    for i in range(n):
        ma.append(list(map(int,fi.readline().split())))

## Câu a
for a in ma:
    print(a)

## Câu b
tong = 0
for i in range(m):
    for j in range(m):
        if i == j: tong += ma[i][j]
print(tong)
```

## Giải thích:

### Đọc dữ liệu vào

`m,n = list(map(int,fi.readline().split()))`

Lệnh này sử dụng phương thức .split() để tách xâu bởi các khoảng trắng tạo thành list. Như vậy, với dòng đầu của file mang2.in là `4 5` ta dùng lệnh `fi.readline().split())` và thu được dữ liệu vào là \['4', '5'].

Nếu ta dùng `m,n = fi.readline().split()` thì m được gán bằng '4' và n được gán bằng '5'. Lúc này m,n có kiểu dữ liệu là string.

Để chuyển dữ liệu từ string sang int ta có thể ép kiểu cho từng biến. Tuy nhiên như thế sẽ làm cho code dài và khó đọc. Trong bài này ta chuyển dữ liệu string sang int bằng hàm map(). `m,n = list(map(int,fi.readline().split()))`

**Để dễ hiểu hơn ta xét ví dụ sau:**

```python
s = '1 2 3'

# Tách xâu s thành list a
ma = s.split() 

# in mảng a ta có: ['1', '2', '3']. 
#(lúc này các phần tử là string)
print(ma)

# Chuyển các phần tử của mảng a thành kiểu int
ma = list(map(int,ma))

# in mảng a ta có: [1, 2, 3]
print(ma)
```

* [Đọc thêm về hàm map() tại đây](https://python.dainganxanh.com/phu-luc/ham-map)
* [Xem thêm ví dụ ép kiểu bằng map() tại đây](https://python.dainganxanh.com/phu-luc/ghi-chep-hau-truong#ep-kieu-phan-tu-list)

Còn tiếp...


# Công thức với dãy số

```
// Some code
# Tỏng các số tự nhiên từ 1 đến n
s = int(n*(n+1)/2)

# Tổng các số tự nhiên lẻ từ 1 đến n
s_le = int(n/2 + 0.5)**2 

# Tổng các số tự nhiên chẵn
s_chan = int((n/2)**2 + n/2 )

#Tổng bình phương của n số tự nhiên liên tiếp
s_binhphuong = int((n*(n+1)*(2*n - 1))/6 )

# Tổng lập phương dãy n số tự nhiên
s_lapphuong = int((n*2 + n)/2)**2
```


# Tài liệu tham khảo

<https://www.python.org>

<https://www.programiz.com>

<https://www.w3schools.com/python>


