LazyObject

源码地址:django/utils/functional.py

import copy
import operatorempty = object()def new_method_proxy(func):def inner(self, *args):if self._wrapped is empty:self._setup()return func(self._wrapped, *args)return innerclass LazyObject(object):"""A wrapper for another class that can be used to delay instantiation of thewrapped class.By subclassing, you have the opportunity to intercept and alter theinstantiation. If you don't need to do that, use SimpleLazyObject."""# Avoid infinite recursion when tracing __init__ (#19456)._wrapped = Nonedef __init__(self):# Note: if a subclass overrides __init__(), it will likely need to# override __copy__() and __deepcopy__() as well.self._wrapped = empty__getattr__ = new_method_proxy(getattr)def __setattr__(self, name, value):if name == "_wrapped":# Assign to __dict__ to avoid infinite __setattr__ loops.self.__dict__["_wrapped"] = valueelse:if self._wrapped is empty:self._setup()setattr(self._wrapped, name, value)def __delattr__(self, name):if name == "_wrapped":raise TypeError("can't delete _wrapped.")if self._wrapped is empty:self._setup()delattr(self._wrapped, name)def _setup(self):"""Must be implemented by subclasses to initialize the wrapped object."""raise NotImplementedError('subclasses of LazyObject must provide a _setup() method')# Because we have messed with __class__ below, we confuse pickle as to what# class we are pickling. We're going to have to initialize the wrapped# object to successfully pickle it, so we might as well just pickle the# wrapped object since they're supposed to act the same way.## Unfortunately, if we try to simply act like the wrapped object, the ruse# will break down when pickle gets our id(). Thus we end up with pickle# thinking, in effect, that we are a distinct object from the wrapped# object, but with the same __dict__. This can cause problems (see #25389).## So instead, we define our own __reduce__ method and custom unpickler. We# pickle the wrapped object as the unpickler's argument, so that pickle# will pickle it normally, and then the unpickler simply returns its# argument.def __reduce__(self):if self._wrapped is empty:self._setup()return (unpickle_lazyobject, (self._wrapped,))# We have to explicitly override __getstate__ so that older versions of# pickle don't try to pickle the __dict__ (which in the case of a# SimpleLazyObject may contain a lambda). The value will end up being# ignored by our __reduce__ and custom unpickler.def __getstate__(self):return {}def __copy__(self):if self._wrapped is empty:# If uninitialized, copy the wrapper. Use type(self), not# self.__class__, because the latter is proxied.return type(self)()else:# If initialized, return a copy of the wrapped object.return copy.copy(self._wrapped)def __deepcopy__(self, memo):if self._wrapped is empty:# We have to use type(self), not self.__class__, because the# latter is proxied.result = type(self)()memo[id(self)] = resultreturn resultreturn copy.deepcopy(self._wrapped, memo)# if six.PY3:#     __bytes__ = new_method_proxy(bytes)#     __str__ = new_method_proxy(str)#     __bool__ = new_method_proxy(bool)# else:__str__ = new_method_proxy(str)__unicode__ = new_method_proxy(unicode)  # NOQA: unicode undefined on PY3__nonzero__ = new_method_proxy(bool)# Introspection support__dir__ = new_method_proxy(dir)# Need to pretend to be the wrapped class, for the sake of objects that# care about this (especially in equality tests)__class__ = property(new_method_proxy(operator.attrgetter("__class__")))__eq__ = new_method_proxy(operator.eq)__ne__ = new_method_proxy(operator.ne)__hash__ = new_method_proxy(hash)# List/Tuple/Dictionary methods support__getitem__ = new_method_proxy(operator.getitem)__setitem__ = new_method_proxy(operator.setitem)__delitem__ = new_method_proxy(operator.delitem)__iter__ = new_method_proxy(iter)__len__ = new_method_proxy(len)__contains__ = new_method_proxy(operator.contains)def unpickle_lazyobject(wrapped):"""Used to unpickle lazy objects. Just return its argument, which will be thewrapped object."""return wrappedclass LazyA(LazyObject):def _setup(self):self._wrapped = A()class A(object):def __init__(self):self.name = 'Tom'self.age = 18self.sex = 'meal'a = LazyA()
print a.name

django中延迟加载,一个类只在第一次使用的时候才初始化,这里LazyA继承了LazyObject,实现了_setup方法。方法放回一个实例A。

当第一次调用a.name的时候调用 LazyObject.__getattr__ --> LazyObject.new_method_proxy() --> self._setup() --> A.__init__() --> getattr(self._wrapped, *args)

