作者: Jim Wang 公众号: 巴博萨船长

摘要:如果父类是个公用类,很多子项目中都运用到了,而子类也要作为一个基本类在该项目中的很多地方都要用到,但是的原始父类里面有些类属性,如何实现父类与子类间属性的设置与获取的传递呢?

Abstract: If the parent class is a public class, it is used in many sub-projects, and the sub-class is also used as a basic class in many places in the project, but there are some class attributes in the original parent class, how to implement the parent class What about the transfer of property settings and acquisitions between subclasses?

作者: Jim Wang 公众号: 巴博萨船长

导语

前几天做一个项目,遇见类似这样一个问题。父类是个公用类,很多子项目中都运用到了,而子类也要作为一个基本类在该项目中的很多地方都要用到,但是的原始父类里面有些类属性(注意这里是类属性,不是实例属性)。在程序运行时候要进行重新设置。

背景

Python中父类的类属性,类方法,实例属性都能够被子类继承,实力属性的再设置很简单,当然为了控制类属性的访问权限(Python中不能完全实现控制),也可以用@preproty装饰符优化和控制实力属性的设置,父类的类属性被子类继承,可以很容易的获得父类属性的内容,但是如果想设置父类的类属性,就要用 父类名.类属性名 称来实现,那么能不能用 子类名.类属性名 来实现类属性的同步设置呢。

来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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]
[1, 2, 3, 4]
['a']

发现,如果使用

1
Father.mylist = ['a']

来实现类属性的设置,想象中,应该父类的类属性也能被重新设置,但是结果显示出,想象的和现实还是有差距的。

我也尝试了用@preproty和@xxx.setter等修饰符,单独以及配合@classmethod修饰符来实现用Father.mylist的实现类属性的同步设置,但结果都失败了。其实可以继续使用Grandfather的类名加类属性及Grandfather来实现父类属性的设置但是,就感觉明明有了新父类,模块中交替使用这两个类名来设置类属性,确实有点不是太完美。

后来经过尝试找到了一个新的方法,就是使用类元metaclass,至于metaclass的内容,大家可以在网上找到相应的文章,这里介绍两种使用方法。

第一种方法

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
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]
[1, 2, 3, 4]
['a']
['a']

因为我们创建类的时候使用了metaclass, 那么类被创建的时候就会添加mylist作为自己的类属性,但是当我们使用Father.mylist来设置类属性的时候,我们其实是在将这个值传递给了Grandfather。因为Grandfather该类已经被创建,所以override该类属性的property是不现实(也许可以,但是我读书少)。那么只有在创建Father的时候来override此类属性的property,而如果想实现,那就必须用到类元,及metaclass,这东西简单,但是确实是所有类的始祖。

第二种方法

第二中方法,当然,也是在理解第一种方法之后,后来在读别的文章的时候发现了第二种方法。这里写出来方便大家理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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中类是可以动态在任意合法位置使用合法缩进创建的。其实两者方法的原理一样。但是我个人还是更喜欢第一种,代码更加简洁明快。

才疏学浅,欢迎交流提意见彼此提高。需要声明一下51cto博客作者zuiwuxin就是作者本人,所以不存在版权问题。以后该博客将作为个人文章的主要发布地。


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。