Flask開發高級:上下文管理前戲

在對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)]}}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章