12/08/2018, 15:41

Những thủ thuật Python bạn nhất định phải biết trên con đường go pro

Với bất kỳ ngôn ngữ nào, nếu bạn muốn thuần thục ngôn ngữ đó, bạn nhất định phải biết các thủ thuật cũng như tính năng đặc trưng của ngôn ngữ đó. Những điều này thường được tích lũy qua quá trình làm việc lâu dài, mà một vài quyển sách dạy lập trình không thể nói hết được. Python cũng không ...

cover

Với bất kỳ ngôn ngữ nào, nếu bạn muốn thuần thục ngôn ngữ đó, bạn nhất định phải biết các thủ thuật cũng như tính năng đặc trưng của ngôn ngữ đó. Những điều này thường được tích lũy qua quá trình làm việc lâu dài, mà một vài quyển sách dạy lập trình không thể nói hết được. Python cũng không phải là ngoại lệ.

Bài viết này sẽ trình bày một vài thủ thuật trong số đó. Các thủ thuật này sẽ giúp code của bạn trong "pro" hơn, ngắn gọn và dễ hiểu hơn. Nếu bạn đã có kinh nghiệm với Python, có thể bạn đã biết gần hết các thủ thuật, nhưng vẫn có thể có những thứ bạn chưa đụng đến bao giờ. Còn nếu bạn mới làm quen với ngôn ngữ, bài viết này sẽ giúp ích cho bạn rất nhiều.

Mỗi một thủ thuật hoặc tính năng của ngôn ngữ đều được demo bằng code và tôi nghĩ rằng, mình không cần phải nói thêm gì nữa. Mọi thứ để đã được thể hiện ở code rồi.

Trong bài viết này, tôi sẽ sử dụng Python 3, code Python 2 có thể thay đổi đôi chút.

>>> a, b, c = 1, 2, 3
>>> a, b, c
(1, 2, 3)
>>> a
1
>>> b
2
>>> c
3
>>> a, b, c = (2 * i + 1 for i in range(3))
>>> a, b, c
(1, 3, 5)
>>> a, (b, c), d = (1, (2, 3), 4)
>>> a, b, c, d
(1, 2, 3, 4)

Swap hai biến

>>> a, b = 1, 2
>>> a, b = b, a
>>> a, b
(2, 1)

Sử dụng toán tử * đại diện cho "phần còn lại"

>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[-1]
10
>>> a[-3]
8
>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[2:8]
[2, 3, 4, 5, 6, 7]

Slice với index âm

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[-4:-2]
[7, 8]

Slice nhảy bước

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::2]
[0, 2, 4, 6, 8, 10]
>>> a[::3]
[0, 3, 6, 9]
>>> a[2:8:2]
[2, 4, 6]

Slice nhảy bước âm

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[::-2]
[10, 8, 6, 4, 2, 0]

Slice kết hợp gán với list

>>> a = [1, 2, 3, 4, 5]
>>> a[2:3] = [0, 0]
>>> a
[1, 2, 0, 0, 4, 5]
>>> a[1:1] = [8, 9]
>>> a
[1, 8, 9, 2, 0, 0, 4, 5]
>>> a[1:-1] = []
>>> a
[1, 5]

Slice đặt tên

>>> a = [0, 1, 2, 3, 4, 5]
>>> LASTTHREE = slice(-3, None)
>>> LASTTHREE
slice(-3, None, None)
>>> a[LASTTHREE]
[3, 4, 5]
a = [0, 1]
>>> a[LASTTHREE]
[0, 1]

Dùng enumerate duyệt qua cả index và giá trị

>>> a = ['Hello', 'world', '!']
>>> for i, x in enumerate(a):
...     print('{}: {}'.format(i, x))
...
0: Hello
1: world
2: !

Duyệt qua key và value của dict

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> for k, v in m.items():
...     print('{}: {}'.format(k, v))
...
b: 2
c: 3
a: 1
d: 4
>>> a = [1, 2, 3]
>>> b = ['a', 'b', 'c']
>>> z = list(zip(a, b))
>>> z
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> list(zip(*z))
[(1, 2, 3), ('a', 'b', 'c')]

Nhóm các phần tử liền nhau

Dùng itertools

>>> a = [1, 2, 3, 4, 5, 6]
>>> group_adjacent = lambda a, k: list(zip(*([iter(a)] * k)))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]

Dùng slice

>>> from itertools import islice
>>> a = [1, 2, 3, 4, 5, 6]
>>> group_adjacent = lambda a, k: list(zip(*(islice(a, i, None, k) for i in range(k))))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]

