在对Flask的上下文管理源码进行剖析之前,我们得回顾下python相关的基础知识。
面向对象
__ call__ |
只要定义类型的时候,实现__call__函数,这个类型就成为可调用的。换句话说,调用一个对象,就是触发对象所在类中的__call__方法的执行
现在,假如你想实现单例模式(只能创建唯一实例的类),实现起来也很简单:
class Singleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
else:
return cls.__instance
# Example
class Spam(metaclass=Singleton):
def __init__(self):
print(' Creating Spam')
a = Spam()
b = Spam()
print(a is b) # True
反射 |
在Python中,反射指的是通过字符串来操作对象的属性, 涉及到四个内置函数的使用,由于Python中一切皆对象,所以类和对象都可以用下述四个方法。
下面是具体的示例:
class Teacher:
def __init__(self,full_name):
self.full_name =full_name
t=Teacher('Winstonfy')
hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
setattr(t,'age',18) # 等同于t.age=18
delattr(t,'age') # 等同于del t.age
#基于反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行
class FtpServer:
def serve_forever(self):
while True:
inp=input('input your cmd>>: ').strip()
cmd,file=inp.split()
if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
func(file)
def get(self,file):
print('Downloading %s...' %file)
def put(self,file):
print('Uploading %s...' %file)
server=FtpServer()
server.serve_forever()
#input your cmd>>: get a.txt
#Downloading a.txt...
#input your cmd>>: put a.txt
#Uploading a.txt...
__setattr__,__getattr__,__delattr__ |
# __setattr__,__getattr__,__delattr__
class Foo:
x=1
def __init__(self,y):
self.y=y
def __getattr__(self, item):
print('----> from getattr:你找的属性不存在')
def __setattr__(self, key, value):
print('----> from setattr')
# self.key=value #这就无限递归了,你好好想想
# self.__dict__[key]=value #应该使用它
def __delattr__(self, item):
print('----> from delattr')
# del self.item #无限递归了
self.__dict__.pop(item)
#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)
#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
#f1.xxxxxx
__ slots__ |
你的程序要创建大量(可能上百万) 的对象,导致占用很大的内存。
对于主要是用来当成简单的数据结构的类而言,你可以通过给类添加 slots 属性来极大的减少实例所占的内存。比如:
class Date:
__slots__ = ['year', 'month', 'day']
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
当你定义 slots 后,Python 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在 slots 中列出的属性名在内部被映射到这个数组的指定小标上。使用slots 一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在 slots中定义的那些属性名。
偏函数
偏函数 |
partial的作用,固定函数中的一些参数,返回一个新的函数,方便调用
下面是示例:
from functools import partial
def mod( n, m ):
return n + m
mod_by_100 = partial( mod, 100 ) # 100传给n
print mod( 100, 7 ) # 107
print mod_by_100( 7 ) # 107
线程安全
线程安全 |
没有开多线程,需要执行10秒钟:
import time
class Foo(object):
pass
foo = Foo()
def add(a):
foo.num = a
time.sleep(1)
print(foo.num)
for i in range(10):
add(i)
开多线程,但没有关注线程安全,1秒钟后全TM打印出来的是9:
import time
import threading
class Foo(object):
pass
foo = Foo()
def add(a):
foo.num = a
time.sleep(1)
print(foo.num,threading.current_thread().ident)
for i in range(10):
th=threading.Thread(target=add,args=(i,))
th.start()
在多线程中,同一个进程中的多个线程是共享一个内存地址的,多个线程操作数据时,就会造成数据的不安全,所以我们就要加锁(效率降低)。但是,对于一些变量,如果仅仅只在本线程中使用,怎么办?
方法一,可以通过全局的字典,key为当前线程的线程ID,value为具体的值。
方法二,使用threading.local方法
threading.local 在多线程操作时,为每一个线程创建一个值,使得线程之间各自操作自己 的值,互不影响。
1秒钟后打印出来的是0~9:
import time
import threading
from threading import local
class Foo(object):
pass
foo = local()
def add(a):
foo.num = a
time.sleep(1)
print(foo.num,threading.current_thread().ident)
for i in range(10):
th=threading.Thread(target=add,args=(i,))
th.start()
这是什么原理呢:
# 开启线程之前 按线程优先以字典进行存储(copy一份) 以空间换时间及线程安全
th_local = {
线程1 id:{foo.num:0},
线程2 id:{foo.num:1},
线程3 id:{foo.num:2},
线程4 id:{foo.num:3},
......
}
th.local.get(线程1 id)[foo.num] # 取的时候按线程进行取
数据结构
栈 |
栈(stack)是一种线性数据结构,它就像一个上图所示的放入乒乓球的圆筒容器,栈中的元素只能先入后出(First In Last Out,简称FILO)。最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶(top)。
栈的基本操作:
class Stack(object):
"""用数组实现栈"""
def __init__(self):
self.__list =[]
def push(self,item):
"""添加一个新元素item到栈顶"""
self.__list.append(item)
def pop(self):
"""弹出栈顶元素"""
return self.__list.pop()
def top(self):
"""返回栈顶元素"""
if self.__list:
return self.__list[-1]
else:
return None
def is_empty(self):
"""判断栈是否为空"""
return self.__list is None
def size(self):
"""返回栈的元素个数"""
return len(self.__list)
彩蛋
一个请求进Flask后,就存储在下面这种数据结构中:
{9528:{stack: [ctx(request,session)]}}