Python
Learning Resources
Websites:
Books:
- Python Crash Course: cơ bản, dễ hiểu
- Automate the Boring Stuff with Python: cơ bản, dễ hiểu, nhiều ví dụ về ứng dụng tự động hoá của Python.
Advantages of Python:
- Readability
- Easy to learn
- Huge community and supports.
Why Python is slow ?
- “It’s the GIL (Global Interpreter Lock)”
- “It’s because its interpreted and not compiled”
- “It’s because its a dynamically typed language”
Reference: https://hackernoon.com/why-is-python-so-slow-e5074b6fe55b
Some good practice tips
The code explains itself.
a = 10_00_000
a, b, *_ = (1, 2, 3, 4)
# Use:
a = dict.get('key', '')
# instead of
a = '' if 'key' in dict else dict['key']
x = lambda a, b : a * b
# x(5, 6) -> 30
x = lambda a : a*3 + 3
# x(3) -> 12
# map() function
def square_it_func(a):
return a * a
x = map(square_it_func, [1, 4, 7])
# print(x) -> '[1, 16, 49]'
def multiplier_func(a, b):
return a * b
x = map(multiplier_func, [1, 4, 7], [2, 5, 8])
# print(x) -> '[2, 20, 56]'
# filter() function
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
# Function that filters out all numbers which are odd
def filter_odd_numbers(num):
return True if num % 2 == 0 else False
filtered_numbers = filter(filter_odd_numbers, numbers)
# -> [2, 4, 6, 8, 10, 12, 14]
Itertools: The Python Itertools module is a collection of tools for handling iterators. An iterator is a data type that can be used in a for loop including lists, tuples, and dictionaries.
from itertools import *
# Easy joining of two lists into a list of tuples
for i in izip([1, 2, 3], ['a', 'b', 'c']):
print i
# ('a', 1)
# ('b', 2)
# ('c', 3)
# The count() function returns an interator that
# produces consecutive integers, forever. This
# one is great for adding indices next to your list
# elements for readability and convenience
for i in izip(count(1), ['Bob', 'Emily', 'Joe']):
print i
# (1, 'Bob')
# (2, 'Emily')
# (3, 'Joe')
# The dropwhile() function returns an iterator that returns
# all the elements of the input which come after a certain
# condition becomes false for the first time.
def check_for_drop(x):
print 'Checking: ', x
return (x > 5)
for i in dropwhile(should_drop, [2, 4, 6, 8, 10, 12]):
print 'Result: ', i
# Checking: 2
# Checking: 4
# Result: 6
# Result: 8
# Result: 10
# Result: 12
# The groupby() function is great for retrieving bunches
# of iterator elements which are the same or have similar
# properties
a = sorted([1, 2, 1, 3, 2, 1, 2, 3, 4, 5])
for key, value in groupby(a):
print(key, value), end=' ')
# (1, [1, 1, 1])
# (2, [2, 2, 2])
# (3, [3, 3])
# (4, [4])
# (5, [5])
Generators: Generator functions allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop. -> simplifies code and memory efficient than loop.
# a generator that yields items instead of returning a list
def f
num = 0
while num < n:
yield num
num += 1
sum_of_first_n = sum(firstn(1000000))
We can turn a list comprehension into a generator expression by:
# list comprehension
doubles = [2*n for n in range(50)]
# generator expressino
doubles = (2*n for n in range(50))
Command line arguments
sys.argv
return a list of command line arguments
sys
Module
sys.executable
return string: absolute path to the Python interpreter.
sys.exit()
exit from Python, can take an optional argument (exit status).
sys.exit(0)
sys.platform
Use to check what OS is currently in used.
Assert Statement
Assertion condition:
- True: program continue to run
- False: Assertion stops the program and gives
AssertionError
. Syntax:assert <condition> assert <condition>, <error message>
Documenting your code with Docstrings
“Code is more often read than written.” - Guido Van Rossum
Python Docstrings giúp ta thêm phần documentation cho các module, function, class và method.
Khác với comments, docstring phải mô tả “làm cái gì”.
Có thể sử dụng Google style guide (dễ nhìn nhất) làm docstring format.
Đọc thêm về
Nguyên tắc xây dựng docstrings
- Được khởi tạo bởi multiline comments “”” ngay dưới class, method, function
- Mọi function đều nên có docstring.
- Dòng đầu tiên nên là short description.
- Mỗi dòng nên cách ra để dễ nhìn.
Ví dụ
def my_function():
"""Do nothing, but document it.
Args:
n: the number to get the square root of.
Returns:
the square root of n.
Raises:
TypeError: if n is not a number.
ValueError: if n is negative.
"""
pass
>>> help(mymodule.MyClass)
>>> help(mymodule.my_function)
>>> print(my_function.__doc__)
Tham khảo
Type Hints
Python có tính chất dynamically-typed (kiểu biến không cần xác định rõ). Type Hints (thư viên typing
) được tạo ra để giúp giải quyết vấn đề này.
- Đọc code dễ dàng hơn
def send_request(request_data : Any,
headers: Optional[Dict[str, str]],
user_id: Optional[UserId] = None,
as_json: bool = True):
...
def hello(name: str) -> None:
print(f'hello {name}')
-
IDE hiểu tốt hơn:
Khi biết được kiểu biến thì IDE sẽ đưa ra được gợi ý chính xác hơn, và đưa ra cảnh báo nếu sai kiểu.
Tham khảo
Python CLI
Để hiểu cách sử dụng argparse
thì bạn hãy đọc 2 bài đầu tiên dưới phần Reference, nhất là bài của RealPython.
Mình có viết ví dụ về cách sử dụng tại repo minhdq99hp/sample-code
Có một số thư viện hỗ trợ việc viết CLI cho python (VD: click), trong đó argparse
là một cách standard nhất để viết.
- An argument is a single part of a command line, delimited by blanks.
- An option is a particular type of argument (or a part of an argument) that can modify the behavior of the command line.
- A parameter is a particular type of argument that provides additional information to a single option or command.
argparse
Đọc bài viết này.
Ngoài ra, mình cũng viết file argparse_example.py
tại repo minhdq99hp/sample-code
Định nghĩa
The subprocess
mudle allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.
Có các hàm:
- call
- check_call
- check_output
Về bản chất, subprocess
sử dụng class Popen
để tạo và quản lý process. Khi có nhu cầu sử dụng phức tạp hơn thì phải sử dụng Popen
. VD: kill process.
Code tham khảo mình để trong repo minhdq99hp/sample-code.
References
- https://docs.python.org/2/library/subprocess.html
Python High Performance
1. Benchmarking and Profiling
Phát hiện phần bị chạy chậm trong phần mềm (bottleneck) là một công việc quan trọng khi muốn tối ưu code.
Profiling là một kỹ thuật giúp xác định được bottlenecks. Profiler là một chương trình chạy code và quan sát xem mỗi function chạy mất bao lâu, giúp xác định ra vị trí chạy chậm.
Khi thiết kế chương trình hiệu suất cao (performance-intensive), bước đầu tiên là viết code chưa cần quan tâm đến tối ưu vội.
“Premature optimization is the root of all evil” - Donald Knuth
Một số nguyên tắc cần phải nhớ khi tối ưu code:
- Make it run: đảm bảo code chạy ra kết quả đúng trước khi tối ưu.
- Make it right: đảm bảo thiết kế chương trình chắc chắn, có hệ thống.
- Make it fast: sau đó, tối ưu những phần chạy chậm trước.
Writing tests and benchmarks
- Test: Trong quá trình tối ưu, chún ta sẽ phải viết lại code, vì thế phải tạo ra các hàm test để tránh mất thời gian vào broken code.
- Benchmark: Tạo hàm benchmark để đánh giá performance của code.
Timing your benchmarks
Cách 1: dùng lệnh time
của Unix.
time python simul.py
sẽ trả về thông tin:
real 0m7.998s
user 0m8.094s
sys 0m0.221s
Với:
- real: Thời gian thực tế để chạy chương trình.
- user: Thời gian của các CPUs để tính toán
- sys: Thời gian của các CPUs để thực hiện các system-related tasks. VD: memory allocation.
Chú ý:
- user+sys có thể nhiều hơn real do đa nhân chạy song song.
- Để đo chính xác hơn thì benchmark nên chạy đủ lâu, để quá trình setup ngắn hơn execution.
Cách 2: Module timeit
Module timeit chạy đoạn code trong vòng lặp n lần, rồi thực hiện quá trình này r lần (thường r là 3) rồi trả về giá trị tốt nhất. Vì vậy, timeit phù hợp để tính toán thời gian chính xác một phần nhỏ của chương trình.
Chú ý:
timeit
có thể dùng dưới dạng module hoặc magic command trong Jupyter Notebook.
Finding bottlenecks with cProfile
python -m cProfile simul.py
se in ra danh sách thời gian chạy của từng phần trong code.
cProfile
output có 5 cột: ncalls, tottime, cumtime, percall, filename:lineno:
Metric quan trọng nhất là tottime, thời gian chạy thực tế trong function body, không bao gồm sub-calls.
KCachegrind là một công cụ GUI hỗ trợ profiling. Dùng pyprof2calltree
để convert output của cProfile
sáng KCachegrind
… (đọc thêm)
Profile line by line with line_profiler
Sau khi xác định được function cần tối ưu thì sử dụng line_profiler
để check xem thời gian chạy của các dòng. (đọc thêm)
Optimizing Code
Có một số cách để tối ưu, cách tốt nhất là thay đổi thuật toán.
Module dis
(disassemble): liệt kê các instructions.
Profiling memory usage with memory_profiler
Tương tự, memory_profiler
thống kê lượng bộ nhớ bị chiếm qua từng dòng. (đọc thêm)
Cách để tối ưu memory: sử dụng __slot__
Performance tuning tips for pure Python code
Khi tối ưu code Python, tốt nhất nên nhìn vào standard library, nơi chứa nhiều modules đã được tối ưu, viết bằng C.
Module collections
cung cấp nhiều data containers phù hợp để giải quyết một số trường hợp.
2. Fast Array Operations with Numpy
Reference
- Book: Python High Performance Programming
Contributors
- minhdq99hp $\dagger$
Comments