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 ...
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ả