前言
作为 TA 工具人,已经用 Python 很长一段时间,很多内置库都使用得得心应手了。 最近开始尝试了解一些第三方库,从而能够更好解决一些实际运用遇到的问题。 attrs 这个库是一个非常不错的代码优化方案。 可以简略了很多类描述的方法。
Github 地址 官方说明文档
attrs 解决的问题
attrs 统一了类属性描述,让代码更加简洁可读。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class ArtisanalClass (object ): def __init__ (self, a, b ): self.a = a self.b = b def __repr__ (self ): return "ArtisanalClass(a={}, b={})" .format (self.a, self.b) def __eq__ (self, other ): if other.__class__ is self.__class__: return (self.a, self.b) == (other.a, other.b) else : return NotImplemented def __ne__ (self, other ): result = self.__eq__(other) if result is NotImplemented : return NotImplemented else : return not result def __lt__ (self, other ): if other.__class__ is self.__class__: return (self.a, self.b) < (other.a, other.b) else : return NotImplemented def __le__ (self, other ): if other.__class__ is self.__class__: return (self.a, self.b) <= (other.a, other.b) else : return NotImplemented def __gt__ (self, other ): if other.__class__ is self.__class__: return (self.a, self.b) > (other.a, other.b) else : return NotImplemented def __ge__ (self, other ): if other.__class__ is self.__class__: return (self.a, self.b) >= (other.a, other.b) else : return NotImplemented def __hash__ (self ): return hash ((self.__class__, self.a, self.b))
如果要写一个完整的类需要加上很多内置方法的实现。 这会导致大量的重复工作,而且代码会非常冗长。
1 2 3 4 5 import attr@attr.s class SmartClass (object ): a = attr.ib() b = attr.ib()
代码例子来源于 文档
attrs 只需要简单的描述就可以帮你完成上面一大堆的功能,非常有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class SM_Controller (object ): def __init__ (self,polyName,col,row,conPre,size,shape,color,shape ): self.start = start self.polyName = polyName self.col = col self.row = row self.conPre = conPre self.size = size self.shape = shape self.color = color import attr@attr.s class SM_Controller (object ): polyName = attr.ib() col = attr.ib() row = attr.ib() conPre = attr.ib() size = attr.ib() shape = attr.ib() color = attr.ib() shape = attr.ib()
上面就是实战中的例子
打印对象的时候使用 attrs 也会变得清晰。
attrs 介绍 不可变类构建 1 2 3 4 5 6 7 8 9 10 11 12 13 import attr@attr.s(frozen=True ) class SmartClass (object ): a = attr.ib() b = attr.ib() smart = SmartClass(1 ,2 ) smart.a = 3
加入 frozen 参数可以让类初始化之后不可改变。
验证数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @attr.s class SmartClass (object ): a = attr.ib() b = attr.ib() @a.validator def must_be_str (self,attribute,value ): if not isinstance (value,str ): raise ValueError("must be a string type" ) smart = SmartClass(1 ,2 )
支持对初始化的数据进行校验。
参数默认值 1 2 3 4 5 6 7 8 @attr.s class SmartClass (object ): a = attr.ib(default=1 ) b = attr.ib(factory=list ) smart = SmartClass() print (smart) smart = SmartClass(b=2 ,a='a' ) print (smart)
参数添加默认值之后可以不进行传参,也可以用键值进行传参。 使用 attrs 构建类就变得更加灵活。
踩过的坑 mutable 容器
构建 mutable 容器的时候,需要用 factory 参数或者 attr.ib(default=attr.factory(list))
参数默认值使用数组和字典这类 mutable 容器会导致很多问题
1 2 3 4 5 6 7 8 def return_list (a=[] ): a.append(1 ) return a a = return_list() print (a) a = return_list() print (a)
虽然我们想要进入函数的时候 a 的默认值都是这个数组。 然而实际运行的时候赋值只发生了一次,并不会每次函数运行都重置默认值的。 所以参数不能使用 [] {} 这些容器,而是推荐使用 None,然后再函数中初始化
1 2 3 4 5 6 7 8 def return_list (a=None ): a = a if a else [] a.append(1 ) return a a = return_list() print (a) a = return_list() print (a)
所以如果默认值想要为 数组 之类的,需要用 attrs 提供的 factory 方法。
hash 值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import attr@attr.s class SmartClass (object ): a = attr.ib(default=1 ) b = attr.ib(factory=list ) smart = SmartClass() a = {smart: 1 }
attrs 默认处理 __hash__
方法,所以无法把它当成键值使用。 然而不使用 attrs 装饰的话是可以的。 解决方法就是加上 `@attr.s (hash=False)` 就可以了。
参考文档 Hashing 的部分
init 初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 import attr@attr.s class SmartClass (object ): a = attr.ib(default=1 ) b = attr.ib(factory=list ) def __init__ (self,*args, **kwargs ): super (SmartClass,self).__init__(*args, **kwargs) print (123 ) smart = SmartClass()
依照文档 attrs 的装饰器会覆盖掉原有的 __init__
但是如果我们确实要在 __init__
进行数据处理,需要使用 __attrs_post_init__
方法 或者也可以使用继承的方式调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import attr@attr.s class SmartClass (object ): a = attr.ib(default=1 ) b = attr.ib(factory=list ) def __attrs_post_init__ (self ): print (123 ) class SuperClass (SmartClass ): def __init__ (self, *args, **kwargs ): super (SuperClass, self).__init__(*args, **kwargs) print (456 ) smart = SmartClass() sup = SuperClass()
总结
Python3.7 加入了 dataclass 内置模块,基本上涵盖 attrs 大多数功能。 attrs 的好处在于老版本完美兼容 Python2 可以在 Maya 等 DCC 里面使用。 让代码编写更加紧凑和简洁。