最近在项目里发现一段代码,初看比较难看懂,细看,也还是比较难看懂。遂研究了一下,证实了这段代码确实,没啥作用,遂删之。记录在下。

去掉几个用于封装的函数,保留最小代码后,剩下以下14行代码。你能说出这段代码是干嘛的?每行代码分别被执行了几次吗?

class CachedProperty():

def __init__(self,func,name=None):

self.func = func

self.name = name or func.__name__

def __get__(self,instance,cls=None):

res = instance.__dict__[self.name] = self.func(instance)

return res

class MyClass():

@CachedProperty

def caches(self):

return {}

mc = MyClass()

mc.caches['key'] = 1

print(mc.caches['key'])

要读懂上面14行代码,需要理解两样东西,一个是类装饰器,一个是描述器。

类装饰器

装饰器比较常见,类装饰器就不常见了。

给func1加上func2装饰器,等同于令func1=func2(func1), func1依然是一个callable的对象。给func1加上class2装饰器,同样需要返回的是一个callable的对象。所以在class2中,要求实现__call__方法。

进群:960410445 即可获取数十套PDF!

例子:

class log():

def __init__(self,func):

self.func = func

def __call__(self, *args, **kwargs):

print('log...')

return self.func(*args, **kwargs)

@Log

def func1(x):

return x

以上就等同于令func1=Log(func1), 实际上就是一个callable的Log类实例了。调用func1的时候,就触发__call__方法完成调用过程。

描述器

描述器是一个有"绑定行为"的对象属性,用其他编程语言做一个较通俗的对比,就是实现了setter和getter的属性。

定义一个描述器,需要为属性至少定义__delete__, __get__, __set__中的任意一个方法。这些方法会在进行属性访问时自动被调用。

一个简单的例子:

class Ds():

def __init__(self, v):

self.v = v

def __set__(self, obj, v):

self.v = v

def __get__(self, obj, objtype):

return self.v

class T():

x = Ds(10)

print(T.x) # 10

这里还要扯到一个优先级的问题, 如果只定义了__get__方法,那么当T.__dict__拥有同名属性的时候,优先使用T.__dict__的,若还定义了__set__方法,则描述器永远优先,称为非资料描述器,反之称为资料描述器。这一点对读懂14行代码至关重要。

代码解释

回到最初的14行代码,可以说是'使用类装饰器的方式来定义一个非资料描述器,利用优先级的特性,完成方法调用结果缓存的功能'。

一行行来看

class CachedProperty():

def __init__(self,func,name=None):

# func == caches

self.func = func

# self.name == 'caches'

self.name = name or func.__name__

# instance等于对象实例, 在本例子中,等同于mc,cls等同于MyClass

def __get__(self,instance,cls=None):

# 最右边返回{},赋值给中间的mc.__dict__['caches']和最左边的res

res = instance.__dict__[self.name] = self.func(instance)

return res

class MyClass():

# 类装饰器, 和下面三行一起,等同于 caches = CachedProperty(lambda: {})

@CachedProperty

def caches(self): # 不需要被当成方法调用, 所以不需要实现__call__

return {}

mc = MyClass()

# 第一步,获得mc.caches, 按优先级, mc.__dict__['caches']不存在,

# 所以触发__get__方法调用,得到{}, 且mc.__dict__['caches']也等于{}

# 第二步,为mc.__dict__['caches']['key']赋值1

mc.caches['key'] = 1

# 按优先级,mc.__dict__['caches']已存在,不再触发__get__方法和cahces(self)方法执行

# 取mc.__dict__['caches']['key'], 返回1。

print(mc.caches['key'])

以上,经过一通花里胡哨的操作,实际效用就是: 实现了caches(self)方法只会被执行一次,其结果将被缓存起来,供以后再次调用时使用。

在这个例子中,caches(self)只有一行代码,返回{},在简化前,也是大概这个意思,所以,在本项目中,这段代码实际上毫无作用,遂可删之。

之后,发现同样的做法出现在django和flask等框架中,我的理解是,CacheProperty目的是为了实现view等较耗资源函数的结果缓存,并做到懒加载。例如,某类方法需要加载一个大模板并进行渲染,就可以使用CacheProperty, 保证在一次调用后,将结果缓存起来。若目标方法只是一个不耗计算资源的简单方法,就没必要再使用了。