第二就取值的时候就不会进行self._setup()

主要通过 __getatrr__, __setatrr__, __delatrr__ 三个元方法实现延迟加载。其他元方法各位可不必纠结。

Lazy function

这个函数接受函数和任意数量的结果类(被期望作为初始函数的结果),会返回一个包装器(就叫惰性函数吧),之后我们调用这个函数。第一次调用会返回proxy class,没有调用初始化函数只是返回一个包装类。只有在调用了该代理实例的任何方法(函数的结果)之后,才会调用初始话函数。
#coding=utf8
from functools import total_ordering, wraps
from django.utils import sixclass Promise(object):"""This is just a base class for the proxy class created inthe closure of the lazy function. It can be used to recognizepromises in code."""passdef _lazy_proxy_unpickle(func, args, kwargs, *resultclasses):return lazy(func, *resultclasses)(*args, **kwargs)def lazy(func, *resultclasses):"""Turns any callable into a lazy evaluated callable. You need to give resultclasses or types -- at least one is needed so that the automatic forcing ofthe lazy evaluation code is triggered. Results are not memoized; thefunction is evaluated on every access."""@total_orderingclass __proxy__(Promise):"""Encapsulate a function call and act as a proxy for methods that arecalled on the result of that function. The function is not evaluateduntil one of the methods on the result is called."""__prepared = Falsedef __init__(self, args, kw):#3)代理实例存储原始调用的args和kwargsself.__args = argsself.__kw = kwif not self.__prepared:#4)如果是第一个调用惰性函数,准备代理类self.__prepare_class__()self.__prepared = Truedef __reduce__(self):return (_lazy_proxy_unpickle,(func, self.__args, self.__kw) + resultclasses)@classmethoddef __prepare_class__(cls):#5)遍历期望的结果类for resultclass in resultclasses:#6)相反的方向遍历每个结果类的每个超类for type_ in resultclass.mro():#7)循环遍历每个类的属性for method_name in type_.__dict__.keys():# All __promise__ return the same wrapper method, they# look up the correct implementation when called.if hasattr(cls, method_name):continue#8)为结果类的方法返回新的包装器方法,闭包中保存函数名meth = cls.__promise__(method_name)#9)代理类没有该属性,将包装器增加该属性setattr(cls, method_name, meth)cls._delegate_bytes = bytes in resultclassescls._delegate_text = six.text_type in resultclassesassert not (cls._delegate_bytes and cls._delegate_text), ("Cannot call lazy() with both bytes and text return types.")if cls._delegate_text:if six.PY3:cls.__str__ = cls.__text_castelse:cls.__unicode__ = cls.__text_castcls.__str__ = cls.__bytes_cast_encodedelif cls._delegate_bytes:if six.PY3:cls.__bytes__ = cls.__bytes_castelse:cls.__str__ = cls.__bytes_cast@classmethoddef __promise__(cls, method_name):# Builds a wrapper around some magic methoddef __wrapper__(self, *args, **kw):# Automatically triggers the evaluation of a lazy value and# applies the given magic method of the result type.#10)最后会调用原始函数得到结果res = func(*self.__args, **self.__kw)return getattr(res, method_name)(*args, **kw)return __wrapper__def __text_cast(self):return func(*self.__args, **self.__kw)def __bytes_cast(self):return bytes(func(*self.__args, **self.__kw))def __bytes_cast_encoded(self):return func(*self.__args, **self.__kw).encode('utf-8')def __cast(self):if self._delegate_bytes:return self.__bytes_cast()elif self._delegate_text:return self.__text_cast()else:return func(*self.__args, **self.__kw)def __str__(self):# object defines __str__(), so __prepare_class__() won't overload# a __str__() method from the proxied class.# 10)最后会调用原始函数得到结果return str(self.__cast())def __ne__(self, other):if isinstance(other, Promise):other = other.__cast()return self.__cast() != otherdef __eq__(self, other):if isinstance(other, Promise):other = other.__cast()return self.__cast() == otherdef __lt__(self, other):if isinstance(other, Promise):other = other.__cast()return self.__cast() < otherdef __hash__(self):return hash(self.__cast())def __mod__(self, rhs):if self._delegate_bytes and six.PY2:return bytes(self) % rhselif self._delegate_text:return six.text_type(self) % rhsreturn self.__cast() % rhsdef __deepcopy__(self, memo):# Instances of this class are effectively immutable. It's just a# collection of functions. So we don't need to do anything# complicated for copying.memo[id(self)] = selfreturn self@wraps(func)def __wrapper__(*args, **kw):# Creates the proxy object, instead of the actual value.#2)调用惰性函数时,我们得到的是代理实例而不是实际值return __proxy__(args, kw)return __wrapper__def testFunc(text):return text.title()#1)得到了包装器函数
lazy_func = lazy(testFunc, str)
res = lazy_func('bbbbbbbbbbbbbb')
print res
print res.upper()