Chuyển đổi dict

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m.items()
dict_items([('b', 2), ('c', 3), ('a', 1), ('d', 4)])
>>> list(zip(m.values(), m.keys()))
[(2, 'b'), (3, 'c'), (1, 'a'), (4, 'd')]
>>> mi = dict(zip(m.values(), m.keys()))
>>> mi
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

Dùng itertools

>>> import itertools
>>> a = [[1, 2], [3, 4], [5, 6]]
>>> list(itertools.chain.from_iterable(a))
[1, 2, 3, 4, 5, 6]

Dùng sum

>>> a = [[1, 2], [3, 4], [5, 6]]
>>> sum(a, [])
[1, 2, 3, 4, 5, 6]

Dùng list comprehension

>>> a = [[1, 2], [3, 4], [5, 6]]
>>> [x for l in a for x in l]
[1, 2, 3, 4, 5, 6]
>>> a = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> [x for l1 in a for l2 in l1 for x in l2]
[1, 2, 3, 4, 5, 6, 7, 8]

Nâng cao: list với các phần tử bất kỳ

>>> a = [1, 2, [3, 4], [[5, 6], [7, 8]]]
>>> latten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
>>> flatten(a)
[1, 2, 3, 4, 5, 6, 7, 8]

Biểu thứ generator

>>> g = (x ** 2 for x in range(10))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> sum(x ** 3 for x in range(10))
2025
>>> sum(x ** 3 for x in range(10) if x % 3 == 1)
408

Dict comprehension

>>> m = {x: x ** 2 for x in range(5)}
>>> m
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> m = {x: 'A' + str(x) for x in range(10)}
>>> m
{0: 'A0', 1: 'A1', 2: 'A2', 3: 'A3', 4: 'A4', 5: 'A5', 6: 'A6', 7: 'A7', 8: 'A8', 9: 'A9'}

Chuyển đổi dict dùng comprehension

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m
{'b': 2, 'c': 3, 'a': 1, 'd': 4}
>>> {v: k for k, v in m.items()}
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
>>> A = {1, 2, 3, 3}
>>> B = {3, 4, 5, 6, 7}
>>> A | B
{1, 2, 3, 4, 5, 6, 7}
>>> A & B
{3}
>>> A - B
{1, 2}
>>> B - A
{4, 5, 6, 7}
>>> A ^ B
{1, 2, 4, 5, 6, 7}
>>> A ^ B == ((A - B) | (B - A))
True

namedtupple

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(x=1.0, y=2.0)
>>> p
Point(x=1.0, y=2.0)
>>> p.x
1.0
>>> p.y
2.0
>>> class Point(collections.namedtuple('PointBase', ['x', 'y'])):
...     __slots__ = ()
...     def __add__(self, other):
...         return Point(x=self.x + other.x, y=self.y + other.y)
...
>>> p = Point(x=1.0, y=2.0)
>>> q = Point(x=2.0, y=3.0)
>>> p + q
Point(x=3.0, y=5.0)

Tập hợp cùng tuần số xuất hiện

>>> import collections
>>> A = collections.Counter([1, 2, 2])
>>> B = collections.Counter([2, 2, 3])
>>> A
Counter({2: 2, 1: 1})
>>> B
Counter({2: 2, 3: 1})
>>> A | B
Counter({2: 2, 1: 1, 3: 1})
>>> A & B
Counter({2: 2})
>>> A + B
Counter({2: 4, 1: 1, 3: 1})
>>> A - B
Counter({1: 1})
>>> B - A
Counter({3: 1})

Phần tử xuất hiện nhiều nhất

>>> import collections
>>> A = collections.Counter([1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 6, 7])
>>> A
Counter({3: 4, 1: 2, 2: 2, 4: 1, 5: 1, 6: 1, 7: 1})
>>> A.most_common(1)
[(3, 4)]
>>> A.most_common(3)
[(3, 4), (1, 2), (2, 2)]

Hàng đợi hai đầu

>>> import collections
>>> Q = collections.deque()
>>> Q.append(1)
>>> Q.appendleft(2)
>>> Q.extend([3, 4])
>>> Q.extendleft([5, 6])
>>> Q
deque([6, 5, 2, 1, 3, 4])
>>> Q.pop()
4
>>> Q.popleft()
6
>>> Q
deque([5, 2, 1, 3])
>>> Q.rotate(3)
>>> Q
deque([2, 1, 3, 5])
>>> Q.rotate(-3)
>>> Q
deque([5, 2, 1, 3])

Hàng đợi hai đầu có giới hạn độ dài

