Flask系列四

阅读实际使用的开源项目如flask,对于提高编程能力有巨大好处。flask是实现网站功能,使用数据库的一个编程框架,已有中文出版有关书籍介绍。本系列讲座涉及的是非常精彩的The Flask Mega-Tutorial,开始于2017年12月6日开始,结束于2018年5月结束,每周一课。

    对于一个不太大,但足够复杂的app的代理current_app的实现方法,本系列讲座进行彻底分析,分析过程像美食家需要慢慢品味精美大餐,以体会高明厨师的精巧设计和制作。主餐啦:

 

系列四

主餐(1)

get_ident线程标识函数

本地容器Local

本地工作栈LocalStrak

 

1、  get_ident线程标识函数

Import 语句与其它语句一样,也可作为try,except格式中的语句,寻找最新的可用get_ident函数。

# since each threadhas its own greenlet we can just use those as identifiers
# for the context. 
If greenlets are notavailable we fall back to the
# current thread ident depending on where it is.
try:
   
from greenletimport getcurrent as get_ident
except ImportError:
   
try:
       
from threadimport get_ident
   
except ImportError:
       
from _threadimport get_ident

         get_ident(或greenlet 模块的getcurrent)可以作为线程的标识,用于线程管理,其机理如下:

         当某一线程调用get_ident将得到与此线程相关的标识。如果函数来自于thread模块,此标识就是一个整数;如果使用的是greenlet 模块中的getcurrent函数,此标识可以为greenlet的对象,例如<greenlet.greenlet object at 0x01373E90>。同一线程无论调用get_ident多少次,标识不会改变,但不同线程的标识不会相同。有关greenlet信息,可查阅python之greenlet学习

 

2、  工作容器Local的测试方法

下面release_local函数的注释部分给出了工作容器Local的测试方法。测试方法具体见文中中文注释。

def release_local(local):
   
"""Releases the contents of the local forthe current context.
   
This makes it possible to use localswithout a manager.

    Example::

        >>> loc = Local()           #产生一个Local的实例loc
        >>> loc.foo = 42            #增加属性foo,并赋值42
        >>>release_local(loc)      #释放包括属性的内容
        >>> hasattr(loc, 'foo')     #测试属性foo是否还存在
        False                       #测试结果:属性foo已不存在

    With this function one can release:class:`Local` objects as well
    as :class:`LocalStack` objects.  However it is not possible to
    release data held by proxies thatway, one always has to retain
    a reference to the underlying localobject in order to be able
    to release it.

    .. versionadded:: 0.6.1
    """
   
local.__release_local__()

最后一句表明,函数release_local的作用是调用参数local的release_local方法。

 

3、  工作容器Local类

Local类的开始部分如下:

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')

    def __init__(self):
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

    def __iter__(self):
        return iter(self.__storage__.items())

    def __call__(self, proxy):
        """Create a proxy for a name."""
        return LocalProxy(self, proxy)
 

         __slots__语句(见系列三)申明Local类有且仅有__storage__和__ident_func__两个属性。所以要关注的就是两个属性。__storage__将作为Local类的属性存放的字典,代替__dict__字典;__ident_func__是识别函数变量。

         在__init__中,此两个属性被初始化:字典__storage__被初始化为空字典,识别函数__ident_func__初始化为前面的get_ident函数。

         使用object.__setattr__的方法,而不使用一般初始化属性采用的格式:

                   self.__storage__= {}

                   self.__ident_func__= get_ident

的原因是在后面__setattr__被重载,参见系列一。

         后面是此类的迭代方法__iter__。

         最后是__call__方法,使得Local的实例可以像函数一样调用。例如

>>>local = Local()

>>>local(proxy)

__call__在下一系列中进行测试。

下面是类的后半部分,涉及与属性操作相关的三个方法,使用字典storage。由于重载了__setattr__,对属性写入操作调用此方法。而对于属性读出操作,对于槽口__ident_func__和storage,会调用__get__,对于其它属性,属性的常规操作getattr将失败,就会调用__getattr__。(参见系列一__getattr__、系列三__slots__)。

    def __release_local__(self):
        self.__storage__.pop(self.__ident_func__(), None)

    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}

    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

先看__setattr__方法:

在前面的初始化中,storage被初始化成空字典。

首先变量ident得到函数get_ident返回代表线程的标识;

然后试图进行操作:

           storage[ident][name] = value

如果storage为空字典,将发生exceptKeyError,使得执行后面的语句

        storage[ident] ={name: value}

用此ident作为键,设置{name:value}作为值。其中name是“属性”的名称,value是“属性”的值。例如假设loc是Local的实例,ident为整数的简单情形如7916,则属性赋值:

loc.foo = 42