读懂python代码_读懂花里胡哨的14行Python代码!相关推荐

  1. python人脸检测代码_如何用不到25行Python代码实现人脸检测

    本文我们会讲讲怎样利用不到 25 行 Python 代码和开源库 OpenCV,以很简单的方式实现人脸识别. 在正式开始前,先提以下两点小小的建议:先别急着跳到代码部分,最好在前文理解一下代码是干什么 ...

  2. python鲜花_【实战案例】90 行Python代码实现一棵鲜花盛开树

    今天请各位读者朋友欣赏用 Python 实现的鲜花盛宴,你准备好了吗?90 行代码即可实现一棵美丽的鲜花盛开树.对于知识的运用也是如此,需要不断吸收新知识,学习新技能,才能盛开出美丽的花朵.接下来就看 ...

  3. python简单好玩的代码_只需要9步100行Python代码就可以实现一个简单又好玩的弹球游戏...

    今天给大家分享一个有趣又好玩的弹球的游戏,一来可以把类的知识融会一下,二来加深对Python的兴趣.你会发现哎呀Python写小游戏还是蛮方便的,蛮有意思的~~ 需要本文源码:私信回复[源码]即可获取 ...

  4. python酷炫图形代码_牛批了,1 行 Python 代码就可实现炫酷可视化

    之前画图一直在用 matlibplot.pyecharts,最近学习了一个新的可视化库--cufflinks,用了两天我已经深深爱上它了 主要是因为它用法简单.图形漂亮.代码量少,用一两行代码,就能画 ...

  5. 简单且有创意的python作品_适合练手的 14 个Python 小项目,趣味十足!

    掌握一门技术,迅速提升能力,最好方法是通过实践项目,最好是完整且有趣的.分享一本,我认为非常不错的书籍,<Python Playground>,这本书每个章节就是一个完整的小项目. ​​​ ...

  6. python60行绘图程序_不敢相信,60行python代码就写出了贪吃蛇游戏

    通过写游戏玩编程是一个非常有趣的事,网上用Python写贪吃蛇的代码很多,大部分都用类来写的,比较复杂.今天看到一个非常简洁的贪吃蛇游戏代码,只有60几行,非常适合小白上手,而且思路极其清晰,赶紧分享 ...

  7. 贪吃蛇python撞墙不死_不敢相信,60行python代码就写出了贪吃蛇游戏

    通过写游戏玩编程是一个非常有趣的事,网上用Python写贪吃蛇的代码很多,大部分都用类来写的,比较复杂.今天看到一个非常简洁的贪吃蛇游戏代码,只有60几行,非常适合小白上手,而且思路极其清晰,赶紧分享 ...

  8. python 快速排序_小白入门知识详解:Python实现快速排序的方法(含实例代码)...

    前言: 今天为大家带来的内容是:小白入门知识详解:Python实现快速排序的方法(含实例代码)希望通过本文的内容能够对各位有所帮助,喜欢的话记得点赞转发收藏不迷路哦!!! 提示: 这篇文章主要介绍了P ...

  9. bp神经网络预测python代码_机器学习之多层神经网络(附Python代码和数据)

    1 引言 多层神经网络,Multiple-layers Perceptron (MLP),又被称为多层感知机,是机器学习中深度学习的典型算法.关于多层神经网络的算法原理,我们在Stata和R实现的文章 ...

最新文章

  1. OEA 框架演示 - 快过原型的开发
  2. Kotlin 在kotlin内使用Java的一些注意(长篇)
  3. linux下boost的一个扩展线程池-threadpool-的学习
  4. SSM-网站后台管理系统制作(2)---SSM基本工作原理
  5. centos7安装telnet后/etc/xinetd.d/telnet下没有telnet
  6. 勇攀监控高峰-EMonitor之根因分析 背景
  7. mysql 触发器 new.id,mysql触发器的使用方法
  8. 数据治理的好处有哪些
  9. 快速搭建一个本地FTP服务器
  10. idea取消vim模式
  11. GIS应用实例--模型预测、多元回归、空间自相关分析
  12. 一氧化碳(CO)荧光探针cas855751-82-5,二氧化硫荧光探针 激发波长653 nm,发射波长836 nm-齐岳介绍
  13. JavaScript动态创建DOM(七)
  14. 微型计算机主要性能指标是什么,微型计算机的主要性能指标
  15. 安卓开发——视频播放器
  16. 合唱队形c语言编程,洛谷 P1091 合唱队形
  17. 用s函数实现Lugre摩擦模型
  18. matlab定义双精度型变量_MATLAB小数(浮点数)类型
  19. Unity之引导功能遮罩事件穿透
  20. 咕咚已上传服务器怎到不丁微信,微信运动怎样使用咕咚数据?

热门文章

  1. MySQL—创建数据表
  2. python—多线程之死锁
  3. 多线程inline hook
  4. 消息断点+内存断点定位窗口过程
  5. linux编译llvm代码
  6. 012 背包二叉树遍历分析和代码编写
  7. 三种方法删除有序表中s和t直接的元素(包含s和t)
  8. 锁用不好,可能把自己锁住哦!(解决多线程的死锁问题)
  9. MySQL中的分页查询
  10. JVM方法区内存分配