1.1.1. Python#
1.1.1.1. Variable name#
# Variable không được bắng đầu bằng số
# ko chứa dấu '-', được chứa dấu'_'
# có phân biệt UPCASE
# không được sử dụng keyword của python gồm
# Dấu "\" ở cuối dòng thể hiện dòng code sẽ nối tiếp dòng phía dưới
['and','as','assert','break','class','countinue','def','del',
'elif','else','except','exec','finally','for','from','global',
'if','import','in','is','lambda','not','or','pass','print','raise',
'return','try','while','with','with','yield']
['and',
'as',
'assert',
'break',
'class',
'countinue',
'def',
'del',
'elif',
'else',
'except',
'exec',
'finally',
'for',
'from',
'global',
'if',
'import',
'in',
'is',
'lambda',
'not',
'or',
'pass',
'print',
'raise',
'return',
'try',
'while',
'with',
'with',
'yield']
1.1.1.2. Python Types#
1.1.1.2.1. Numbers#
Numbers is int
and float
type(1) # int
type(-10) # int
type(0) # int
type(0.0) # float
type(2.2) # float
type(4E2) # float - 4*10 to the power of 2
float
# Arithmetic
10 + 3 # 13
10 - 3 # 7
10 * 3 # 30
10 ** 3 # 1000
10 / 3 # 3.3333333333333335
10 // 3 # 3 --> floor division - no decimals and returns an int
10 % 3 # 1 --> modulo operator - return the reminder. Good for deciding if number is even or odd
1
# relationship
3<4
4>3
3<=4
4<=3
3!=4
3==3
True
# Basic Functions
pow(5, 2) # 25 --> like doing 5**2
abs(-50) # 50
round(5.46) # 5
round(5.468, 2)# 5.47 --> round to nth digit
bin(512) # '0b1000000000' --> binary format
hex(512) # '0x200' --> hexadecimal format
'0x200'
# Converting Strings to Numbers
age = input("How old are you?")
age = int(age)
pi = input("What is the value of pi?")
pi = float(pi)
# format
print('{:3}'.format(123.456))
print('{:9}'.format(123.456))
print('{:09}'.format(123.456))
print('{:09.4f}'.format(123.456))
a='{:09.4f}'.format(123.456) #type a = str
print(float(a))
print(int(float(a)))
123.456
123.456
00123.456
0123.4560
123.456
123
# fraction
from fractions import*
frac=Fraction(7,14)
print(type(frac))
frac1=Fraction(2,19)
frac2=frac+frac1
print(frac)
print(frac2)
<class 'fractions.Fraction'>
1/2
23/38
1.1.1.2.2. String#
type('Hellloooooo') # str
'I\'m thirsty'
"I'm thirsty"
"\n" # new line
"\t" # adds a tab
'Hey you!'[4] # y
name = 'Andrei Neagoie'
name[4] # e
name[:] # Andrei Neagoie
name[1:] # ndrei Neagoie
name[:1] # A
name[-1] # e
name[::1] # Andrei Neagoie
name[::-1] # eiogaeN ierdnA
name[0:10:2]# Ade e
# : is called slicing and has the format [ start : end : step ]
'Hi there ' + 'Timmy' # 'Hi there Timmy' --> This is called string concatenation
'*'*10 # **********
'**********'
# Basic Functions
len('turtle') # 6
# Basic Methods
' I am alone '.strip() # 'I am alone' --> Strips all whitespace characters from both ends.
'On an island'.strip('d') # 'On an islan' --> # Strips all passed characters from both ends.
'but life is good!'.split() # ['but', 'life', 'is', 'good!']
'Help me'.replace('me', 'you') # 'Help you' --> Replaces first with second param
'Need to make fire'.startswith('Need')# True
'and cook rice'.endswith('rice') # True
'bye bye'.index('e') # 2
'still there?'.upper() # STILL THERE?
'HELLO?!'.lower() # hello?!
'ok, I am done.'.capitalize() # 'Ok, I am done.'
'oh hi there'.find('i') # 4 --> returns the starting index position of the first occurrence
'oh hi there'.count('e') # 2
2
# String Formatting
name1 = 'Andrei'
name2 = 'Sunny'
print(f'Hello there {name1} and {name2}') # Hello there Andrei and Sunny
print('Hello there {} and {}'.format(name1, name2)) # Hello there Andrei and Sunny
Hello there Andrei and Sunny
Hello there Andrei and Sunny
1.1.1.2.3. Boolean#
True
or False
. Used in a lot of comparison and logical operations in Python
bool(True)
bool(False)
# all of the below evaluate to False. Everything else will evaluate to True in Python.
print('1', bool(None))
print('2', bool(False))
print('3', bool(0))
print('4', bool(0.0))
print('5', bool([]))
print('6', bool({}))
print('7', bool(()))
print('8', bool(''))
print('9', bool(range(0)))
print('10', bool(set()))
# See Logical Operators and Comparison Operators section for more on booleans.
1 False
2 False
3 False
4 False
5 False
6 False
7 False
8 False
9 False
10 False
1.1.1.2.4. Lists#
my_list = [1, 2, '3', True]# We assume this list won't mutate for each example below
len(my_list) # 4
my_list.index('3') # 2
my_list.count(2) # 1 --> count how many times 2 appears
my_list[3] # True
my_list[1:] # [2, '3', True]
my_list[:1] # [1]
my_list[-1] # True
my_list[::1] # [1, 2, '3', True]
my_list[::-1] # [True, '3', 2, 1]
my_list[0:3:2] # [1, '3']
# : is called slicing and has the format [ start : end : step ]
[1, '3']
# Add to List
my_list * 2 # [1, 2, '3', True, 1, 2, '3', True]
my_list + [100] # [1, 2, '3', True, 100] --> doesn't mutate original list, creates new one
my_list.append(100) # None --> Mutates original list to [1, 2, '3', True, 100] # Or: <list> += [<el>]
my_list.extend([100, 200]) # None --> Mutates original list to [1, 2, '3', True, 100, 200]
my_list.insert(2, '!!!') # None --> [1, 2, '!!!', '3', True] - Inserts item at index and moves the rest to the right.
' '.join(['Hello','There'])# 'Hello There' --> Joins elements using string as separator.
'Hello There'
# Copy a List
basket = ['apples', 'pears', 'oranges']
new_basket = basket.copy()
new_basket2 = basket[:]
# Remove from List
[1,2,3].pop() # 3 --> mutates original list, default index in the pop method is -1 (the last item)
[1,2,3].pop(1) # 2 --> mutates original list
[1,2,3].remove(2)# None --> [1,3] Removes first occurrence of item or raises ValueError.
[1,2,3].clear() # None --> mutates original list and removes all items: []
del [1,2,3][0] #
# Ordering
[1,2,5,3].sort() # None --> Mutates list to [1, 2, 3, 5]
[1,2,5,3].sort(reverse=True) # None --> Mutates list to [5, 3, 2, 1]
[1,2,5,3].reverse() # None --> Mutates list to [3, 5, 2, 1]
newls = sorted([1,2,5,3]) # [1, 2, 3, 5] --> new list created
newls = list(reversed([1,2,5,3])) # [3, 5, 2, 1] --> reversed() returns an iterator
# Advanced Ordering
sorted_by_second = sorted(['hi','you','man'], key=lambda el: el[1]) # ['man', 'hi', 'you']
sorted_by_key = sorted([
{'name': 'Bina', 'age': 30},
{'name':'Andy', 'age': 18},
{'name': 'Zoey', 'age': 55}
],
key=lambda el: (el['name']))# [{'name': 'Andy', 'age': 18}, {'name': 'Bina', 'age': 30}, {'name': 'Zoey', 'age': 55}]
# Useful operations
1 in [1,2,5,3] # True
min([1,2,3,4,5])# 1
max([1,2,3,4,5])# 5
sum([1,2,3,4,5])# 15
15
# Get First and Last element of a list
mList = [63, 21, 30, 14, 35, 26, 77, 18, 49, 10]
first, *x, last = mList
print(first) #63
print(last) #10
63
10
# List Comprehensions
# new_list[<action> for <item> in <iterator> if <some condition>]
a = [i for i in 'hello'] # ['h', 'e', 'l', 'l', '0']
b = [i*2 for i in [1,2,3]] # [2, 4, 6]
c = [i for i in range(0,10) if i % 2 == 0]# [0, 2, 4, 6, 8]
# Advanced Functions
list_of_chars = list('Helloooo') # ['H', 'e', 'l', 'l', 'o', 'o', 'o', 'o']
sum_of_elements = sum([1,2,3,4,5]) # 15
element_sum = [sum(pair) for pair in zip([1,2,3],[4,5,6])] # [5, 7, 9]
# Read line of a file into a list
with open("myfile.txt") as f:
lines = [line.strip() for line in f]
1.1.1.2.5. Dictionaries#
my_dict = {'name': 'Andrei Neagoie', 'age': 30, 'magic_power': False}
my_dict['name'] # Andrei Neagoie
len(my_dict) # 3
list(my_dict.keys()) # ['name', 'age', 'magic_power']
list(my_dict.values()) # ['Andrei Neagoie', 30, False]
list(my_dict.items()) # [('name', 'Andrei Neagoie'), ('age', 30), ('magic_power', False)]
my_dict['favourite_snack'] = 'Grapes'# {'name': 'Andrei Neagoie', 'age': 30, 'magic_power': False, 'favourite_snack': 'Grapes'}
my_dict.get('age') # 30 --> Returns None if key does not exist.
my_dict.get('ages', 0 ) # 0 --> Returns default (2nd param) if key is not found
0
#Remove key
del my_dict['name']
my_dict.pop('age')
new_dict = my_dict.pop('name', None)
# update
my_dict.update({'cool': True}) # {'name': 'Andrei Neagoie', 'age': 30, 'magic_power': False, 'favourite_snack': 'Grapes', 'cool': True}
{**my_dict, **{'cool': True} } # {'name': 'Andrei Neagoie', 'age': 30, 'magic_power': False, 'favourite_snack': 'Grapes', 'cool': True}
{'magic_power': False, 'favourite_snack': 'Grapes', 'cool': True}
# create dict
new_dict = dict([['name','Andrei'],['age',32],['magic_power',False]]) # Creates a dict from collection of key-value pairs.
new_dict = dict(zip(['name','age','magic_power'],['Andrei',32, False]))# Creates a dict from two collections.
# Dictionary Comprehension
{key: value for key, value in new_dict.items() if key == 'age' or key == 'name'} # {'name': 'Andrei', 'age': 32} --> Filter dict by keys
{'name': 'Andrei', 'age': 32}
1.1.1.2.6. Tuples#
Like lists, but they are used for immutable things (that don’t change)
my_tuple = ('apple','grapes','mango', 'grapes')
apple, grapes, mango, grapes = my_tuple# Tuple unpacking
len(my_tuple) # 4
my_tuple[2] # mango
my_tuple[-1] # 'grapes'
'grapes'
# Immutability
my_tuple[1] = 'donuts' # TypeError
my_tuple.append('candy')# AttributeError
# Methods
my_tuple.index('grapes') # 1
my_tuple.count('grapes') # 2
2
# Zip
list(zip([1,2,3], [4,5,6])) # [(1, 4), (2, 5), (3, 6)]
# unzip
z = [(1, 2), (3, 4), (5, 6), (7, 8)] # Some output of zip() function
unzip = lambda z: list(zip(*z))
unzip(z)
[(1, 3, 5, 7), (2, 4, 6, 8)]
1.1.1.2.7. Sets#
Unordered collection of unique elements.
my_set = set()
my_set.add(1) # {1}
my_set.add(100)# {1, 100}
my_set.add(100)# {1, 100} --> no duplicates!
new_list = [1,2,3,3,3,4,4,5,6,1]
set(new_list) # {1, 2, 3, 4, 5, 6}
my_set.remove(100) # {1} --> Raises KeyError if element not found
my_set.discard(100) # {1} --> Doesn't raise an error if element not found
my_set.clear() # {}
new_set = {1,2,3}.copy()# {1,2,3}
set1 = {1,2,3}
set2 = {3,4,5}
set3 = set1.union(set2) # {1,2,3,4,5}
set4 = set1.intersection(set2) # {3}
set5 = set1.difference(set2) # {1, 2}
set6 = set1.symmetric_difference(set2)# {1, 2, 4, 5}
set1.issubset(set2) # False
set1.issuperset(set2) # False
set1.isdisjoint(set2) # False --> return True if two sets have a null intersection.
False
1.1.1.3. Functions#
1.1.1.3.1. def#
*args
and **kwargs
def some_func(*args, **kwargs):
pass
args = (1, 2)
kwargs = {'x': 3, 'y': 4, 'z': 5}
some_func(*args, **kwargs) # same as some_func(1, 2, x=3, y=4, z=5)
def f(*args):
# f(1, 2, 3)
pass
def f(x, *args):
# f(1, 2, 3)
pass
def f(*args, z):
# f(1, 2, z=3)
pass
def f(x, *args, z):
# f(1, 2, z=3)
pass
def f(**kwargs):
# f(x=1, y=2, z=3)
pass
def f(x, **kwargs):
# f(x=1, y=2, z=3) | f(1, y=2, z=3)
pass
def f(*args, **kwargs):
# f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
pass
def f(x, *args, **kwargs):
# f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
pass
def f(*args, y, **kwargs):
# f(x=1, y=2, z=3) | f(1, y=2, z=3)
pass
def f(x, *args, z, **kwargs):
# f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3)
pass
def add(*a):
return sum(a)
add(1, 2, 3) # 6
6
Other Uses of *
[*[1,2,3], *[4]] # [1, 2, 3, 4]
{*[1,2,3], *[4]} # {1, 2, 3, 4}
(*[1,2,3], *[4]) # (1, 2, 3, 4)
{**{'a': 1, 'b': 2}, **{'c': 3}}# {'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2, 'c': 3}
1.1.1.3.2. lambda#
# lambda: <return_value>
# lambda <argument1>, <argument2>: <return_value>
# Factorial
from functools import reduce
n = 3
factorial = reduce(lambda x, y: x*y, range(1, n+1))
print(factorial) #6
6
# Fibonacci
fib = lambda n : n if n <= 1 else fib(n-1) + fib(n-2)
result = fib(10)
print(result) #55
55
1.1.1.4. Advanced Python#
1.1.1.4.1. Comprehensions#
_list = [i+1 for i in range(10)] # [1, 2, ..., 10]
_set = {i for i in range(10) if i > 5} # {6, 7, 8, 9}
_iter = (i+5 for i in range(10)) # (5, 6, ..., 14)
_dict = {i: i*2 for i in range(10)} # {0: 0, 1: 2, ..., 9: 18}
[a if a else 'zero' for a in [0, 1, 0, 3]] # ['zero', 1, 'zero', 3]
['zero', 1, 'zero', 3]
output = [i+j for i in range(3) for j in range(3)] # [0, 1, 2, 1, 2, 3, 2, 3, 4]
# Is the same as:
output = []
for i in range(3):
for j in range(3):
output.append(i+j)
1.1.1.4.2. Map Filter Reduce#
from functools import reduce
list(map(lambda x: x + 1, range(10))) # [1, 2, 3, 4, 5, 6, 7, 8, 9,10]
list(filter(lambda x: x > 5, range(10))) # (6, 7, 8, 9)
reduce(lambda acc, x: acc + x, range(10)) # 45
45
1.1.1.4.3. Any All#
any([False, True, False])# True if at least one item in collection is truthy, False if empty.
all([True,1,3,True]) # True if all items in collection are true
True
1.1.1.4.4. Generators#
def count(start, step):
while True:
yield start
start += step
1.1.1.4.5. Exceptions#
try:
5/0
except ZeroDivisionError:
print("No division by zero!")
No division by zero!
while True:
try:
x = int(input('Enter your age: '))
except ValueError:
print('Oops! That was no valid number. Try again...')
else: # code that depends on the try block running successfully should be placed in the else block.
print('Carry on!')
break
Carry on!
1.1.1.4.6. File IO#
‘r’ - Read (default).
‘w’ - Write (truncate).
‘x’ - Write or fail if the file already exists.
‘a’ - Append.
‘w+’ - Read and write (truncate).
‘r+’ - Read and write from the start.
‘a+’ - Read and write from the end.
‘t’ - Text mode (default).
‘b’ - Binary mode.
1.1.1.4.7. Regex#
import re
<str> = re.sub(<regex>, new, text, count=0) # Substitutes all occurrences.
<list> = re.findall(<regex>, text) # Returns all occurrences.
<list> = re.split(<regex>, text, maxsplit=0) # Use brackets in regex to keep the matches.
<Match> = re.search(<regex>, text) # Searches for first occurrence of pattern.
<Match> = re.match(<regex>, text) # Searches only at the beginning of the text.
1.1.1.5. Multi-threading#
# -*- coding: utf-8 -*-
import random
def run_operation(
num_op, matrix_size=128, kernel_size=3, float_from=-1.0, float_to=1.0
):
for _ in range(num_op):
op = Operation(matrix_size, kernel_size, float_from, float_to)
op()
pass
class Operation(object):
def __init__(self, matrix_size=128, kernel_size=3, float_from=-1.0, float_to=1.0):
self.matrix_size = matrix_size
self.kernel_size = kernel_size
self.float_from = float_from
self.float_to = float_to
assert self.matrix_size >= 3
assert self.kernel_size >= 3
assert self.matrix_size >= self.kernel_size
pass
def _init_matrix(self, size):
matrix = []
for r in range(size):
one_row = []
for c in range(size):
one_row.append(random.uniform(self.float_from, self.float_to))
matrix.append(one_row)
return matrix
def __call__(self):
self.matrix = self._init_matrix(size=self.matrix_size)
self.kernel = self._init_matrix(size=self.kernel_size)
nloop = self.matrix_size - self.kernel_size + 1
self.result = self._init_matrix(size=nloop)
for my in range(nloop):
for mx in range(nloop):
for ky in range(self.kernel_size):
for kx in range(self.kernel_size):
kernel_val = self.kernel[ky][kx]
matrix_val = self.matrix[my + ky][mx + kx]
self.result[my][mx] = matrix_val * kernel_val
return True
# Threading
import time
import threading
class MultiThread(object):
def __init__(self, num_thread=4, num_op=100):
self.num_thread = num_thread
self.num_op = num_op
assert self.num_thread > 0
assert self.num_op > 0
pass
def __call__(self):
thread_list = []
for _ in range(self.num_thread):
t = threading.Thread(target=run_operation, args=(self.num_op,))
t.start()
thread_list.append(t)
for _ in range(len(thread_list)):
t = thread_list[_]
t.join()
pass
def main_threading(num_cpus=4, num_ops=10):
tstart = time.time()
multi = MultiThread(num_thread=num_cpus, num_op=num_ops)
multi()
tend = time.time()
print("Time for running %d threads (%d ops) is %.2f seconds" % (num_cpus, num_ops, tend-tstart))
main_threading()
Time for running 4 threads (10 ops) is 2.42 seconds
threading.active_count()
6
# processing
import time
from multiprocessing import Process
class MultiProcess(object):
def __init__(self, num_process=4, num_op=100):
self.num_process = num_process
self.num_op = num_op
assert self.num_process > 0
assert self.num_op > 0
pass
def __call__(self):
process_list = []
for _ in range(self.num_process):
p = Process(target=run_operation, args=(self.num_op,))
p.start()
process_list.append(p)
for _ in range(len(process_list)):
p = process_list[_]
p.join()
pass
def main_processing(num_cpus=4, num_ops=10):
tstart = time.time()
multi = MultiProcess(num_process=num_cpus, num_op=num_ops)
multi()
tend = time.time()
print("Time for running %d processes (%d ops) is %.2f seconds" % (num_cpus, num_ops, tend-tstart))
main_processing()
Time for running 4 processes (10 ops) is 0.07 seconds
https://codelearn.io/sharing/lap-trinh-da-tien-trinh
Nên chạy threading khi các luồng cần dùng chung tham số, còn processing khi các tiến trình chạy độc lập với nhau
import multiprocessing
print("Số lượng cpu : ", multiprocessing.cpu_count())
Số lượng cpu : 16
1.1.1.5.1. MultiThreading#
1.1.1.5.1.1. Kế thừa class Thread#
các threads là bất đồng bộ (asynchronous). Hai threads chạy độc lập với nhau mà không theo thứ tự.
import threading
import time
class FirstThread(threading.Thread):
def __init__(self, thread_id, thread_name, counter):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.thread_name = thread_name
self.counter = counter
# sửa lại hàm run để chạy operator
def run(self):
print("Start thread {}!".format(self.thread_name))
while (self.counter):
time.sleep(0.01)
print("{} : {}".format(self.thread_name, self.counter))
self.counter -= 1
print("End thread {}".format(self.thread_name))
thread1 = FirstThread(1, "khanh thread", 5)
thread2 = FirstThread(2, "ai thread", 5)
thread1.start()
thread2.start()
Start thread khanh thread!
Start thread ai thread!
khanh thread : 5
ai thread : 5
khanh thread : 4
ai thread : 4
khanh thread : 3
ai thread : 3
khanh thread : 2
ai thread : 2
khanh thread : 1
End thread khanh thread
ai thread : 1
End thread ai thread
1.1.1.5.1.2. Cơ chế Thread Lock#
Chúng ta có thể đồng bộ (synchronous) các thread. Tức là cho phép một thread chạy xong thì thread khác mới được phép chạy bằng cách sử dụng
Thread Lock
trong python.Trong hàm
run()
của thread thì chỉ cần thêm hàmthread.acquire()
vàthread.release()
vào đầu và cuối hàm thì luồng sẽ được locking cho đến khi thread chạy xong thì thread khác mới được xử lý tiếp
import threading
import time
class FirstThread(threading.Thread):
def __init__(self, thread_id, thread_name, counter):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.thread_name = thread_name
self.counter = counter
# sửa lại hàm run để chạy operator
def run(self):
threadLock.acquire() # mở luồng lock thread - ko còn chạy multithreading nữa
print("Start thread {}!".format(self.thread_name))
while (self.counter):
time.sleep(0.01)
print("{} : {}".format(self.thread_name, self.counter))
self.counter -= 1
print("End thread {}".format(self.thread_name))
threadLock.release() # đóng lại luồng lock thread
threadLock = threading.Lock()
thread1 = FirstThread(1, "khanh thread", 5)
thread2 = FirstThread(2, "ai thread", 5)
thread1.start()
thread2.start()
threads = [thread1, thread2]
for t in threads:
t.join()
Start thread khanh thread!
khanh thread : 5
khanh thread : 4
khanh thread : 3
khanh thread : 2
khanh thread : 1
End thread khanh thread
Start thread ai thread!
ai thread : 5
ai thread : 4
ai thread : 3
ai thread : 2
ai thread : 1
End thread ai thread
1.1.1.5.2. Processing#
1.1.1.5.2.1. Khởi tạo#
from multiprocessing import Process
import time
def _counter(counter, process_name):
while (counter):
time.sleep(0.01)
print("{}: {}".format(process_name, counter))
counter -= 1
def operator():
counter = 5
exec1 = Process(target=_counter, args=(counter, "khanh thread")) # pass counter and thread_name into method _counter
exec2 = Process(target=_counter, args=(counter, "ai thread"))
exec1.start()
exec2.start()
execs = [exec1,exec2]
for exec in execs:
# lệnh join() để đảm bảo main process hoàn thành sau cùng sau khi các child process khác kết thúc.
exec.join()
operator()
1.1.1.5.2.2. process lock#
# Sử dụng lock để chạy đồng bộ, tức là process chạy không song song mà chạy lần lượt
from multiprocessing import Process, Lock
import time
def _counter_lock(counter, process_name, lock):
lock.acquire()
while (counter):
time.sleep(0.01)
print("{}: {}".format(process_name, counter))
counter -= 1
lock.release()
counter = 5
lock = Lock()
exec1 = Process(target=_counter_lock, args=(counter, "khanh thread", lock)) # pass counter and thread_name into method _counter
exec2 = Process(target=_counter_lock, args=(counter, "ai thread", lock))
execs = [exec1, exec2]
for exec in execs:
exec.start()
execs = [exec1,exec2]
for exec in execs:
# lệnh join() để đảm bảo main process hoàn thành sau cùng sau khi các child process khác kết thúc.
exec.join()
1.1.1.5.3. Pool trong multiprocessing#
Trong python chúng ta có thể sử dụng pool để tận dụng được các tính toán song song trên nhiều process một lúc. Cơ chế của pool đã loại bỏ hạn chế của GIL trong python, cho phép nhiều luồng hoạt động đồng thời và giúp đẩy nhanh quá trình tính toán.
Trong Pool chúng ta có thể khai báo nhiều workers cùng thực hiện chương trình. Các chương trình có thể thực hiện một cách bất đồng bộ thông qua hàm apply_async()
. Tức là cho phép thực hiện song song nhiều method trên các workers. Đồng thời apply_async()
cũng cho phép đưa vào các hàm callback
để xử lý giữa liệu sau cùng.
# sử dụng 5 workers để tính toán bất đồng bộ bình phương của các số trong phạm vi 20. Kết quả sau khi tính sẽ được lưu vào một list
import multiprocessing as mp
import time
def _square(x):
return x*x
def log_result(result):
# Hàm được gọi bất kỳ khi nào _square(i) trả ra kết quả.
# result_list được thực hiện trên main process, khong phải pool workers.
result_list.append(result)
def apply_async_with_callback():
pool = mp.Pool(processes=5)
for i in range(20):
pool.apply_async(_square, args = (i, ), callback = log_result)
pool.close()
pool.join()
print(result_list)
if __name__ == '__main__':
result_list = []
apply_async_with_callback()
1.1.1.5.4. Process Pool với concurrent.futures#
from concurrent.futures import ProcessPoolExecutor
from time import sleep
import timeit
def _counter(counter, task_name):
print("Start process {}!".format(task_name))
while (counter):
print("{} : {}".format(task_name, counter))
counter -= 1
print("End process {}!".format(task_name))
return "Completed {}!".format(task_name)
def _submit_process():
executor = ProcessPoolExecutor(max_workers=5)
future = executor.submit(_counter, 10, "task1") # Tham số truyền vào là tên hàm và các đối số của hàm
print('State of future: ', future.done()) # để kiểm tra trạng thái của task, task chưa hoàn thành nên future.done() == False
print('futre result: ', future.result()) # thường được dùng để kiểm tra kết quả sau khi task cuối cùng trong process pool đã thực thi xong
print('State of future: ', future.done()) # task đã hoàn thành nên future.done() == True
if __name__ =="__main__":
_submit_process()
# sử dụng hàm map để truyền theo từng phần tử của list
from concurrent.futures import ProcessPoolExecutor
from concurrent.futures import as_completed
x1s = [5, 10, 20, 35]
x2s = [15, 20, 30, 55]
y1s = [5, 10, 10, 15]
y2s = [15, 20, 20, 35]
def _bbox(x1, x2, y1, y2):
w = x2-x1
h = y2-y1
area = w*h
return area
if __name__ =="__main__":
with ProcessPoolExecutor(max_workers = 5) as executor:
results = executor.map(_bbox, x1s, x2s, y1s, y2s)
for result in results:
print(result)
1.1.1.5.5. Thread Pool#
from concurrent.futures import ThreadPoolExecutor
from time import sleep
def _counter(counter, task_name):
print("Start process {}!".format(task_name))
while (counter):
print("{} : {}".format(task_name, counter))
counter -= 1
print("End process {}!".format(task_name))
return "Completed {}!".format(task_name)
def _submit_thread():
executor = ThreadPoolExecutor(max_workers=5)
future = executor.submit(_counter, 10, "task1")
print('State of future: ', future.done())
print('futre result: ', future.result())
print('State of future: ', future.done())
if __name__ =="__main__":
_submit_thread()
1.1.1.5.6. so sánh process pool và thread pool#
Chúng ta đã biết rằng khi sử dụng threads thì sẽ có lợi về I/O vì các threads có thể chia sẻ data qua lại lẫn nhau. Còn giữa các processes thì data được sử dụng hoàn toàn độc lập nên không có lợi về I/O. Tuy nhiên khi sử dụng process thì chúng ta sẽ được allocate về CPU, Memomory,… nên lời khuyên là nếu task của bạn gặp phải giới hạn về I/O bound thì nên sử dụng thread pool và giới hạn về CPUs bound thì nên sử dụng process pool.
CPU Bound
là những chương trình bị giới hạn chủ yếu bởi tốc độ của CPU: việc tính toán small set of number,…–>Nên dùng CPU pool
I/O Bound
means the rate at which a process progresses is limited by the speed of the I/O subsystem. A task that processes data from disk, for example, counting the number of lines in a file is likely to be I/O bound. –>Nên dùng thread pool
Memory bound
means the rate at which a process progresses is limited by the amount memory available and the speed of that memory access. A task that processes large amounts of in memory data, for example : nhân matrix lớnCache bound
means the rate at which a process progress is limited by the amount and speed of the cache available. A task that simply processes more data than fits in the cache will be cache bound.
Nên dùng
process pool
khi tính toánNên dùng
thread pool
khi đọc ghi
1.1.1.5.7. Queue#
Khi chạy một
process
trênmultiple-threads
, queue thường được sử dụng để làm nơi lưu trữ dữ liệu chung giữa cácthreads
với nhau.Queue
tuong tự nhưlist
nhưng có tốc độ truy xuất nhanh hơn, phù hợp với dữ liệu có lượng phần tử lớn -FIFO
: phần tửput
đầu tiên sẽget
đầu tiên (First in first out) -LIFO
: phần tửput
cuối cùng sẽget
đầu tiên (last in first out)
list = [14,20,34,24,15,16,27,38,19]
import queue
# create fifo queue
fifo_q = queue.Queue()
fifo_q_sizelist = queue.Queue(len(list)) # set max size = len(list)
# put element to queue:
for item in list:
fifo_q.put(item)
fifo_q_sizelist.put(item)
# return size queue
print('size queue', fifo_q.qsize())
# check queue is full or đã get phần tử
print('fifo_q_sizelist queue is full: ', fifo_q_sizelist.full())
# check queue is empty or not
print('queue is empty: ', fifo_q.empty())
# get phần tử trong queue đồng thời xóa phần tử đó
print('The 1st item in queue: ',fifo_q.get())
print('Size or queue after get 1 item: ',fifo_q.qsize())
# loop for queue:
while not fifo_q.empty():
print('the next item is',fifo_q.get())
size queue 9
fifo_q_sizelist queue is full: True
queue is empty: False
The 1st item in queue: 14
Size or queue after get 1 item: 8
the next item is 20
the next item is 34
the next item is 24
the next item is 15
the next item is 16
the next item is 27
the next item is 38
the next item is 19
lifo_q = queue.LifoQueue()
from concurrent.futures import ThreadPoolExecutor
import queue
def _sum_queue(name, work_queue):
sum = 0
while not work_queue.empty():
print(f"Task {name} running")
count = work_queue.get()
sum += count
print(f"Task {name} total: {sum}")
return sum
def task(name, work_queue):
if work_queue.empty():
print(f"Task {name} nothing to do")
else:
print("Start ThreadPoolExecutor!")
with ThreadPoolExecutor(max_workers = 5) as executor:
print("Submit task!")
future = executor.submit(_sum_queue, name, work_queue)
sum = future.result()
return sum
# Create the queue of work
work_queue = queue.Queue()
# Put some work in the queue
for work in [15, 10, 5, 2]:
work_queue.put(work)
# Create some synchronous tasks
tasks = [("one", work_queue), ("two", work_queue)]
# Run the tasks
for n, q in tasks:
print(task(n, q))
Start ThreadPoolExecutor!
Submit task!
Task one running
Task one running
Task one running
Task one running
Task one total: 32
32
Task two nothing to do
None
Trong ví dụ trên giải sử chúng ta có hai threads hoạt động một cách synchronous là one và two. Hai threads này sử dụng chung một nguồn dữ liệu là work_queue. Khi thread one chạy xong thì toàn bộ các phần tử của queue đã được trích xuất xong nên ở thread two chúng ta không có gì để chạy tiếp.
import time
import requests
import concurrent.futures
def get_wiki_page_existence(wiki_page_url, timeout=10):
response = requests.get(url=wiki_page_url, timeout=timeout)
page_status = "unknown"
if response.status_code == 200:
page_status = "exists"
elif response.status_code == 404:
page_status = "does not exist"
return wiki_page_url + " - " + page_status
def func_with_multithread():
print("Running with multi-threads:")
threaded_start = time.time()
wiki_page_urls = ["https://en.wikipedia.org/wiki/" + str(i) for i in range(25)]
with concurrent.futures.ThreadPoolExecutor(2) as executor:
futures = []
for url in wiki_page_urls:
futures.append(
executor.submit(
get_wiki_page_existence, wiki_page_url=url, timeout=10
)
)
for future in concurrent.futures.as_completed(futures):
try:
print(future.result(),end= "\r")
except requests.ConnectTimeout:
print("ConnectTimeout.")
print("\nThreaded time:", time.time() - threaded_start)
def func_without_multithread():
wiki_page_urls = ["https://en.wikipedia.org/wiki/" + str(i) for i in range(25)]
print("Running without threads:")
without_threads_start = time.time()
for url in wiki_page_urls:
print(get_wiki_page_existence(wiki_page_url=url),end= "\r")
print("\nWithout threads time:", time.time() - without_threads_start)
if __name__ == "__main__":
func_with_multithread()
func_without_multithread()
Running with multi-threads:
https://en.wikipedia.org/wiki/24 - exists
Threaded time: 3.638731002807617
Running without threads:
https://en.wikipedia.org/wiki/24 - exists
Without threads time: 6.373312711715698
import os
import time
import pandas_read_xml as pdx
import pandas as pd
import numpy as np
from tqdm import tqdm
from json import loads, dumps
import concurrent.futures as cf
import queue
from multiprocessing import Process
def write_log(filename,output_path = os.path.join(os.getcwd(),'log.txt')):
with open(output_path, "a", encoding='utf8') as myfile:
myfile.write("\n"+"Loi doc file: " + filename)
def operation(list_files,output_folder):
df = pd.DataFrame()
keys = ['maTKhai', 'pbanTKhaiXML', 'loaiTKhai']
output_path = os.path.join(output_folder,'doc.csv')
for path in tqdm(list_files):
try:
data = loads(dumps(pdx.read_xml(path, encoding='utf8').to_dict()))[
'HSoThueDTu']['0']['HSoKhaiThue']
res = {key: data['TTinChung']['TTinTKhaiThue']
['TKhaiThue'][key] for key in keys}
list_pluc = list(data['PLuc'].keys())
list_pluc.sort()
res['PLUC'] = str(list_pluc)
res['filename'] = path
try:
res['MST'] = data['TTinChung']['TTinTKhaiThue']['NNT']['mst']
except:
res["MST"] = np.nan
df = df.append(pd.DataFrame(res, index=[0]))
# df.to_csv(output_path,sep="|", mode='a', header=not os.path.exists(output_path))
except:
write_log(path)
df.to_csv(output_path,sep="|", mode='a', header=not os.path.exists(output_path))
def without_thread(folder,output_folder):
files = [os.path.join(folder,i) for i in os.listdir(folder)]
operation(files,output_folder)
def getlink(folder):
workqueue = queue.Queue()
workqueue.queue = queue.deque([
os.path.join(folder,i) for i in os.listdir(folder)])
return workqueue
def multiprocessing_run(folder,numBot):
work_queue = [os.path.join(folder,i) for i in os.listdir(folder)]
mofile = [[]]*numBot
while len(work_queue)>0 :
for i in range(numBot):
if len(work_queue)>0:
mofile[i] = mofile[i] + [work_queue.pop()]
# return mofile
jobs = []
for file in mofile:
p = Process(target=operation, args=([file]))
jobs.append(p)
p.start()
for proc in jobs:
proc.join()
def _submit_thread(folder,num_cpus):
work_queue = [os.path.join(folder,i) for i in os.listdir(folder)]
mofile = [[]]*num_cpus
while len(work_queue)>0 :
for i in range(num_cpus):
if len(work_queue)>0:
mofile[i] = mofile[i] + [work_queue.pop()]
output_folder = r'D:\DATA SUPERLAKE\0. RAW DATA\TAX\TAX 2021\tax_code'
with cf.ProcessPoolExecutor(max_workers=num_cpus) as executor:
# futures = pd.DataFrame()
for i in mofile:
executor.submit( operation, i,output_folder)
# for future in cf.as_completed(futures):
# print(future.result(),end='\r')#.to_csv(output_path,sep="|", mode='a', header=not os.path.exists(output_path))
def main():
tstart = time.time()
num_cpus = 10
folder = r'D:\DATA SUPERLAKE\0. RAW DATA\TAX\TAX 2021\tax_code\Phan tich 27052022\sample2 - Copy'
# without_thread(folder,output_path)
# multiprocessing_run(folder,num_cpus)
_submit_thread(folder,num_cpus)
print("Time for running is %.2f seconds" %(time.time()-tstart))
if __name__ == "__main__":
main()
import os
from sqlite3 import Time
import time
import pandas_read_xml as pdx
import pandas as pd
import numpy as np
from tqdm import tqdm
from json import loads, dumps
import concurrent.futures as cf
import queue
from multiprocessing import Process
def write_log(filename,output_path = os.path.join(os.getcwd(),'log.txt')):
with open(output_path, "a", encoding='utf8') as myfile:
myfile.write("\n"+"Loi doc file: " + filename)
def operation(list_files,output_folder):
df = pd.DataFrame()
keys = ['maTKhai', 'pbanTKhaiXML', 'loaiTKhai']
output_path = os.path.join(output_folder,'doc.csv')
# for path in tqdm(list_files):
# for path in list_files:
path = list_files
try:
data = loads(dumps(pdx.read_xml(path, encoding='utf8').to_dict()))[
'HSoThueDTu']['0']['HSoKhaiThue']
res = {key: data['TTinChung']['TTinTKhaiThue']
['TKhaiThue'][key] for key in keys}
list_pluc = list(data['PLuc'].keys())
list_pluc.sort()
res['PLUC'] = str(list_pluc)
res['filename'] = path
try:
res['MST'] = data['TTinChung']['TTinTKhaiThue']['NNT']['mst']
except:
res["MST"] = np.nan
df = df.append(pd.DataFrame(res, index=[0]))
# df.to_csv(output_path,sep="|", mode='a', header=not os.path.exists(output_path))
except:
write_log(path)
df.to_csv(output_path,sep="|", mode='a', header=not os.path.exists(output_path))
def without_thread(folder,output_folder):
files = [os.path.join(folder,i) for i in os.listdir(folder)]
operation(files,output_folder)
def getlink(folder):
workqueue = queue.Queue()
workqueue.queue = queue.deque([
os.path.join(folder,i) for i in os.listdir(folder)])
return workqueue
def multiprocessing_run(folder,numBot):
work_queue = [os.path.join(folder,i) for i in os.listdir(folder)]
mofile = [[]]*numBot
while len(work_queue)>0 :
for i in range(numBot):
if len(work_queue)>0:
mofile[i] = mofile[i] + [work_queue.pop()]
# return mofile
jobs = []
for file in mofile:
p = Process(target=operation, args=([file]))
jobs.append(p)
p.start()
for proc in jobs:
proc.join()
def _submit_thread(folder,num_cpus):
# work_queue = [os.path.join(folder,i) for i in os.listdir(folder)]
# mofile = [[]]*num_cpus
# while len(work_queue)>0 :
# for i in range(num_cpus):
# if len(work_queue)>0:
# mofile[i] = mofile[i] + [work_queue.pop()]
work_queue = getlink(folder)
output_folder = r'D:\DATA SUPERLAKE\0. RAW DATA\TAX\TAX 2021\tax_code'
with cf.ProcessPoolExecutor(max_workers=num_cpus) as executor:
# futures = pd.DataFrame()
# pbar = tqdm(total=work_queue.qsize())
size = work_queue.qsize()
def generator():
while not work_queue.empty():
# for i in mofile:
yield work_queue.get()
for i in tqdm(generator(),total = size):
executor.submit(operation, i ,output_folder)
# pbar.update()
# pbar.close()
# for future in cf.as_completed(futures):
# print(future.result(),end='\r')#.to_csv(output_path,sep="|", mode='a', header=not os.path.exists(output_path))
def main():
tstart = time.time()
num_cpus = 10
folder = r'D:\DATA SUPERLAKE\0. RAW DATA\TAX\TAX 2021\tax_code\Phan tich 27052022\sample2 - Copy'
# without_thread(folder,output_path)
# multiprocessing_run(folder,num_cpus)
_submit_thread(folder,num_cpus)
print("Time for running is %.2f seconds" %(time.time()-tstart))
if __name__ == "__main__":
main()
1.1.1.6. Decorators#
Mở rộng chức năng của hàm khác
Nhận hàm dưới decorator làm hàm tham số, mở rộng chức năm của hàm tham số mà không cần chỉnh sửa hàm tham số
áp dụng cho ghi log, khởi tạo tài nguyên trước khi chạy function, hoặc dọn dẹp sau khi chạy, đo time,…
from functools import wraps
def debug(func):
@wraps(func)
def out(*args, **kwargs):
print(func.__name__)
return func(*args, **kwargs)
return out
@debug
def add(x, y):
return x + y
1.1.1.6.1. Cơ bản hàm decorator#
def display_decorator(func):
def wrapper(str): # closure
# logic trước khi chạy hàm func
print(f'Log: The function {func.__name__} is executing ...')
func(str)
# logic sau khi chạy hàm func
print('Log: Execution completed.\n')
return wrapper
#bản chất
def display(str):
print(str)
display = display_decorator(display)
display('Hello world')
# tương đương
# truyền hàm say_hello làm tham số cho hàm display_decorator
@display_decorator
def say_hello(str):
print(str)
say_hello('Hello, Donald')
def makebold(f):
return lambda: "<b>" + f() + "</b>"
def makeitalic(f):
return lambda: "<i>" + f() + "</i>"
@makebold
@makeitalic
def say():
return "Hello"
print(say()) #// kết quả là <b><i>Hello</i></b>
1.1.1.6.2. Lớp decorator#
Xử lý cho hàm deco phức tạp
class decoclass(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# logic trước khi gọi hàm f
print('decorator initialised')
self.f(*args, **kwargs)
print('decorator terminated')
# logic sau khi gọi hàm f
@decoclass
def hello(name,name2):
print(f'Hello, {name}. Welcome to {name2} heaven!')
hello('XXX','YYY')
Inside decorator
Inside inner function
I like cafedevn
Inside actual function
# tạo hàm decorator tính time
from datetime import datetime
from time import sleep
return 0
return 1
return 2
runtime = 0:00:03.016835
# tạo hàm decorator tính time
from datetime import datetime
from time import sleep
class decoclass(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# logic trước khi gọi hàm f
print('decorator initialised')
self.f(*args, **kwargs)
print('decorator terminated')
# logic sau khi gọi hàm f
def runtime(self,*arg):
start = datetime.now()
decoclass(self.f(*args))
print("runtime = ",datetime.now()-start)
@decoclass.runtime
def print_second1():
for i in range(3):
print('return ', i)
sleep(1)
print_second1()
return 0
return 1
return 2
runtime hello = 0:00:03.025241
import math
import timeit
# class runtime(object):
# def __init__(self, f):
# self.f = f
# def __call__(self, *args, **kwargs):
# start = timeit.default_timer()
# res = self.f(*args, **kwargs)
# stop = timeit.default_timer()
# print(stop - start, " (seconds)")
# return res
def runtime(func):
def func_wrapper(x):
start = timeit.default_timer()
res = func(x)
stop = timeit.default_timer()
print(stop - start, "(seconds)")
return res
return func_wrapper
def is_prime(number):
if number == 2:
return True
if number <= 1 or not number % 2:
return False
max_range = int(math.sqrt(number)) + 1
for div in range(3, max_range, 2):
if not number % div:
return False
return True
@runtime
def run_program(max_number):
for number in range(max_number):
is_prime(number)
run_program(1000000)
I like Geeksforgeeks
Summation of values - 27
Geeks
for
Geeks
def decorator(*args, **kwargs):
print("Inside decorator")
def inner(func):
# code functionality here
print("Inside inner function")
print("I like", kwargs['like'])
func()
# reurning inner function
return inner
@decorator(like = "cafedevn")
def my_func():
print("Inside actual function")
def runtime(f, *args, **kwargs):
start = datetime.now()
f()
print("runtime = ",datetime.now()-start)
@runtime
def print_second():
for i in range(3):
print('return ', i)
sleep(1)
print_second
from time import sleep
from datetime import datetime
import functools
def runtime(string):
def decorator_log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = datetime.now()
result = func(*args, **kwargs)
print(f"runtime {string} = ",datetime.now()-start)
return result
return wrapper
return decorator_log
@runtime (string = "hello")
def print_second():
for i in range(3):
print('return ', i)
sleep(1)
print_second()
Khong
Dat
Khong-Dat@team.com
Khong Dat
# Python code to illustrate
# Decorators with parameters in Python
def decorator_func(x, y):
def Inner(func):
def wrapper(*args, **kwargs):
print("I like Geeksforgeeks")
print("Summation of values - {}".format(x+y) )
func(*args, **kwargs)
return wrapper
return Inner
# Not using decorator
def my_fun(*args):
for ele in args:
print(ele)
# another way of using decorators
decorator_func(12, 15)(my_fun)('Geeks', 'for', 'Geeks')
An
Viet
Khong-Dat@team.com
An Viet
1.1.1.6.3. Special decorator#
@property : biến method thành attribute
@"method".setter : gán value cho method(có property), có thể sửa các attribute ban đầu có liên quan
@"method".deleter : xóa value cho method(có property), có thể xóa các attribute ban đầu có liên quan
class Teamm:
def __init__(self, ho, ten):
self.ho = ho
self.ten = ten
self.email = ho + "-" + ten + "@team.com"
def ho_va_ten(self): # regular method
return "{} {}".format(self.ho, self.ten)
ktd = Teamm("Khong", "Dat")
'Khanh'
print(ktd.ho)
print(ktd.ten)
print(ktd.email)
print(ktd.ho_va_ten())
None
# thử modify attribute
ktd.ho = "An"
ktd.ten = "Viet"
print(ktd.ho)
print(ktd.ten)
print(ktd.email) # email ko bị đổi theo
print(ktd.ho_va_ten())
class Teamm:
def __init__(self, ho, ten):
self.ho = ho
self.ten = ten
@property # bien method thanh attribute
def email(self): # regular method
return self.ho + "_" + self.ten + "@team.com"
@property # bien method thanh attribute
def ho_va_ten(self): # regular method
return "{} {}".format(self.ho, self.ten)
@ho_va_ten.setter # method ho_va_ten có thể gán giá trị và sửa giá trị attribute ho, ten
def ho_va_ten(self, ten_moi):
ho_moi, ten_moi = ten_moi.split(" ")
self.ho = ho_moi
self.ten = ten_moi
@ho_va_ten.deleter # method ho_va_ten có thể gán giá trị và sửa giá trị attribute ho, ten
def ho_va_ten(self):
self.ho = None
self.ten = None
ktd = Teamm("Khong", "Dat")
ktd.ho_va_ten = "An Khanh"
ktd.ten
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
Wall time: 0 ns
del ktd.ho_va_ten
print(ktd.ten)
120
1.1.1.6.4. Decorator memorization#
Ghi nhớ là một kỹ thuật ghi lại các kết quả trung gian để chúng có thể được sử dụng nhằm tránh tính toán lặp lại và tăng tốc độ cho chương trình. Ghi nhớ có thể được sử dụng để tối ưu hóa các chương trình sử dụng đệ quy. Trong Python, ghi nhớ có thể được thực hiện với sự trợ giúp của Decorator
%%time
# Bai toan tinh giai thua
# Simple recursive program to find factorial
def facto(num):
if num == 1:
return 1
else:
return num * facto(num-1)
print(facto(100))
# Chương trình như trên có thể sử dụng Decorator để giải quyết như sau:
# Factorial program with memoization using
# decorators.
# A decorator function for function 'f' passed
# as parameter
"""
Có thể giải thích ví dụ trên như sau:
1. Một hàm có tên memoize_factoria l đã được định nghĩa. Mục đích chính của nó là lưu trữ các kết quả trung gian trong biến được gọi là bộ nhớ.
2. Chức năng thứ hai được gọi là facto là chức năng để tính toán giai thừa. Nó đã được chú thích bởi một decorator (hàm memoize_factorial).
Facto có quyền truy cập vào biến bộ nhớ.
3. Khi facto (5) được gọi, các phép toán đệ quy diễn ra ngoài việc lưu trữ các kết quả trung gian.
Mỗi khi một phép tính cần được thực hiện, nó sẽ được kiểm tra xem kết quả có sẵn trong bộ nhớ hay không .
Nếu có, thì nó được sử dụng, ngược lại, giá trị được tính toán và được lưu trữ trong bộ nhớ .
"""
def memoize_factorial(f):
memory = {}
# This inner function has access to memory
# and 'f'
def inner(num):
if num not in memory:
memory[num] = f(num)
return memory[num]
return inner
@memoize_factorial
def facto(num):
if num == 1:
return 1
else:
return num * facto(num-1)
print(facto(5))
1.1.1.6.5. Gỡ lỗi bằng decorator#
# Bạn có thể sử dụng trình Decorator trong Python để gỡ lỗi bằng cách dùng functools.wraps() trong thư viện tiêu chuẩn của Python.
# Ví dụ như sau:
# importing the module
import functools
# decorator
def make_geek_happy(func):
@functools.wraps(func)
def wrapper():
neutral_message = func()
happy_message = neutral_message + " You are happy!"
return happy_message
return wrapper
def speak():
"""Returns a neutral message"""
return "Hi!"
positive_message = make_geek_happy(speak)
print(positive_message()) # Hi! You are happy!
print(speak.__name__)
print(speak.__doc__)
print(positive_message.__name__)
print(positive_message.__doc__)
1.1.1.7. Class#
class Name:
age = 80 # Class Object Attribute
def __init__(self, a):
self.a = a # Object Attribute
@classmethod
def get_class_name(cls):
return cls.__name__
### Khoi tao class
# khoi tao class
class Company:
pass
# khai bao object
hoaphat = Company()
# add attribute
hoaphat.ten = "Hoa Phat"
hoaphat.sector = "Xay Dung"
hoaphat.industry = "San xuat thep"
'Cong ty Hoa Phat'
hoaphat.ten
1.1.1.7.1. Hàm constructor (initialize method)#
# từ khóa self sẽ nhận giá trị chính là đối tượng đã gọi hàm đó
# contructor mo dau va ket thuc bang "__"
class Company:
country = "VietNam" # attribute dac trung cua ca class
def __init__(self, name_para, sector_para, industry_para): # contructor
self.name = "Cong ty " + name_para # attribute dac trung cua mỗi object
self.sector = sector_para
self.industry = industry_para
'VietNam'
# truyen cac argument theo thu tu
hoaphat = Company("Hoa Phat", "Xay Dung", "San xuat thep")
hoaphat.name
tcb = Company("Techcombank", "Tai Chinh", "Ngan hang")
Company.country
tcb.country # su dung chung attribute cua class
{'name': 'Hoa Phat',
'sector': 'Xay Dung',
'industry': 'San xuat thep',
'stt': 1}
1.1.1.7.2. Attribute#
# ham xu dung trong class la method
class Company:
# Attribute dac trung cua class
country = "VietNam"
number_of_company = 0
def __init__(self, name_para, sector_para, industry_para):
self.name = name_para
self.sector = sector_para
self.industry = industry_para
self.stt = Company.number_of_company + 1
Company.number_of_company += 1
def thongtin(self):
return "Cong ty {} lam nganh {}, linh vuc {}".format(
self.name, self.industry, self.sector
)
def thongtinchitiet(self):
return "Cong ty {} lam nganh {}, linh vuc {}, dat nuoc {}".format(
self.name, self.industry, self.sector, Company.country
)
'Cong ty Hoa Phat lam nganh San xuat thep, linh vuc Xay Dung, dat nuoc VietNam'
hpg = Company("Hoa Phat", "Xay Dung", "San xuat thep")
tcb = Company("Techcombank", "Tai Chinh", "Ngan hang")
1
VN
VietNam
# dict attribute
vars(hpg)
2
# list attribute name , include lớp cha
dir(hpg)
VietNam
'VN'
Company.thongtinchitiet(hpg)
print(hpg.stt)
hpg.country = "VN" # sua thuoc tinh cuar object nhung khong lam thay doi thuoc tinh cua class
print(hpg.country)
print(Company.country)
Company.number_of_company
print(Company.country)
Company.country = "VN" # sua thuoc tinh cua Class
tcb.country # thuoc tinh cua object cung bi thay doi theo class
1.1.1.7.3. Method#
Nếu bạn dựng một phương thức cần sử dụng đối tượng đó thì dùng regular method
Nếu bạn cần dùng class thì dùng class method
Trường hợp còn lại (tức là không dùng gì) thì dùng static method
# ham xu dung trong class la method
class Company:
# Attribute dac trung cua class
country = "VietNam"
number_of_company = 0
def __init__(self, name_para, sector_para, industry_para):
self.name = name_para
self.sector = sector_para
self.industry = industry_para
self.stt = Company.number_of_company + 1
Company.number_of_company += 1
# REGULAR METHOD - hướng đến seft là đối tượng
# regular method luon co it nhat 1 argument self la chinh object
def thongtin(self):
return "Cong ty {} lam nganh {}, linh vuc {}".format(
self.name, self.industry, self.sector
)
def thongtinchitiet(self):
return "Cong ty {} lam nganh {}, linh vuc {}, dat nuoc {}".format(
self.name,
self.industry,
self.sector,
Company.country, # có thể chứa cả attribute đặc trưng của class
)
# CLASS METHOD - hướng đến class
@classmethod
def update_country(cls, country_para): # thay đổi attribute của class
cls.country = country_para
@classmethod
def from_list(cls, lis): # method get object thường bắt đầu bới from
name_para, industry_para, sector_para = lis
return cls(name_para, industry_para, sector_para)
# STATIC METHOD - hàm bình thường mà không động gì đến class hay object
def get_info_class():
print("Đây là Class Company tạo bới KTĐ")
hpg = Company("Hoa Phat", "Xay Dung", "San xuat thep")
tcb = Company("Techcombank", "Tai Chinh", "Ngan hang")
VietNam
VNam
VNam_2
1.1.1.7.3.1. regular method#
hpg.thongtin()
1.1.1.7.3.2. Class method#
# Dùng để update attribute của class
print(Company.country)
# update bang class
Company.update_country("VNam")
print(Company.country)
# update bang object
hpg.update_country("VNam_2")
print(Company.country)
# nhập object cho class
input_obj = ["FPTS", "Ban le", "Cong nghe"]
fpts = Company.from_list(input_obj)
fpts.thongtin()
Đây là Class Company tạo bới KTĐ
fpts.__dict__
1.1.1.7.3.3. Static method#
Company.get_info_class()
Cong ty Techcombank lam nganh Ngan hang, linh vuc Tai Chinh, dat nuoc VietNam
from datetime import date
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# a class method to create a Person object by birth year.
# class method để tạo ra các factory methods. Các factory methods sẽ trả về đối tượng của lớp (tương tự như một hàm constructor) cho các trường hợp sử dụng khác nhau.
@classmethod
def fromBirthYear(cls, name, year):
return cls(name, date.today().year - year)
# a static method to check if a Person is adult or not.
# static methods để tạo ra các hàm tiện ích (utility functions).
@staticmethod
def isAdult(age):
return age > 18
person1 = Person('mayank', 21)
person2 = Person.fromBirthYear('mayank', 1996)
print( person1.age)
print( person2.age )
# print the result
print( Person.isAdult(22))
Day la Techcombank, nganh Tai Chinh
1.1.1.7.3.4. Special Method#
class Company:
# Attribute dac trung cua class
country = "VietNam"
number_of_company = 0
# khai bao thuoc tinh
def __init__(self, name_para, sector_para, industry_para):
self.name = name_para
self.sector = sector_para
self.industry = industry_para
self.stt = Company.number_of_company + 1
Company.number_of_company += 1
# method khi print object
# Nếu không có phương thức __str__ nào được định nghĩa, thì hàm print t (hoặc print str(t)) sẽ sử dụng __repr__
def __str__(self):
return "Day la {}, nganh {}".format(self.name, self.sector)
# method khi goi object (output)
# Nếu không có phương thức __repr__ nào được định nghĩa, thì phương thức mặc định sẽ được sử dụng.
def __repr__(self):
return "Cong ty {} lam nganh {}, linh vuc {}, dat nuoc {}".format(
self.name,
self.industry,
self.sector,
Company.country, # có thể chứa cả attribute đặc trưng của class
)
def __call__(self): # chạy object như hàm khi call ()
print("chạy như hàm...")
def __len__(self):
# print("chạy trên method __len__", len(self.name))
return len(self.name)
def __add__(self, other):
return self.name + "_" + other.name
hpg = Company("Hoa Phat", "Xay Dung", "San xuat thep")
tcb = Company("Techcombank", "Tai Chinh", "Ngan hang")
'Hoa Phat_Techcombank'
tcb # call __str__
'Hoa Phat_Techcombank'
print(tcb) # call __repr__
8
tcb() # method __call__
Day la Hoa Phat, nganh Xay Dung
Company.__add__(hpg, tcb)
hpg + tcb # method __add__
len(hpg)
print(hpg)
3
Cong ty Vinhomes lam nganh Ban le, linh vuc Nha dat
1.1.1.7.4. Kế thừa#
tạo một class mới kế thừa các thuộc tính và phương thức của class khác
class Stock(Company): # kế thừa thuộc tính của Company, có thể kế thừa nhiều class khác bằng cách Stock(X1,X2,...)
country = "VNNNNN" # sửa lại attribute, không dùng cái của class cũ
exchange = "HOSE"
def __init__(
self, name_para, sector_para, industry_para, stock_price_para, volumn_stock_para
):
super().__init__(
name_para, sector_para, industry_para
) # Viết lại nhanh attribute trong init constructor của class cũ
# or cach khac
Company.__init__(self,name_para, sector_para, industry_para)
self.stock_price = stock_price_para # thêm các attribute của class mới
self.volumn_stock = volumn_stock_para
# tạo method mới
def trading_values(self):
return (self.stock_price) * (self.volumn_stock)
# sua lai method cua class ke thua
@classmethod
def from_list(cls, lis): # method get object thường bắt đầu bới from
name_para, industry_para, sector_para, stock_price_para, volumn_stock_para = lis
return cls(
name_para, industry_para, sector_para, stock_price_para, volumn_stock_para
)
Base1
Base2
Derived
Cafedev1 Cafedev2
vhm = Stock.from_list(["Vinhomes", "Nha dat", "Ban le", 120000, 1500000])
# su dung attrubute va method cua class cu
print(vhm.number_of_company)
print(vhm.thongtin())
13
12
11
# use attribute va method rieng
print(vhm.country)
print(vhm.trading_values())
day la 1
day la 2
# đa kế thừa: kế thừa nhiều class cùng 1 lúc
# Python example to show working of multiple
# inheritance
class Base1(object):
def __init__(self):
self.str1 = "Cafedev1"
print("Base1")
class Base2(object):
def __init__(self):
self.str2 = "Cafedev2"
print("Base2")
class Derived(Base1, Base2):
def __init__(self):
# Calling constructors of Base1
# and Base2 classes
Base1.__init__(self)
Base2.__init__(self)
print("Derived")
def printStrs(self):
print(self.str1, self.str2)
ob = Derived()
ob.printStrs()
1.1.1.7.5. Iterators and Generators#
Iterators là các đối tượng mà chúng ta có thể thực hiện các phép lặp/duyệt trên chúng.
– Python sử dụng phương thức __iter__()
để trả về một đối tượng iterator của class
– Đối tượng iterator
sau đó sẽ sử dụng phương thức __next__()
để lấy được next item – phần tử tiếp theo.
– Vòng lặp for sẽ dừng lại khi ngoại lệ StopIteration Exception
được kích hoạt.
class PrintList:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index-= 1
return self.data[self.index] + 10
for i in PrintList([1,2,3]):
print(i)
India 1111
India 2222
India 3333
USA 1111
USA 2222
USA 3333
# Sử dụng generator là một cách khác để tạo ra các iterators
class cophieu():
def __init__(self, data):
self.data = data
self.index = len(data)
def inLanLuot(self):
for i in self.data:
yield 'day la ' + str(i)
for i in cophieu([1,2]).inLanLuot():
print(i)
1.1.1.7.6. Đa hình#
# ghi đè phương thức
class Bird:
def intro(self):
print("There are many types of birds.")
def flight(self):
print("Most of the birds can fly but some cannot.")
class sparrow(Bird):
def flight(self):
print("Sparrows can fly.")
class ostrich(Bird):
def flight(self):
print("Ostriches cannot fly.")
obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()
obj_bird.intro()
obj_bird.flight()
obj_spr.intro()
obj_spr.flight()
obj_ost.intro()
obj_ost.flight()
class India():
def capital(self):
print("India 1111")
def language(self):
print("India 2222")
def type(self):
print("India 3333")
class USA():
def capital(self):
print("USA 1111")
def language(self):
print("USA 2222")
def type(self):
print("USA 3333")
def func(obj):
obj.capital()
obj.language()
obj.type()
obj_ind = India()
obj_usa = USA()
func(obj_ind)
func(obj_usa)
1.1.1.7.7. Lớp decorator#
Xử lý cho hàm deco phức tạp
class decoclass(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# logic trước khi gọi hàm f
print('decorator initialised')
self.f(*args, **kwargs)
print('decorator terminated')
# logic sau khi gọi hàm f
@decoclass
def hello(name,name2):
print(f'Hello, {name}. Welcome to {name2} heaven!')
hello('XXX','YYY')
return 0
return 1
return 2
runtime = 0:00:03.016835
# tạo hàm decorator tính time
from datetime import datetime
from time import sleep
# tạo hàm decorator tính time
from datetime import datetime
from time import sleep
class decoclass(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# logic trước khi gọi hàm f
print('decorator initialised')
self.f(*args, **kwargs)
print('decorator terminated')
# logic sau khi gọi hàm f
def runtime(self,*arg):
start = datetime.now()
decoclass(self.f(*args))
print("runtime = ",datetime.now()-start)
@decoclass.runtime
def print_second1():
for i in range(3):
print('return ', i)
sleep(1)
print_second1()
def runtime(f, *args, **kwargs):
start = datetime.now()
f()
print("runtime = ",datetime.now()-start)
@runtime
def print_second():
for i in range(3):
print('return ', i)
sleep(1)
print_second
An
Viet
Khong-Dat@team.com
An Viet
1.1.1.7.8. Special decorator#
@property : biến method thành attribute
@"method".setter : gán value cho method(có property), có thể sửa các attribute ban đầu có liên quan
@"method".deleter : xóa value cho method(có property), có thể xóa các attribute ban đầu có liên quan
class Teamm:
def __init__(self, ho, ten):
self.ho = ho
self.ten = ten
self.email = ho + "-" + ten + "@team.com"
def ho_va_ten(self): # regular method
return "{} {}".format(self.ho, self.ten)
ktd = Teamm("Khong", "Dat")
'Khanh'
print(ktd.ho)
print(ktd.ten)
print(ktd.email)
print(ktd.ho_va_ten())
None
# thử modify attribute
ktd.ho = "An"
ktd.ten = "Viet"
print(ktd.ho)
print(ktd.ten)
print(ktd.email) # email ko bị đổi theo
print(ktd.ho_va_ten())
class Teamm:
def __init__(self, ho, ten):
self.ho = ho
self.ten = ten
@property # bien method thanh attribute
def email(self): # regular method
return self.ho + "_" + self.ten + "@team.com"
@property # bien method thanh attribute
def ho_va_ten(self): # regular method
return "{} {}".format(self.ho, self.ten)
@ho_va_ten.setter # method ho_va_ten có thể gán giá trị và sửa giá trị attribute ho, ten
def ho_va_ten(self, ten_moi):
ho_moi, ten_moi = ten_moi.split(" ")
self.ho = ho_moi
self.ten = ten_moi
@ho_va_ten.deleter # method ho_va_ten có thể gán giá trị và sửa giá trị attribute ho, ten
def ho_va_ten(self):
self.ho = None
self.ten = None
ktd = Teamm("Khong", "Dat")
ktd.ho_va_ten = "An Khanh"
ktd.ten
del ktd.ho_va_ten
print(ktd.ten)
1.1.1.7.9. Metaclass#
Hầu hết thời gian chúng ta sẽ không sử dụng các metaclasses, chúng giống như là ma thuật đen vậy, và thường được coi là một thứ gì đó hết sức phức tạp, nhưng vẫn có một số trường hợp mà chúng ta sử dụng đến các metaclasses, đó là:
– Như chúng ta đã thấy trong ví dụ trên, các metaclasses sẽ lan truyền từ trên xuống dưới trong hệ thống phân cấp thừa kế (inheritance hierachy). Do đó nó cũng sẽ ảnh hưởng đến tất cả các lớp con. Nếu gặp trường hợp như vậy, chúng ta nên sử dụng các metaclasses.
– Khi chúng ta muốn thay đổi các classes một cách tự động, khi nó được tạo ra.
– Nếu bạn là một API developer, bạn có thể sử dụng các metaclasses.
Và có một trích dẫn của ông Tim Peters như sau:
“Metaclasses là loại ma thuật sâu sắc mà 99% người dùng không bao giờ nên lo lắng về chúng. Nếu bạn phân vân liệu rằng mình có cần tới chúng hay không, điều đó có nghĩa là bạn không cần tới chúng đâu (người mà thật sự cần tới chúng sẽ biết chắc chắn rằng họ cần chúng, mà không cần phải tìm kiểm câu trả lời cho lý do vì sao”.