Skip to content

Có người nói họ pro Python, tôi cho họ xem bài này, và cái kết 😱

Posted on:October 1, 2017

Chào các bạn, dạo này mình mới học được cách giật tít từ mấy trang lá cải, đem áp dụng vào một số nơi và thấy là độ hiệu quả không ngờ =)))


Chuyện cũng chẳng có gì, mình có thói quen hay lên github xem mấy trending repositories. Và một ngày đẹp trời mình gặp wtfpython, thấy rất chất, nhiều điều mới và hữu ích nên muốn chia sẻ lại cho mọi người.

Bài khá dài, thực ra là quá dài, nên mình trích những điểm mà mình cho là hay nhất để viết lại (tất nhiên phù hợp với license của tác giả).

Bài viết gốc nằm trên repo wtfpython, các bạn có thể lên đó star hay clone về nghiên cứu.


Skipping lines?

Output

>>> apple = 11
>>> аpple = 12
>>> apple
11

wtf ? có gì đó sai sai ?

💡 Giải thích

Thực chất trong ví dụ trên, hai ký tự ‘а’ và ‘a’ là khác nhau. Ký tự ‘a’ ở dòng 1 là Latin thông thường, và ký tự ‘а’ ở dòng thứ 2 là Cyrillic ‘а’.

>>> ord('a')
97
>>> ord('а')
1072

Thật là kỳ diệu đúng không ? Ví dụ đầu tiên này không liên quan đến Python cho lắm, đơn thuần nó chỉ muốn nhấn mạnh một điều rằng “những đoạn code tưởng chừng như không thể sai vẫn có thể sai”.

Sẽ ra sao nếu bạn phải debug một đoạn code mà dev trước đó ‘cố ý’ để lại như vậy ?

Sẽ ra sao nếu bạn bị dẫn tới trang fishing bằng cách thức như vậy? ví dụ ’https://аpple.com’ ? các bạn có thể đọc thêm về vụ fishing này tại đây

Lời khuyên là hãy luôn cẩn thận, vậy thôi.


Time for some hash brownies!

some_dict = {}
some_dict[5.5] = "Ruby"
some_dict[5.0] = "JavaScript"
some_dict[5] = "Python"

Output

>>> some_dict[5.5]
"Ruby"
>>> some_dict[5.0]
"Python"
>>> some_dict[5]
"Python"

“Javascript” đã biến đâu mất rồi?

💡 Giải thích

>>> 5 == 5.0
True
>>> hash(5) == hash(5.0)
True

Bạn có thể đọc thêm về hash trong Python tại đây


Evaluation time discrepancy

array = [1, 8, 15]
g = (x for x in array if array.count(x) > 0)
array = [2, 8, 22]

Output

print(list(g))
[8]

💡 Giải thích


Modifying a dictionary while iterating over it

x = {0: None}

for i in x:
    del x[i]
    x[i+1] = None
    print(i)

Output

0
1
2
3
4
5
6
7

Tại sao in có đúng 8 số ?

💡 Giải thích


Deleting a list item while iterating over it

list_1 = [1, 2, 3, 4]
list_2 = [1, 2, 3, 4]
list_3 = [1, 2, 3, 4]
list_4 = [1, 2, 3, 4]

for idx, item in enumerate(list_1):
    del item

for idx, item in enumerate(list_2):
    list_2.remove(item)

for idx, item in enumerate(list_3[:]):
    list_3.remove(item)

for idx, item in enumerate(list_4):
    list_4.pop(idx)

Output

>>> list_1
[1, 2, 3, 4]
>>> list_2
[2, 4]
>>> list_3
[]
>>> list_4
[2, 4]

Tại sao kết quả lại ra [2, 4]

💡 Giải thích

>>> some_list = [1, 2, 3, 4]
>>> id(some_list)
139798789457608
>>> id(some_list[:]) # Notice that python creates new object for sliced list.
139798779601192

Sự khác nhau giữa del, remove, pop

Kết quả


Let’s make a giant string!

def add_string_with_plus(iters):
    s = ""
    for i in range(iters):
        s += "xyz"
    assert len(s) == 3*iters

def add_string_with_format(iters):
    fs = "{}"*iters
    s = fs.format(*(["xyz"]*iters))
    assert len(s) == 3*iters

def add_string_with_join(iters):
    l = []
    for i in range(iters):
        l.append("xyz")
    s = "".join(l)
    assert len(s) == 3*iters

