4.1 python类的特殊成员,偏函数,线程安全,栈,flask上下文
目录
一. Python 类的特殊成员(部分)
class MyClass(object):
def __call__(self, *args, **kwargs):
print(f'__call__ -- args:{args}')
def __setattr__(self, key, value):
print(f'__setattr__ -- key:{key} value:{value}')
def __getattr__(self, item):
print(f'__getattr__ -- item:{item}')
def __setitem__(self, key, value):
print(f'__setitem__ -- key:{key} value:{value}')
def __getitem__(self, item):
print(f'__getitem__ -- item:{item}')
my_class = MyClass()
my_class(1, 2, 3) # 执行__call__
my_class.age = 41 # 执行__setattr__
res1 = my_class.age # 执行__getattr__
my_class['王力宏'] = '王二哥' # 执行__setitem__
res2 = my_class['王力宏'] # 执行__getitem__
# 以上代码执行结果:
__call__ -- args:(1, 2, 3)
__setattr__ -- key:age value:41
__getattr__ -- item:age
__setitem__ -- key:王力宏 value:王二哥
__getitem__ -- item:王力宏
二. Python偏函数
Python 偏函数是通过 functools
模块被用户调用。
1. 描述
函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。
偏函数是将所要承载的函数作为partial()
函数的第一个参数,原函数的各个参数依次作为partial()
函数后续的参数,除非使用关键字参数。
通过语言描述可能无法理解偏函数是怎么使用的,那么就举几个常见的例子来说明。
2. 实例一: 取余函数
取余函数,对于整数 100,取得对于不同数 m 的 100%m
的余数。
from functools import partial
def mod(n, m):
return n % m
mod_by_100 = partial(mod, 100)
print(mod(100, 7)) # 2
print(mod_by_100(7)) # 2
3. 实例二: 求三个数的和
from functools import partial
def add(a, b, c):
print(a, b, c)
return a + b + c
par_add = partial(add, 1, 2)
print(par_add(3))
par_add2 = partial(add, 1)
print(par_add2(5, 7))
# 执行结果:
1 2 3
6
1 5 7
13
三. 线程安全
1. 实例一: 无线程,消耗时间过长
import time
class Foo(object):
pass
foo = Foo()
def add(i):
foo.num = i
time.sleep(1)
print(foo.num)
for i in range(10):
add(i)
# 执行结果: 每秒打印出一个数字,总共耗时10秒
0
1
2
3
4
5
6
7
8
9
2. 实例二: 开启线程,节省了时间,但数据不安全
import time
import threading
class Foo(object):
pass
foo = Foo()
def add(i):
foo.num = i
time.sleep(1)
print(foo.num, i)
for i in range(20):
th = threading.Thread(target=add, args=(i,))
th.start()
# 执行结果: 几乎在1秒的时间打印出了全部结果
19 4
19 2
19 1
19 3
19 0
19 14
19 13
19 11
19 10
19 9
19 7
19 5
19 16
19 8
19 15
19 19
19 18
19 17
19 6
19 12
3. 实例三: 使用local, 以空间换时间, 保证了数据安全
import time
import threading
from threading import local
class Foo(local): # 继承local
pass
foo = Foo()
def add(i):
foo.num = i
time.sleep(1)
print(foo.num, i, threading.current_thread().ident)
for i in range(20):
th = threading.Thread(target=add, args=(i,))
th.start()
# 执行结果: 以空间换时间,浪费了部分资源,保证了执行效率和数据安全
8 8 7760
11 11 1856
9 9 836
7 7 10612
5 5 1148
4 4 12124
2 2 6040
19 19 12652
6 6 8288
3 3 6552
18 18 10232
17 17 12668
15 15 9348
13 13 13132
10 10 7948
1 1 7404
16 16 13200
12 12 9440
0 0 7880
14 14 7616
4. Flask上下文机制
# 总结: threading.local
Flask上下文机制就是使用的 threading.local,这个技术不算很牛逼,但是概念很重要:
线程进来,开辟一个空间给这个线程,线程操作的所有任务,都会复制一份到该空间中.
四. 栈Stack
栈遵循 后进先出(Last In First Out) 原则.
# 栈Stack: 遵循 后进先出(Last In First Out) 的原则
class Stack(object):
data = []
def __setattr__(self, key, value):
self.push(key, value)
def push(self, key, value):
self.data.append({key: value})
def pop(self):
return self.data.pop()
stack = Stack()
stack.name = '666'
stack.name = '777'
stack.name = '888'
print(stack.data)
print(stack.pop(), stack.data)
print(stack.pop(), stack.data)
print(stack.pop(), stack.data)
# 执行结果:
[{'name': '666'}, {'name': '777'}, {'name': '888'}]
{'name': '888'} [{'name': '666'}, {'name': '777'}]
{'name': '777'} [{'name': '666'}]
{'name': '666'} []
五. LocalStack
from threading import get_ident # get_ident()可以获取到当前线程id
import threading
import time
class MyLocalStack(object):
storage = {} # 结构: {key1: [el1], key2: [el2]}
def push(self, item):
try:
self.storage[get_ident()].append(item)
except:
self.storage[get_ident()] = [item]
def pop(self):
return self.storage[get_ident()].pop()
my_local_stack = MyLocalStack()
def run(i):
my_local_stack.push(i)
time.sleep(1)
for i in range(5):
th = threading.Thread(target=run, args=(i,))
th.start()
print(my_local_stack.storage)