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àm thread.acquire()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.2.3. Share memory#

  • Cung cấp bộ nhớ chung như value , array giữa 2 process

# NẾU CHẠY THẾ NÀY THÌ ARRAY ko được dùng chung
from multiprocessing import Process, Lock
import time

# hàm đổi dấu phần tử
def _counter_arr(arrs, process_name,lock):
    lock.acquire()
    for i, el in enumerate(arrs):
        time.sleep(0.01)
        arrs[i] = -arrs[i] 
        print("{}: {}".format(process_name, arrs[i]))
    lock.release()

if __name__ =="__main__":
	arrs = [1, 2, 3, 4]
	lock = Lock()
	exec1 = Process(target=_counter_arr, args=(arrs, "khanh process",lock)) # pass counter and thread_name into method _counter
	exec2 = Process(target=_counter_arr, args=(arrs, "ai process",lock))
	execs = [exec1, exec2]

	exec1.start()
	exec2.start()

	for exec in execs:
		exec.join()
from multiprocessing import Process, Value, Array, Lock
import time

def _counter_arr(arrs, process_name):
    lock.acquire()
    for i, el in enumerate(arrs):
        time.sleep(0.01)
        arrs[i] = -arrs[i]
        print("{}: {}".format(process_name, arrs[i]))
    lock.release()
    
if __name__ =="__main__":
    arrs = Array('i', range(1, 5, 1))
    lock = Lock()
    exec1 = Process(target=_counter_arr, args=(arrs, "khanh process")) # pass counter and thread_name into method _counter
    exec2 = Process(target=_counter_arr, args=(arrs, "ai process"))
    execs = [exec1, exec2]

    exec1.start()
    exec2.start()

    for exec in execs:
        exec.join()
  • Giả định bạn đang có một pipeline biến đổi dữ liệu gồm nhiều step khác nhau, mỗi một process sẽ phụ trách một step trong pipeline. Khi đó dữ liệu cần được shared chung giữa các process.

1.1.1.5.2.4. Share object#

  • Queue là một định dạng stack an toàn khi làm việc với multi thread và process. Chúng ta có thể tạo ra một queue và cho phép các thread, process truy cập dữ liệu mà không bị hiện tượng concurrency vì dữ liệu được truy suất và sử dụng một lần bởi một thread hoặc process

from multiprocessing import Process, Queue
import time

def _counter_queue(queue, process_name, max_count):
    # lock.acquire()
    while max_count:
        time.sleep(0.01)
        value = queue.get()
        print("{}: {}".format(process_name, value))
        max_count -= 1
    # lock.release()
if __name__ =="__main__":
# dữ liệu q được xử dụng lần lượt lên ko data nào được sử dụng chung giữa 2 processes nên tránh được concurrency
    starttime = time.datetime()
    q = Queue()
    for i in range(10):
        q.put(i)

    max_count = 5
    # lock = Lock()
    exec1 = Process(target=_counter_queue, args=(q, "khanh process", max_count)) # pass counter and thread_name into method _counter
    exec2 = Process(target=_counter_queue, args=(q, "ai process", max_count))
    execs = [exec1, exec2]

    exec1.start()
    exec2.start()

    for exec in execs:
        exec.join()
    print(time.datetime()-starttime)
from multiprocessing import Process, Queue
import time
from datetime import datetime

def _counter_queue(queue, process_name, max_count):
    # lock.acquire()
    while max_count:
        time.sleep(0.01)
        value = queue.get()
        # print("{}: {}".format(process_name, value))
        max_count -= 1
    # lock.release()
if __name__ =="__main__":
# dữ liệu q được xử dụng lần lượt lên ko data nào được sử dụng chung giữa 2 processes nên tránh được concurrency
	starttime = datetime.now()
	q = Queue()
	for i in range(1000):
		q.put(i)

	max_count = 200
    # lock = Lock()
	execs = []
	for i in range(5):
		exe = Process(target=_counter_queue, args=(q, f"{i} process", max_count)) # pass counter and thread_name into method _counter
		execs.append(exe)
		
	for exe in execs:
		exe.start()
		
	for exe in execs:
		exe.join()
		
	# for value in range(1000):
	# 	time.sleep(0.01)
	# 	# value = queue.get()
	# print("{}: {}".format('process_name', value))

	print(datetime.now()-starttime)

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ớn

  • Cache 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án

  • Nên dùng thread pool khi đọc ghi

1.1.1.5.7. Queue#

  • Khi chạy một process trên multiple-threads, queue thường được sử dụng để làm nơi lưu trữ dữ liệu chung giữa các threads 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( property),  thể sửa các attribute ban đầu  liên quan
    @"method".deleter : xóa value cho method( property),  thể xóa các attribute ban đầu  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( property),  thể sửa các attribute ban đầu  liên quan
    @"method".deleter : xóa value cho method( property),  thể xóa các attribute ban đầu  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”.