def convert_list_to_string(l, iters):
    s = "".join(l)
    assert len(s) == 3*iters

Output

>>> timeit(add_string_with_plus(10000))
100 loops, best of 3: 9.73 ms per loop
>>> timeit(add_string_with_format(10000))
100 loops, best of 3: 5.47 ms per loop
>>> timeit(add_string_with_join(10000))
100 loops, best of 3: 10.1 ms per loop
>>> l = ["xyz"]*10000
>>> timeit(convert_list_to_string(l, 10000))
10000 loops, best of 3: 75.3 µs per loop

Có điều gì đáng chú ý với các cách concat strings này ?

Giải thích


String interning

Output

>>> a = "some_string"
>>> id(a)
140420665652016
>>> id("some" + "_" + "string") # Notice that both the ids are same.
140420665652016
# using "+", three strings:
>>> timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100)
0.25748300552368164
# using "+=", three strings:
>>> timeit.timeit("s1 += s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100)
0.012188911437988281

Giải thích


Yes, it exists!

Có thể bạn đã biết, Python cũng có else cho vòng lặp for:

def does_exists_num(l, to_find):
    for num in l:
        if num == to_find:
            print("Exists!")
            break
    else:
        print("Does not exist")

Output

>>> some_list = [1, 2, 3, 4, 5]
>>> does_exists_num(some_list, 4)
Exists!
>>> does_exists_num(some_list, -1)
Does not exist

thậm chí trong exception cũng có else luôn:

try:
    pass
except:
    print("Exception occurred!!!")
else:
    print("Try block executed successfully...")

Output

Try block executed successfully...

Giải thích


is is not what it is!

Đoạn code sau đã từng gây sốt cộng đồng mạng

>>> a = 256
>>> b = 256
>>> a is b
True

>>> a = 257
>>> b = 257
>>> a is b
False

>>> a = 257; b = 257
>>> a is b
True

tạm bỏ qua cái cộng đồng suốt ngày đau yếu và dễ lên cơn, đoạn code trên thực sự CHẤT!.

Giải thích

Sự khác nhau giữa is==:

>>> [] == []
True
>>> [] is [] # These are two empty lists at two different memory locations.
False

256 is an existing object but 257 isn’t

>>> id(256)
10922528
>>> a = 256
>>> b = 256
>>> id(a)
10922528
>>> id(b)
10922528
>>> id(257)
140084850247312
>>> x = 257
>>> y = 257
>>> id(x)
140084850247440
>>> id(y)
140084850247344

is not ... is not is (not ...)

>>> 'something' is not None
True
>>> 'something' is (not None)
False

Giải thích


The function inside loop sticks to the same output

funcs = []
results = []
for x in range(7):
    def some_func():
        return x
    funcs.append(some_func)
    results.append(some_func())

funcs_results = [func() for func in funcs]

Output

>>> results
[0, 1, 2, 3, 4, 5, 6]
>>> funcs_results
[6, 6, 6, 6, 6, 6, 6]

Sao func_results lại không phải trả về [0, 1, 2, 3, 4, 5, 6] như results?

Giải thích

funcs = []
for x in range(7):
    def some_func(x=x):
        return x
    funcs.append(some_func)

Output

>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]

Loop variables leaking out of local scope!

for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

Output

6 : for x inside loop
6 : x in global

Nhưng x còn chưa được định nghĩa cơ mà!

# This time let's initialize x first
x = -1
for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

Output

6 : for x inside loop
6 : x in global
x = 1
print([x for x in range(5)])
print(x, ': x in global')

Output (on Python 2.x):

[0, 1, 2, 3, 4]
(4, ': x in global')

Output (on Python 3.x):

[0, 1, 2, 3, 4]
1 : x in global

Giải thích


A tic-tac-toe where X wins in the first attempt!

row = [""]*3
board = [row]*3

Output:

>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]

Tại sao 3 giá trị đầu đều là ếch ?

💡 Giải thích:

Đầu tiên khi ta khai báo biến row sẽ trông thế này image Khi ta nhân nhiều biến row vào với nhau, mục đích để tạo nên board 3x3, nhưng thực chất chúng vẫn đều trỏ đến row mà thôi. image Nếu muốn tạo board, hãy dùng list comprehension thay thế:

board = [[""]*3 for i in range(3)]

Beware of default mutable arguments!

def some_func(default_arg=[]):
    default_arg.append("some_string")
    return default_arg

Output:

>>> some_func()
['some_string']
>>> some_func()
['some_string', 'some_string']
>>> some_func([])
['some_string']
>>> some_func()
['some_string', 'some_string', 'some_string']

💡 Giải thích:


Same operands, different story!

1.

a = [1, 2, 3, 4]
b = a
a = a + [5, 6, 7, 8]

Output:

>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4]

2.

a = [1, 2, 3, 4]
b = a
a += [5, 6, 7, 8]

Output:

>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4, 5, 6, 7, 8]

💡 Giải thích:


Using a variable not defined in scope

a = 1
def some_func():
    return a

def another_func():
    a += 1
    return a

Output:

>>> some_func()
1
>>> another_func()
UnboundLocalError: local variable 'a' referenced before assignment

💡 Giải thích:


When True is actually False

True = False
if True == False:
    print("I've lost faith in truth!")

Output:

I've lost faith in truth!

True == False ? thật giả lẫn lộn hết rồi.

💡 Giải thích:


Name resolution ignoring class scope

1.

x = 5
class SomeClass:
    x = 17
    y = (x for i in range(10))

Output:

>>> list(SomeClass.y)[0]
5

2.

x = 5
class SomeClass:
    x = 17
    y = [x for i in range(10)]

Output (Python 2.x):

>>> SomeClass.y[0]
17

Output (Python 3.x):

>>> SomeClass.y[0]
5

💡 Giải thích


From filled to None in one instruction…

some_list = [1, 2, 3]
some_dict = {
  "key_1": 1,
  "key_2": 2,
  "key_3": 3
}

some_list = some_list.append(4)
some_dict = some_dict.update({"key_4": 4})

Output:

>>> print(some_list)
None
>>> print(some_dict)
None

💡 Giải thích


Class attributes and instance attributes

1.

class A:
    x = 1

class B(A):
    pass

class C(A):
    pass

Ouptut:

>>> A.x, B.x, C.x
(1, 1, 1)
>>> B.x = 2
>>> A.x, B.x, C.x
(1, 2, 1)
>>> A.x = 3
>>> A.x, B.x, C.x
(3, 2, 3)
>>> a = A()
>>> a.x, A.x
(3, 3)
>>> a.x += 1
>>> a.x, A.x
(4, 3)

2.

class SomeClass:
    some_var = 15
    some_list = [5]
    another_list = [5]
    def __init__(self, x):
        self.some_var = x + 1
        self.some_list = self.some_list + [x]
        self.another_list += [x]

Output:

>>> some_obj = SomeClass(420)
>>> some_obj.some_list
[5, 420]
>>> some_obj.another_list
[5, 420]
>>> another_obj = SomeClass(111)
>>> another_obj.some_list
[5, 111]
>>> another_obj.another_list
[5, 420, 111]
>>> another_obj.another_list is SomeClass.another_list
True
>>> another_obj.another_list is some_obj.another_list
True

💡 Giải thích:


Midnight time doesn’t exist?

from datetime import datetime

midnight = datetime(2018, 1, 1, 0, 0)
midnight_time = midnight.time()

noon = datetime(2018, 1, 1, 12, 0)
noon_time = noon.time()

if midnight_time:
    print("Time at midnight is", midnight_time)

if noon_time:
    print("Time at noon is", noon_time)

Output:

('Time at noon is', datetime.time(12, 0))

thời gian lúc nửa đêm đâu mất rồi ? phải chăng Python đi ngủ ?

💡 Giải thích:

Trước phiên bản Python 3.5, giá trị datetime.time sẽ bị coi là False nếu nó gọi đến thời gian nửa đêm (thời gian UTC). Nghe khá là creepy^^


What’s wrong with booleans?

1.

# A simple example to count the number of boolean and
# integers in an iterable of mixed data types.
mixed_list = [False, 1.0, "some_string", 3, True, [], False]
integers_found_so_far = 0
booleans_found_so_far = 0

for item in mixed_list:
    if isinstance(item, int):
        integers_found_so_far += 1
    elif isinstance(item, bool):
        booleans_found_so_far += 1

Outuput:

>>> booleans_found_so_far
0
>>> integers_found_so_far
4

2.

another_dict = {}
another_dict[True] = "JavaScript"
another_dict[1] = "Ruby"
another_dict[1.0] = "Python"

Output:

>>> another_dict[True]
"Python"

💡 Giải thích:

Conclusion

Cái này chúng tôi gọi là Python chất đến từng dòng code!

References