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