前几天做一个项目,遇见类似这样一个问题。父类是个公用类,很多子项目中都运用到了,而子类也要作为一个基本类在该项目中的很多地方都要用到,但是的原始父类里面有些类属性(注意这里是类属性,不是实力属性)。在程序运行时候要进行重新设置。
背景:Python中父类的类属性,类方法,实力属性都能够被子类继承,实力属性的再设置很简单,当然为了控制类属性的访问权限(Python中不能完全实现控制),也可以用@preproty装饰符优化和控制实力属性的设置,父类的类属性被子类继承,可以很容易的获得父类属性的内容,但是如果想设置父类的类属性,就要用 父类名.类属性名 称来实现,那么能不能用 子类名.类属性名 来实现类属性的同步设置呢。
来看一个例子:
class Grandfather(object): mylist = [] def __init__(self): pass class Father(Grandfather): def __init__(self): pass Grandfather.mylist = [1, 2, 3, 4] print Grandfather.mylist print Father.mylist Father.mylist = [‘a‘] print Grandfather.mylist print Father.mylist
打印结果:
[1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] [‘a‘]
发现,如果使用
Father.mylist = [‘a‘]
来实现类属性的设置,想象中,应该父类的类属性也能被重新设置,但是结果显示出,想象的和现实还是有差距的。
我也尝试了用@preproty和@xxx.setter等修饰符,单独以及配合@classmethod修饰符来实现用Father.mylist的实现类属性的同步设置,但结果都失败了。其实可以继续使用Grandfather的类名加类属性及Grandfather来实现父类属性的设置但是,就感觉明明有了新父类,模块中交替使用这两个类名来设置类属性,确实有点不是太完美。
后来经过尝试找到了一个新的方法,就是使用类元metaclass,至于metaclass的内容,大家可以在网上找到相应的文章,这里介绍两种使用方法。
第一种:
class MetaMyList(type): def _get_dummy(self): return Grandfather.mylist def _set_dummy(self, value): Grandfather.mylist = value mylist = property(_get_dummy, _set_dummy) class Grandfather(object): mylist = [] def __init__(self): pass class Father(Grandfather): __metaclass__ = MetaMyList def __init__(self): pass Grandfather.mylist = [1, 2, 3, 4] print Grandfather.mylist print Father.mylist Father.mylist = [‘a‘] print Grandfather.mylist print Father.mylist
打印结果令人很满意:
[1, 2, 3, 4] [1, 2, 3, 4] [‘a‘] [‘a‘]
因为我们创建类的时候使用了__metaclass__, 那么类被创建的时候就会添加mylist作为自己的类属性,但是当我们使用Father.mylist来设置类属性的时候,我们其实是在将这个值传递给了Grandfather。因为Grandfather该类已经被创建,所以override该类属性的property是不现实(也许可以,但是我读书少)。那么只有在创建Father的时候来override此类属性的property,而如果想实现,那就必须用到类元,及metaclass,这东西简单,但是确实是所有类的始祖。
第二中方法,当然,也是在理解第一种方法之后,后来在读别的文章的时候发现了第二种方法。这里写出来方便大家理解。
class Grandfather(object): mylist = [] def __init__(self): pass class Father(Grandfather): class __metaclass__(type): @property def mylist(cls): return Grandfather.mylist @mylist.setter def mylist(cls, value): # @NoSelf Grandfather.mylist = value def __init__(self): pass Grandfather.mylist = [1, 2, 3, 4] print Grandfather.mylist print Father.mylist Father.mylist = [‘a‘] print Grandfather.mylist print Father.mylist
这里就很容易发现在python中类是可以动态在任意合法位置使用合法缩进创建的。其实两者方法的原理一样。但是我个人还是更喜欢第一种,代码更加简洁明快。
才疏学浅,欢迎交流提意见彼此提高。