后,loc.__storage__为变成

{7916: {'foo': 42}}。

当同一个线程第二次设置“属性”的值,变量ident仍旧得到同一标识,则调用__setattr__进行的操作为:

           storage[7916][name] = value;

否则仍将发生except KeyError,而执行其后的语句。

__getattr__方法:按线程__ident_func__标识函数的结果,返回此线程的属性的值。

__delattr__方法:按线程__ident_func__标识函数的结果,删除线程的此属性。

__release_local__方法:

删除特定线程的内容。例如上面的标识为7916的线程的内容,此线程的所有“属性”也将不存在了。

 

4、  本地工作栈LocalStrak

定义一个实例变量_local,它是上面Local的一个实例。从Local可知,实例变量_local的属性和方法有:

1)  实例属性变量__ident_func__,用于线程标识;

2)  _local可以进行属性操作;

3)  删除特定线程的内容。

利用上面的1),定义一个property属性,名称为__ident_func__,对此属性的访问,就是对_local.__ident_func__的访问,省去了_local。

         利用上面的3),定义__release_local__,调用Local的__release_local__,删除特定线程的内容。

LocalStack的前半部分如下,其中的__call__方法以后分析:

from my_app2.flask4_1 import Local
class LocalStack(object):

   
"""This class works similar to a:class:`Local` but keeps a stack
   
of objects instead.  This is best explained with an example::

        >>> ls = LocalStack()
        >>> ls.push(42)
        >>> ls.top
        42
        >>> ls.push(23)
        >>> ls.top
        23
        >>> ls.pop()
        23
        >>> ls.top
        42

    They can be force released by using a:class:`LocalManager` or with
    the :func:`release_local` functionbut the correct way is to pop the
    item from the stack after using.  When the stack is empty it will
    no longer be bound to the currentcontext (and as such released).

    By calling the stack withoutarguments it returns a proxy that resolves to
    the topmost item on the stack.

    .. versionadded:: 0.6.1
    """

   
def __init__(self):
       
self._local= Local()

   
def __release_local__(self):
       
self._local.__release_local__()

   
def _get__ident_func__(self):
       
return self._local.__ident_func__

   
def _set__ident_func__(self, value):
       
object.__setattr__(self._local, '__ident_func__', value)
   
__ident_func__ = property(_get__ident_func__, _set__ident_func__)
   
del _get__ident_func__, _set__ident_func__

   
def __call__(self):
       
def _lookup():
           
rv = self.top
            if rv is None:
               
raise RuntimeError('object unbound')
           
return rv
       
return LocalProxy(_lookup)

         利用2)的属性操作,建立一个stack属性。

         stack属性是一个工作栈,用列表list构成。栈的常规操作为push、pop。

先看push:

    def push(self, obj):
       
"""Pushes a new item to thestack"""
       
rv = getattr(self._local, 'stack', None)

       
if rv is None:
           
self._local.stack= rv = []
       
rv.append(obj)
        return rv

如果_local有属性stack,则rv会得到此stack,否则rv将得到None,接下来程序把stack初始化为空列表,由此创建了stack属性。

         列表的append方法向列表添加对象了,就是使对象入栈。

         下面是方法pop:

    def pop(self):
       
"""Removes the topmost item from thestack, will return the
       
old value or `None` if the stackwas already empty.
        """
       
stack = getattr(self._local, 'stack', None)
       
if stackis None:
           
return None
       
elif len(stack)== 1:
           
release_local(self._local)
           
return stack[-1]
       
else:
           
return stack.pop()

         从工作栈弹出对象:

         先把工作栈赋值给stack变量,然后判stack的元素个数,如只有一个,程序删除此线程的内容,stack属性也会丢失,随后返回此元素;否则程序从stack的尾部,即栈顶弹出一个对象,返回此对象。

         注意像list这样的mutable变量,在下面的语句

stack = getattr(self._local, 'stack', None)

执行后,对于stack的操作,仍会影响_local.stack,因为在stack赋值中得到是一个id号码,此id号码对应一个list的对象,_local.stack的值也是此id号码。对于python来说,就是同一对象,所以对于stack的内容更改,也就是对_local.stack的更改。

         下面是property的属性top,返回栈顶对象。

    @property
   
def top(self):
       
"""The topmost item on the stack.  If the stack is empty,
        `None` is returned.
        """
       
try:
           
return self._local.stack[-1]
       
except (AttributeError, IndexError):
           
return None

 

小结:LocalStack是一个栈,有push、pop、top方法。

测试如下:

>>> ls= LocalStack()
>>> ls.push(42)
>>> ls.top
42
>>> ls.push(23)
>>> ls.top
23
>>> ls.pop()
23
>>> ls.top
42

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值