Python làm một ngôn ngữ khá dễ tiếp cận cho người mới tuy nhiên thì với Python thì cũng có khá nhiều thứ khá nhì nhằng và bài viết này sẽ giúp các bạn hiểu thêm về mấy cái nhì nhằng này. Dưới đây là một số tính năng khá vi diệu có trong Python (Lưu ý đây là tính năng chứ không phải bug).
1. Cấu trúc for-else.
Nhiều ngôn ngữ lập trình có cấu trúc if-else
để xử lý các câu lệnh điều kiện. Tuy nhiên, trong Python, bạn thậm chí có thể sử dụng câu lệnh else
mà không cần if
.
animals = ["Dog", "Cat", "Pig"] for animal in animals: if animal == "Chicken": print("Chicken is an animal!") break
else: print("Not found Chicken!") # Not found Chicken!
Ví dụ, đoạn mã trên không có câu lệnh if
. Nhưng khối mã bên dưới câu lệnh else
đã được thực thi thành công!
Đây là cú pháp for-else
trong Python.
Đó là một tính năng kỳ lạ và chúng ta nên cẩn thận khi sử dụng nó. Nhưng ý tưởng của nó lại đơn giản đến không ngờ:
Khối
else
sẽ được thực thi khi vòng lặpfor
chạy mà không gặpbreak
.
Để chứng minh điều này, hãy xem xét đoạn code sau:
animals = ["Dog", "Cat", "Pig", "Chicken"] for animal in animals: if animal == "Chicken": print("Chicken is an animal!") break
else: print("Not found Chicken!") # Chicken is an animal!
Khi chạy đoạn code trên, danh sách animals
chứa “Chicken” vì vậy, vòng lặp for
đã bị break
và câu lệnh else
không được thực thi.
2. Thay đổi Tuples trong Python ?
Như đã biết thì tuples là immutable Python objects (tức là đối tượng không thể thay đổi được). Tuy nhiên thì có một mẹo có thể tạm coi là thay đổi tuples mặc dù thực tế thì bản chất tuples không hề thay đổi.
Nhưng theo cách dưới đây thì ta có thể tạm coi là tuples được thay đổi.
tp = ([1, 2, 3], 4, 5)
tp[0].append(4)
print(tp)
# ([1, 2, 3, 4], 4, 5)
giải thích cho điều này thì tp
là một immutable Python objects (bất biến). Tuy nhiên, phần tử đầu tiên trong tp là một list
có thể thay đổi được. Khi này, phần tử đầu tiên của tp
sẽ trỏ vào địa chỉ ô nhớ lưu trữ list
mà có giá trị là [1, 2, 3]
ban đầu. Khi ta thay đổi giá trị của phần tử đầu tiên thì tp
không đổi tuy nhiên thì ta vẫn có thể coi là tp
thay đổi (thay đổi về mặt giá trị trả ra). Các bạn có thể xem xét ví dụ dưới đây để thấy rõ hơn:
a = [1, 2, 3]
tp = (a, 4, 5)
print(id(a) ==id(tp[0]))
# True
a.append(4)
print(id(a) == id(tp[0]))
print(tp)
# True
# ([1, 2, 3, 4], 4, 5)
3. 256 là 256, nhưng đôi khi 257 không phải 257
Ngạc nhiên chưa, đây là một tính năng ảo diệu của python chứ hoàn toàn không phải lỗi. Khi chạy ví dụ dưới đây với python shell
thì kết quả trả ra sẽ khiến bạn bất ngờ.
>>> a=256
>>> b=256
>>> a is b
True
>>> x = 257
>>> y = 257
>>> print(x is y)
False
>>>
Về cơ bản, Python tải trước tất cả các số nguyên nhỏ trong phạm vi [-5, 256]
để tiết kiệm thời gian và chi phí bộ nhớ. Do đó, khi một số nguyên trong phạm vi này được khai báo, Python chỉ tham chiếu số nguyên được lưu trong bộ nhớ cache và sẽ không tạo bất kỳ đối tượng mới nào. Với mỗi dòng code là một câu lệnh hoàn toàn khác, được phân tích cú pháp và biên dịch riêng, do đó xảy ra trường hợp như trên.
Trong một từ, a
và b
là cùng một đối tượng, nhưng x
và y
là hai đối tượng khác nhau.
>>> id(a)
1696073345424
>>> id(b)
1696073345424
>>> id(x)
1696122928496
>>> id(y)
1696122928752
Cơ chế này trong Python được gọi là integer interning
hoặc integer caching
.
Đấy là khi chúng ta viết code trong python shell thì sẽ xảy ra trường hợp này còn khi chúng ta viết chúng trong cùng 1 file, cùng một ngữ cảnh (context), trình biên dịch (complier) thì sẽ không xảy ra trường hợp này.
4. String interning
String interning làm cho các hoạt động xử lý chuỗi phổ biến tiết kiệm thời gian và không gian bằng cách lưu chúng vào bộ nhớ đệm. Thay vì mỗi lần tạo một bản sao mới của chuỗi, phương pháp tối ưu hóa này yêu cầu chỉ giữ một bản sao của chuỗi cho mọi giá trị riêng biệt bất biến thích hợp và sử dụng tham chiếu con trỏ ở bất kỳ đâu được gọi.
x = "This is a string"
y = "This is a string"
print(x is y) # prints True
Toán tửis
trong Python được sử dụng để kiểm tra xem hai đối tượng có tham chiếu đến cùng một vị trí bộ nhớ hay không. Nếu nó trả về True
, điều đó có nghĩa là hai đối tượng thực sự là cùng một đối tượng. Tong đoạn code trên, thay vì tạo một bản sao mới khi y
được gán cho một chuỗi có cùng giá trị với x
, Python trỏ đến cùng một chuỗi được gán cho x
. Điều này chỉ đúng với các chuỗi nhỏ hơn; các chuỗi lớn hơn sẽ tạo các bản sao riêng lẻ như bình thường.
x = "This is a string" * 300
y = "This is a string" * 300
print(x is y) # False
Điều này sẽ in ra False
trên console
và các chuỗi không được interning. Và dưới đây là giải pháp:
import sys
x = sys.intern("This is a string" * 300)
y = sys.intern("This is a string" * 300)
print(x is y) # True
5. 0.1+0.2 is not 0.3
Trong thực tế thì mọi người đều biết là0.1 + 0.2 = 0.3
tuy nhiên thì Python lại không nghĩ như vậy:
print(0.1+0.2==0.3)
print(0.1+ 0.2)
# False
# 0.30000000000000004
Thực tế mà nói, đây không phải là lỗi của Python
. Không máy tính nào có thể tính toán giá trị float
một cách chính xác.
Máy tính chỉ có thể lưu trữ và xử lý các số thực với độ chính xác nhất định. Vì vậy, các hoạt động của float
dựa vào việc triển khai phần cứng trong chip xử lý của máy và không có ngôn ngữ lập trình nào có thể chắc chắn rằng các phép tính với float
luôn được tính đúng.