描述符的一个示例用法就是将类属性的初始化延迟到被实例访问时。如果这些属性的初始化依赖全局应用上下文的话那么这一点可能有用。另一个使用场景是初始化的代价很大但在导入类的时候不知道是否会用到这个属性。这样的描述符可以按照如下所示来实现class InitOnAccess:definit(self, klass, *args, **kwargs):self.klass klassself.args argsself.kwargs kwargsself._initialized Nonedefget(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_initializedinitialized![‘a’, ‘r’, ‘g’, ‘u’, ‘m’, ‘e’, ‘n’, ‘t’]m.lazilyinitializedcached![‘a’, ‘r’, ‘g’, ‘u’, ‘m’, ‘e’, ‘n’, ‘t’]PyPI上OpenGL的官方Python库PyOpenGL用到了相似的技术来实现lazy_property它既是装饰器又是数据描述符如下所示class lazy_property(object):definit(self, function):self.fget functiondefget(self, obj, cls):value self.fget(obj)setattr(obj, self.fget.name, value)return value这样的实现与使用 property 装饰器稍后介绍类似但它所包装的函数仅执行一次然后类属性就被替换为它的返回值。当开发人员需要同时满足以下两点要求时这种技术通常很有用。• 对象实例需要被保存为实例之间共享的类属性以节约资源。• 在全局导入时对象不能被初始化因为其创建过程依赖某个全局应用状态/上下文。对于使用 OpenGL 编写的应用来说往往需要同时满足这两点要求。举个例子在OpenGL 中创建着色器的代价非常高因为需要对 GLSLOpenGL 着色语言编写的代码进行编译。合理的做法是只创建一次然后将其定义放在需要用到它的类附近。另一方面如果没有对 OpenGL 上下文进行初始化是无法执行着色器编译的因此很难在全局导入时在全局模块命名空间中可靠地定义并编译着色器。下面的例子展示了 PyOpenGL 的 lazy_property 装饰器这里是 lazy_classattribute的修改版在某个虚构的基于 OpenGL 应用中的可能用法。为了在不同的类实例之间共享属性需要将加粗部分的代码修改为原始的 lazy_property 装饰器如下所示import OpenGL.GL as glfrom OpenGL.GL import shadersclass lazy_class_attribute(object):definit(self, function):self.fget functiondef __get __(self, obj, cls):value self.fget(obj or cls)注意无论是类级别还是实例级别的访问都要保存在类对象中而不是保存在实例中setattr(cls, self.fget. __name __, value)return valueclass ObjectUsingShaderProgram(object):trivial pass-through vertex shader implementationVERTEX_CODE “”#version 330 corelayout(location 0) in vec4 vertexPosition;void main(){gl_Position vertexPosition;}“”trivial fragment shader that results in everythingdrawn with white colorFRAGMENT_CODE “”#version 330 coreout lowp vec4 out_color;void main(){out_color vec4(1, 1, 1, 1);}“”lazy_class_attributedef 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 高级语法特性一样这一特性也应该谨慎使用并在代码中详细说明。对于没
Python 高手编程系列四:现实例子 —
发布时间:2026/6/11 18:17:59
描述符的一个示例用法就是将类属性的初始化延迟到被实例访问时。如果这些属性的初始化依赖全局应用上下文的话那么这一点可能有用。另一个使用场景是初始化的代价很大但在导入类的时候不知道是否会用到这个属性。这样的描述符可以按照如下所示来实现class InitOnAccess:definit(self, klass, *args, **kwargs):self.klass klassself.args argsself.kwargs kwargsself._initialized Nonedefget(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_initializedinitialized![‘a’, ‘r’, ‘g’, ‘u’, ‘m’, ‘e’, ‘n’, ‘t’]m.lazilyinitializedcached![‘a’, ‘r’, ‘g’, ‘u’, ‘m’, ‘e’, ‘n’, ‘t’]PyPI上OpenGL的官方Python库PyOpenGL用到了相似的技术来实现lazy_property它既是装饰器又是数据描述符如下所示class lazy_property(object):definit(self, function):self.fget functiondefget(self, obj, cls):value self.fget(obj)setattr(obj, self.fget.name, value)return value这样的实现与使用 property 装饰器稍后介绍类似但它所包装的函数仅执行一次然后类属性就被替换为它的返回值。当开发人员需要同时满足以下两点要求时这种技术通常很有用。• 对象实例需要被保存为实例之间共享的类属性以节约资源。• 在全局导入时对象不能被初始化因为其创建过程依赖某个全局应用状态/上下文。对于使用 OpenGL 编写的应用来说往往需要同时满足这两点要求。举个例子在OpenGL 中创建着色器的代价非常高因为需要对 GLSLOpenGL 着色语言编写的代码进行编译。合理的做法是只创建一次然后将其定义放在需要用到它的类附近。另一方面如果没有对 OpenGL 上下文进行初始化是无法执行着色器编译的因此很难在全局导入时在全局模块命名空间中可靠地定义并编译着色器。下面的例子展示了 PyOpenGL 的 lazy_property 装饰器这里是 lazy_classattribute的修改版在某个虚构的基于 OpenGL 应用中的可能用法。为了在不同的类实例之间共享属性需要将加粗部分的代码修改为原始的 lazy_property 装饰器如下所示import OpenGL.GL as glfrom OpenGL.GL import shadersclass lazy_class_attribute(object):definit(self, function):self.fget functiondef __get __(self, obj, cls):value self.fget(obj or cls)注意无论是类级别还是实例级别的访问都要保存在类对象中而不是保存在实例中setattr(cls, self.fget. __name __, value)return valueclass ObjectUsingShaderProgram(object):trivial pass-through vertex shader implementationVERTEX_CODE “”#version 330 corelayout(location 0) in vec4 vertexPosition;void main(){gl_Position vertexPosition;}“”trivial fragment shader that results in everythingdrawn with white colorFRAGMENT_CODE “”#version 330 coreout lowp vec4 out_color;void main(){out_color vec4(1, 1, 1, 1);}“”lazy_class_attributedef 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 高级语法特性一样这一特性也应该谨慎使用并在代码中详细说明。对于没