Django Lazy LazyObject相关推荐

  1. Django Vue 项目踩坑记:The field admin.LogEntry.user was declared with a lazy reference to ‘xxx‘

    功能期望 基于Django提供的AbstractUser类重写User模型,在其中根据业务需求增加信息,并将新的用户模型设为系统默认用户模型. 问题描述 完成自定义User模型的编写,并在settin ...

  2. Django源码分析6:auth认证及登陆保持

    django源码分析 本文环境python3.5.2,django1.10.x系列 1.这次分析django框架中登陆认证与接口权限检查. 2.在后端开发中,难免会对接口进行权限验证,其中对于接口是否 ...

  3. Django项目配合sentry实现浅析

    Django项目日志配合sentry概述 本文环境python3.5.2,Django版本1.10.2 Django项目中日志配合sentry的实现 sentry是一个错误跟踪网站,可以收集获取运行中 ...

  4. Django视图层总结

    2019独角兽企业重金招聘Python工程师标准>>> 自定义path转换器 其实就是写一个类,并包含下面的成员和属性: 类属性regex:一个字符串形式的正则表达式属性: to_p ...

  5. 用python做web小项目_Python之路【第十八篇】Django小项目webQQ实现

    WEBQQ的实现的几种方式 1.HTTP协议特点 首先这里要知道HTTP协议的特点:短链接.无状态! 在不考虑本地缓存的情况举例来说:咱们在连接博客园的时候,当tcp连接后,我会把我自己的http头发 ...

  6. django源码简析——后台程序入口

    django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...

  7. django图片上传到oss_django 配置阿里云OSS存储media文件的例子

    1. 安装django-aliyun-oss2-storage包 linux上用 pip install django-aliyun-oss2-storage 无报错,顺利安装 windows上报错: ...

  8. Django框架 day04

    今日内容: 模板层(模板语法)标签过滤器自定义标签,过滤器,inclusion_tag模板的继承 模板的导入 首先来说一下render内部原理和FBV与CBV 三板斧 FBVrender返回一个htm ...

  9. django学习随笔:ManagementUtility

    ManagementUtility类,位于django.core.management目录下的__init__.py文件. 这个类,在其init中: def __init__(self, argv=N ...

最新文章

  1. MATLAB reshape()函数和sub2ind()函数
  2. JavaScript基础学习(七)—BOM
  3. 每天一点点之 taro 框架开发 - taro路由及传参
  4. Windows与Linux下进程间通信技术比较
  5. ajax post 没有返回_Ajax异步技术之三:jQuery中的ajax学习
  6. 教你轻松搞定javascript中的正则
  7. mysql innodb 间隙锁_MySQL中InnoDB的间隙锁问题
  8. 【Kubernetes】Error: Cask minikube is unavailable No Cask with this name exists
  9. python下载指定页面的所有图片
  10. php 遍历某一目录并对该目录中的所有文件重命名
  11. python编程入门指南-Python编程入门指南(上下册)
  12. SpringMVC+uploadify3.2.1版实现附件上传功能(直接可以使用)
  13. xvidcore.dll not found视频播放问题
  14. 万人规模互联网公司的企业IT基础架构概览
  15. [转] 制作PPT的全过程,存着有用
  16. 学术研究入门,如何下载论文?
  17. 02-07GRE真题及答案解析整理
  18. C++中的自定义函数
  19. 计算机系统数据保存期限,哪种存储器存储数据时间长?
  20. Ambari自定义stack

热门文章

  1. 算法笔记一 链表的翻转(C++)
  2. PS5上传图片失败,游戏无法推送更新,提示服务器出了点问题,HTTP状态码:403
  3. Mysql快速备份_sql备份
  4. Java 并发编程的艺术 pdf 下载
  5. Light OJ 1214
  6. 第三届互联网CIO-CTO班招募,CSDN 5个推荐名额,火热报名中
  7. 送5本《Kafka权威指南》第二版
  8. 【Axure高保真原型】常用文件图标元件模板
  9. 浅析技能音效制作思路
  10. 串口开发 打印机 读卡器 遇到的问题