>>> import collections
>>> last_three = collections.deque(maxlen=3)
>>> for i in range(10):
...     last_three.append(i)
...     print(', '.join(str(x) for x in last_three))
...
0
0, 1
0, 1, 2
1, 2, 3
2, 3, 4
3, 4, 5
4, 5, 6
5, 6, 7
6, 7, 8
7, 8, 9

Dict có thứ tự

>>> m = dict((str(x), x) for x in range(10))
>>> print(', '.join(m.keys()))
4, 6, 8, 2, 3, 7, 0, 9, 5, 1
>>> import collections
>>> m = collections.OrderedDict((str(x), x) for x in range(10))
>>> print(', '.join(m.keys()))
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
>>> m = collections.OrderedDict((str(x), x) for x in range(10, 0, -1))
>>> print(', '.join(m.keys()))
10, 9, 8, 7, 6, 5, 4, 3, 2, 1

Dict với giá trị mặc định

>>> m = dict()
>>> m['a']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'a'
>>> import collections
>>> m = collections.defaultdict(int)
>>> m['a']
0
>>> m['b']
0
>>> m = collections.defaultdict(str)
>>> m['a']
'
>>> m['b'] += 'a'
>>> m
defaultdict(<class 'str'>, {'b': 'a', 'a': '})
>>> m = collections.defaultdict(lambda: '[default value]')
>>> m['a']
'[default value]'

Cấu trúc cây với default dict

>>> import collections
>>> import json
>>> tree = lambda: collections.defaultdict(tree)
>>> root = tree()
>>> root['menu']['id'] = 'file'
>>> root['menu']['value'] = 'File'
>>> root['menu']['menuitems']['new']['value'] = 'New'
>>> root['menu']['menuitems']['new']['onclick'] = 'new();'
>>> root['menu']['menuitems']['open']['value'] = 'Open'
>>> root['menu']['menuitems']['open']['onclick'] = 'open();'
>>> root['menu']['menuitems']['close']['value'] = 'Close'
>>> root['menu']['menuitems']['close']['onclick'] = 'close();'
>>> root
defaultdict(<function <lambda> at 0x7f5ce09e4488>, {'menu': defaultdict(<function <lambda> at 0x7f5ce09e4488>, {'value': 'File', 'menuitems': defaultdict(<function <lambda> at 0x7f5ce09e4488>, {'new': defaultdict(<function <lambda> at 0x7f5ce09e4488>, {'value': 'New', 'onclick': 'new();'}), 'close': defaultdict(<function <lambda> at 0x7f5ce09e4488>, {'value': 'Close', 'onclick': 'close();'}), 'open': defaultdict(<function <lambda> at 0x7f5ce09e4488>, {'value': 'Open', 'onclick': 'open();'})}), 'id': 'file'})})
>>> print(json.dumps(root, sort_keys=True, indent=4, separators=(',', ': ')))
{
    "menu": {
        "id": "file",
        "menuitems": {
            "close": {
                "onclick": "close();",
                "value": "Close"
            },
            "new": {
                "onclick": "new();",
                "value": "New"
            },
            "open": {
                "onclick": "open();",
                "value": "Open"
            }
        },
        "value": "File"
    }
}

Sinh uniqe id cho từng giá trị

>>> import collections
>>> import itertools
>>> value_to_numeric_map = collections.defaultdict(itertools.count().__next__)
>>> value_to_numeric_map['a']
0
>>> value_to_numeric_map['b']
1
>>> value_to_numeric_map['c']
2
>>> value_to_numeric_map['a']
0
>>> value_to_numeric_map['b']
1
>>> import heapq
>>> import random
>>> a = [random.randint(0, 100) for __ in range(100)]
>>> a
[77, 59, 53, 12, 57, 81, 90, 67, 62, 6, 93, 35, 28, 45, 32, 95, 55, 52, 0, 19, 99, 47, 3, 74, 15, 81, 69, 73, 94, 37, 51, 58, 36, 51, 57, 96, 15, 65, 4, 42, 42, 0, 36, 89, 34, 73, 12, 62, 13, 34, 84, 51, 92, 37, 17, 56, 59, 15, 8, 31, 36, 90, 70, 95, 41, 56, 100, 84, 50, 91, 72, 43, 15, 12, 90, 34, 58, 97, 72, 67, 79, 28, 84, 9, 25, 75, 47, 43, 100, 9, 27, 86, 12, 90, 4, 19, 45, 62, 91, 76]
>>> heapq.nsmallest(5, a)
[0, 0, 3, 4, 4]
>>> heapq.nlargest(5, a)
[100, 100, 99, 97, 96]

Tích Descartes

>>> import itertools
>>> for p in itertools.product('123', '45'):
...     print('.join(p))
...
14
15
24
25
34
35
>>> for p in itertools.product('12', repeat=4):
...     print('.join(p))
...
1111
1112
1121
1122
1211
1212
1221
1222
2111
2112
2121
2122
2211
2212
2221
2222

Tổ hợp

>>> import itertools
>>> for c in itertools.combinations('12345', 3):
...    print('.join(c))
...
123
124
125
134
135
145
234
235
245
345
>>> for c in itertools.combinations_with_replacement('12345', 2):
...    print('.join(c))
...
11
12
13
14
15
22
23
24
25
33
34
35
44
45
55

Chỉnh hợp

>>> import itertools
>>> for p in itertools.permutations('1234', 3):
...    print('.join(p))
...
123
124
132
134
142
143
213
214
231
234
241
243
312
314
321
324
341
342
412
413
421
423
431
432

Ghép các iterable

>>> import itertools
>>> a = [1, 2, 3, 4]
>>> for p in itertools.chain(itertools.combinations(a, 2), itertools.combinations(a, 3)):
...     print(p)
...
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
(1, 2, 3)
(1, 2, 4)
(1, 3, 4)
(2, 3, 4)
>>> for subset in itertools.chain.from_iterable(itertools.combinations(a, n) for n in range(len(a) + 1)):
...     print(subset)
...
()
(1,)
(2,)
(3,)
(4,)
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
(1, 2, 3)
(1, 2, 4)
(1, 3, 4)
(2, 3, 4)
(1, 2, 3, 4)

Nhóm các phần tử theo một tiêu chí

>>> import itertools
>>> for k, g in itertools.groupby('122333444455555'):
...     print(k, '.join(g))
...
1 1
2 22
3 333
4 4444
5 55555
>>> m = {'a': 1, 'b': 2, 'c': 1, 'd': 2}
>>> sorted_m = dict(sorted(m.items(), key=lambda x: x[1]))
>>> for k, g in itertools.groupby(sorted_m, lambda x: x[1]):
...     print(k, ', '.join(e[0] for e in g))
...
1 c, a
2 b, d

Tham số của startswith

>>> s = 'http://google.com'
>>> s.startswith('http://') or s.startswith('https://')
True
>>> s.startswith(('http://', 'https://'))
True

Tương tự với endswith và isinstance

>>> s.endswith('.com') or s.endswith('.net')
True
>>> s.endswith(('.com', '.net'))
True
>>> isinstance(1, (int, float))
True
>>> 1 + 2
3
>>> _
3
>>> _ + 1
4
>>> foo = 'bar'
>>> foo
'bar'
>>> _ + 'foo'
'barfoo'

Điều này đã được nhắc nhiều khi nói đến "Idiomatic Python"

>>> def foo(l=[]):
...     l.append(1)
...     print(l)
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]
>>> def foo(l=None):
...     l = l or []
...     l.append(1)
...     print(l)
...
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]

python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 ...
127.0.0.1 - - [24/Jul/2017 13:56:51] "GET / HTTP/1.1" 200 -
>>> x = 2
>>> 3 > x == 1
False
>>> 1 < x < 3
True
>>> 10 < 10*x < 30
True
>>> 10 < x**5 < 30
False
>>> 100 < x*100 >= x**6 + 34 > x <= 2*x < 5
True
>>> list(reversed([1, 2, 3, 4]))
[4, 3, 2, 1]
>>> [1, 2, 3, 4][::-1]
[4, 3, 2, 1]
>>> tuple(reversed((1, 2, 3, 4)))
(4, 3, 2, 1)
>>> (1, 2, 3, 4)[::-1]
(4, 3, 2, 1)
>>> x = ['Hello', 'world', '!']
>>> ' '.join(x)
'Hello world !'
>>> class Foo:
...     def one(self):
...         return 1
...
>>> c = Foo()
>>> c.one()
1
>>> def two(self):
...     return 2
...
>>> Foo.two = two
>>> c.two()
2
>>> x = [1, 2, 3]
>>> dir(x)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> x = [1, 2, 3]
>>> import sys
>>> sys.getsizeof(x)
88
>>> import pdb
>>> for i in range(10):
...     if i == 5:
...         pdb.set_trace()
...
> <stdin>(1)<module>()
(Pdb) i
5
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Bài viết gốc: https://manhhomienbienthuy.bitbucket.io/2017/Jul/24/python-tricks-you-have-to-know-when-you-go-pro.html (đã xin phép tác giả             </div>
            
            <div class=

0