神奇的traitlets(赋予PY类属性修改后,自动更改事件)

最近在读一个英伟达库的代码,读到这个的使用法,研究了一下觉得很新奇。


在我们学习py的第一天就是知道它是一个动态的语言,我相信很多人学了很久也不知道动态到底动在哪里,简单的说一下就是创建变量的时候,直接用一个名字和你目标量一连就好,你什么也不用管。一切都是“智能的”,但是这样的便利就会引发一种错误。当你的一个类属性里面的一个变量必须为int时,你缺给了一个strings给它。会怎么样?我也不知道,至少程序是会罢工吧~那一种比较容易想到的做法是在进行赋值之前做一些类型检查。但是一个还好,未来100个呢?我觉得会烦死吧。这就引入了我们本文这个Traitlets。

https://traitlets.readthedocs.io/en/stable/trait_types.html
https://github.com/ipython/traitlets

不多说,直接摔个doc上来,看我文不爽的自己去原味的。

那这个库的作用有什么呢?

Traitlets 允许Python自定义类拥有类型检查、动态计算默认值和Change回调这三种特性。主要是针对自定义的class。而且只需要继承HasTraits即可让自定义的类获得这一系列特性。

类型检查与静态默认值

from traitlets.traitlets import HasTraitsfrom traitlets import Int,Unicode
class Student(HasTraits): age = Int()    dada = Unicode("我")    # 这个是将int已经导入
import traitletsimport threadingimport numpy as np
# 这个是一个camera的classclass Camera(traitlets.HasTraits): # 这个地方是对一些参数的保护写的代码
value = traitlets.Any() width = traitlets.Integer(default_value=224) height = traitlets.Integer(default_value=224) format = traitlets.Unicode(default_value='bgr8') running = traitlets.Bool(default_value=False)    # 而这个是用class。mathmod这样的写法

在以上的代码里面都有体现,就是使用之前,你的类一定要继承一下,然后就是静态默认值,直接写出来就好。完成对你在意量的保护。

在上面,第一个代码里面,你的age其实是一个类属性,但在创建对象时,traitlets已经帮我们创建了同名的示例属性,所以,我们可以放心使用age属性,而不用担心修改的是类属性。

像这个代码里面也有

动态计算默认值

通过@default修饰器将类变量的默认值设置为动态生成:

import getpassfrom traitlets.traitlets import HasTraitsfrom traitlets import Int, Unicode, default
class Identity(HasTraits): username = Unicode()
@default('username') def _default_username(self):        return getpass.getuser()

使用default装饰器,username属性的值由下面的def内_default_username来获得,注意_default_username方法只会被执行一次。

观察者模式,属性修改后,用自己的函数更改事件

如果前面的用法是毛毛雨的话,那这个就是瓢泼大雨了。在开始bb之前,我们说下什么是观察者模式。我不太喜欢书中的定义,太装A++(自己思考)。我喜欢自己下定义,模式这个你就理解成一种固定的做法就好。关键是观察者,我平时解释的是用一种视角来说明。观察者就好像视角是在上帝端,或是有个看门狗在监督这个状态。或者就是你找了一个东西来盯着你的这个状态,改变就做点什么。而且有点回调的味道,其实也就是个回调,你的程序状态发生改变,然后好像有个后台的程序在读取到这种改变去做点什么。这里通过@observe修饰器监视类变量的变动:

先看我的箭头

traitlets.observe('running') # 监视类变量的改动,监视了running这个值 def _on_running(self, change): # change是我们在捕获到改动事件后做出的反应 if change['new'] and not change['old']: # transition from not running -> running # 转换冲未运行到运行 self._running = True #将这里的运行标志更新 self.thread = threading.Thread(target=self._capture_frames) # 这里开启了一个新的线程去干活 #这个函数在上面有定义 self.thread.start()@ #开始运行 elif change['old'] and not change['new']: # transition from running -> not running # 转换从运行中到停止运行 self._running = False self.thread.join()

我直接上我的代码,注释写的很清晰了。就是监控上面的bool量。

其中这个change是个字典:

{ 'owner': object, # HasTraits instance 'new': 1, # the new value 'old': 0, # the old value 'name': "bar", # The name of the changed trait 'type': 'change', # The event type of the notification, usually 'change'}
{“所有者”:对象,#HasTraits实例“new”:1,#新值“old”:0,#旧值“name”:“bar”,#更改特征的名称'type':'change',#通知的事件类型,通常为'change'}

继续说,还有一个功能就是让一个属性在一个范围内变化

交叉验证器这个名字要记住哦~

如果对某个属性有取值范围的限定,或者其他要求,那么可以对这个属性值进行验证

from traitlets import HasTraits, TraitError, Int, Bool, validate
class Parity(HasTraits): value = Int() parity = Int()
@validate('value') def _valid_value(self, proposal): if proposal['value'] % 2 != self.parity: raise TraitError('value and parity should be consistent') return proposal['value']
@validate('parity') def _valid_parity(self, proposal): parity = proposal['value'] if parity not in [0, 1]: raise TraitError('parity should be 0 or 1') if self.value % 2 != parity: raise TraitError('value and parity should be consistent') return proposal['value']
parity_check = Parity(value=2)
# Changing required parity and value together while holding cross validationwith parity_check.hold_trait_notifications(): parity_check.value = 1 parity_check.parity = 1

代码取自demo,是靠奇数偶数来判断的。

建议自定义交叉验证器不要修改HasTraits实例的状态。

http://www.coolpython.net/informal_essay/20-04/traitlets.html

这里引一段这位老哥写的东西。

@validate('age') def _valid_age(self, proposal): age = proposal['value'] if age < 13 or age > 16: raise TraitError('学生年龄异常') return age

这个就写的很简洁明了。

当然了,我用的功能只有这么多,更多的功能可以去doc了~

(0)

相关推荐