flask系列五

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

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

 

系列五

主餐(2):发现暗器

         本地代理LocalProxy

         测试Local.__call__

         测试LocalStack.__call__

         partial函数

1、  本地代理LocalProxy

LocalProxy代理对象,可以通过代理访问对象的属性。

LocalProxy代理的对象分为两类,一种对象可以通过类似于属性的格式获得,称为A类对象;另一种对象可以通过调用函数的方法获得,称为B类对象。

A类对象。对象的属性也是一个对象,例如c一个可以由属性的对象(例如c是类C的实例),有属性b。此c.b也是一个可以有属性的对象,例如有属性a。如果用LocalProxy的实例localProxy代理c.b,则访问localProxy.a与访问c.b.a等价。所以创建LocalProxy的实例时,必须提供对象名与属性名(本例的c,b)作为第一第二参数。对于本例以LocalProxy(c, a)创建实例。

B类对象,例如某一函数f的返回值是对象,并且此对象有属性a,则访问localProxy.a与访问f().a等价。所以创建LocalProxy的实例时,必须提供函数名作为第一参数。对于本例以LocalProxy(f)创建实例。

特别地,第一参数有__release_local__属性时,总是按A类处理。注意Local与LocalStack均有__release_local__的方法。

下面是程序,为方便,注释了装饰类函数implements_bool。

本地代理有四个槽口:'__local'、 '__dict__'、 '__name__'、'__wrapped__'。由于有'__dict__',可以使用常规的属性操作。

#@implements_bool
class LocalProxy(object):
    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')

    def __init__(self, local, name=None):
        object.__setattr__(self, '_LocalProxy__local', local)
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):
            object.__setattr__(self, '__wrapped__', local)
        #print(self._LocalProxy__local is self.__local)

    def _get_current_object(self):
        """Return the current object.  This is useful if you want the real
        object behind the proxy at a time for performance reasons or because
        you want to pass the object into a different context.
        """
        if not hasattr(self.__local, '__release_local__'):
            print('------self=%s, self.__local=%s' %(self, str(self.__local)))
            return self.__local()
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)

    def __getattr__(self, name):
        if name == '__members__':
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)

对于A类对象,需要提供第一第二参数,对应__init__方法的参数local, name。

对于B类对象,需要提供第一参数,对应参数local。

在初始化时使用暗器对槽口__local进行初始化成参数local的值。

说使用暗器是指,对于属性_LocalProxy__local的设置,等价于对属性__local进行设置。下面有个注释掉的print语句,可以表明两者是一样的。

然后初始化__name__成name的值。

如果函数是可调用的,并且不能有属性__release_local__,加上__wrapped__属性。这样虽然Local与LocalStack由于__call__的存在,都是可调用的,但如果直接作为第一参数local创建,不能加上__wrapped__属性。

当一个函数有__wrapped__属性,表明此函数是被包装后的函数,例如被装饰语句包装。使用__wrapped__属性可以得到被包装前的函数。

对于方法_get_current_object:

如果没有属性__release_local__,执行调用__local()。注意对于B类,__local是函数名。

否则执行getattr(self.__local,self.__name__)得到对象。注意对于A类,__local是对象名, __name__是属性名。

对于方法__getattr__:

         由于存在槽口__dict__,对于LocalProxy的属性操作使用__dict__。但是当读操作时,如果在__dict__中找不到,在父类也找不到等,将调用此方法。参见系列一中对__getattr__的说明。

如果name为'__members__'则返回调用方法_get_current_object得到对象的所有属性名(dir的结果)。

否则,调用方法_get_current_object得到对象,然后得到对象的属性。

由于只有__getattr__没有__setattr__,所以使用代理只能读取参数。

2、 测试

在Local.__call__与LocalStack.__call__的测试恰可以包括A类与B类。

1)测试Local.__call__

对于类Local,__call__如下

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

调用LocalProxy的参数分别是Local的实例名与Local的属性名。下面,Local的实例名是loc,与loc的属性名‘req’,即对象loc.req将被代理。

测试如下:

>>>from my_app2.flask4_1 import Local
>>>from my_app2.flask4_2 import LocalStack
>>>from my_app2.flask5_1 import LocalProxy
>>>loc = Local()
>>>request = loc('req')    #request代理loc.req
>>>class C:
...    pass
...
>>>c = C()
>>>loc.req = c  #request将代理c
>>>c.a = 6             #c设置属性
>>>request.a       #使用代理可以读出
6

 

2)  测试LocalStack.__call__

对于类LocalStack,__call__复杂一些:

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

调用LocalProxy时的参数是_lookup函数,当被执行时将返回LocalStack实例的栈顶元素。测试如下:

>>>from my_app2.flask4_1 import Local
>>>from my_app2.flask4_2 import LocalStack
>>>from my_app2.flask5_1 import LocalProxy
>>>_response_local = LocalStack()
>>>response = _response_local()    #代理_response_local.top
>>>class C:
...   pass
... 
>>>c = C()
>>>_response_local.push(c)       #c将出现在_response_local.top
[<C object at 0x0260F870>]
>>>c.foo2 = 8
>>>print(response.foo2)
8

 

3、   partial函数

partial函数是functools模块中的函数。格式:

         partial(func,*args, **keywords)

看例子容易理解。

         类int(x,base=10)返回整数对象,例如对于字符串,以基为2进行解读:

>>>int('10010', base=2)
18

使用partial,可以利用类int形成一个新的类,把base固定为2:

>>>from functools import partial
>>>basetwo = partial(int, base=2)

这样就可以使用此函数了:

>>>basetwo.__doc__ = 'Convert base 2 string to an int.'
>>>basetwo('10010')
18

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值