在對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)]}}