Python 高手编程系列一十三:现实例子 — 延迟求值属性

描述符的一个示例用法就是将类属性的初始化延迟到被实例访问时。如果这些属性的初始

化依赖全局应用上下文的话,那么这一点可能有用。另一个使用场景是初始化的代价很大,但

在导入类的时候不知道是否会用到这个属性。这样的描述符可以按照如下所示来实现:

class InitOnAccess:

def init (self, klass, *args, **kwargs):

self.klass = klass

self.args = args

self.kwargs = kwargs

self._initialized = None

def get (self, instance, owner):

if self._initialized is None:

print('initialized!')

self._initialized = self.klass(*self.args,

**self.kwargs)

else:

print('cached!')

return self._initialized

下面是示例用法:

class MyClass:

... lazily_ _initialized = InitOnAccess(list, "argument")

...

m = MyClass()

m.lazily_ *initialized
initialized!

'a', 'r', 'g', 'u', 'm', 'e', 'n', 't'

m.lazily* *initialized
cached!

'a', 'r', 'g', 'u', 'm', 'e', 'n', 't'

PyPI上OpenGL的官方Python库PyOpenGL用到了相似的技术来实现lazy_property,
它既是装饰器又是数据描述符,如下所示:
class lazy_property(object):
def init (self, function):
self.fget = function
def get (self, obj, cls):
value = self.fget(obj)
setattr(obj, self.fget.name , value)
return value
这样的实现与使用 property 装饰器(稍后介绍)类似,但它所包装的函数仅执行一
次,然后类属性就被替换为它的返回值。当开发人员需要同时满足以下两点要求时,这种
技术通常很有用。
• 对象实例需要被保存为实例之间共享的类属性,以节约资源。
• 在全局导入时对象不能被初始化,因为其创建过程依赖某个全局应用状态/上下文。
对于使用 OpenGL 编写的应用来说,往往需要同时满足这两点要求。举个例子,在
OpenGL 中创建着色器的代价非常高,因为需要对 GLSL(OpenGL 着色语言)编写的代
码进行编译。合理的做法是只创建一次,然后将其定义放在需要用到它的类附近。另一方
面,如果没有对 OpenGL 上下文进行初始化,是无法执行着色器编译的,因此很难在全局
导入时在全局模块命名空间中可靠地定义并编译着色器。
下面的例子展示了 PyOpenGL 的 lazy_property 装饰器(这里是 lazy_class*

attribute)的修改版在某个虚构的基于 OpenGL 应用中的可能用法。为了在不同的类实例

之间共享属性,需要将加粗部分的代码修改为原始的 lazy_property 装饰器,如下所示:

import OpenGL.GL as gl

from OpenGL.GL import shaders

class lazy_class_attribute(object):

def init (self, function):

self.fget = function

def __get __(self, obj, cls):

value = self.fget(obj or cls)

注意:无论是类级别还是实例级别的访问

都要保存在类对象中,而不是保存在实例中

setattr(cls, self.fget. __name __, value)

return value

class ObjectUsingShaderProgram(object):

trivial pass-through vertex shader implementation

VERTEX_CODE = """

#version 330 core

layout(location = 0) in vec4 vertexPosition;

void main(){

gl_Position = vertexPosition;

}

"""

trivial fragment shader that results in everything

drawn with white color

FRAGMENT_CODE = """

#version 330 core

out lowp vec4 out_color;

void main(){

out_color = vec4(1, 1, 1, 1);

}

"""

@lazy_class_attribute

def shader_program(self):

print("compiling!")

return shaders.compileProgram(

shaders.compileShader(

self.VERTEX_CODE, gl.GL_VERTEX_SHADER

),

shaders.compileShader(

self.FRAGMENT_CODE, gl.GL_FRAGMENT_SHADER

)

)

和所有 Python 高级语法特性一样,这一特性也应该谨慎使用,并在代码中详细说明。

对于没有经验的开发者而言,这种类行为的改变可能令人既困惑又意外,因为描述符影响

的是类行为最基本的内容(例如属性访问)。因此,如果描述符在项目代码库中发挥重要作

用的话,那么确保团队所有成员都熟悉并理解这一概念是很重要的。

相关推荐
-银雾鸢尾-3 小时前
C#中的拓展方法
开发语言·c#
guodingdingh5 小时前
软件开发工作问题总结0718
java·开发语言·数据库
优橙教育7 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
想会飞的蒲公英8 小时前
计算机怎样读取中文文本:编码、清洗与标准化
人工智能·python·自然语言处理
SeaTunnel9 小时前
从 Python Script 地狱到标准化数据集成框架
大数据·开发语言·python·程序员·代码·seatunnel
深度研习笔记9 小时前
OpenCV工业视觉实战09|项目EXE打包+工控机无环境部署+后台常驻运行,彻底脱离Python环境,完成项目最终交付
python·opencv·webpack
碎光拾影9 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
CCPC不拿奖不改名9 小时前
大模型推理架构与开源生态知识整理
数据库·windows·python·架构·langchain·开源·github
Marst Code10 小时前
(python)2026Plotly 库评估:交互式可视化到底值不值得引入?
开发语言·python
databook10 小时前
当散点图不够用时:用 t-SNE 可视化多维数据
python·数据分析·数据可视化