python的隐式指针特征
pointer是C/C++的里非常熟悉也容易令人困惑的一个功能点,python里的变量赋值一般表现也和指针类似,但是没有显式地语法指出,·在这里我用自己的理解做一个比喻来帮助大家理解。
当我们写下一行语句,比如a=1 (python)或者int a=1(C++),电脑内存到底做了什么呢?这里的数据“1”在电脑内存里实际上是一个二级制序列,任何数据最终都以这种形式存储。而序列储存的位置,则是这份数据的地址,这个地址在内存中,通过“a”这个代号来获取。
所以,如果把地址比作是一栋房子,数据就像是房子里装的家具,而房子门口的门牌号,就是地址的名字。只不过在程序里,一个房子可以有多个门牌号,共同指向同一个房子,但是不能有多个房子,共享同一个门牌号。
a=1,a=2,是更改了a对应房子里存储的东西(数据)
a=1,b=a,是赋予“1”这个数据所在的房子两块门牌号(a,b)
此时修改a,b任意变量名的值,a、b的值均被修改,换句话说,python赋值默认是指针赋值,但是在class内赋值、copy赋值例外。
python class及inheritance
为什么需要class?
答案是我们需要用数据结构来存储数据的关系,而不是记在脑子里,或者是靠修改变量名称。比如我们需要程序计算,高中生的各科成绩求和。显然,一个简单的sum函数可以完成这样的运算,再来一个for循环,对所有的学生都算一遍。但是这样做之后,我们无法知道把学生的名字、单科成绩、各科成绩对应起来,虽然他们在循环中的次序是对应的。如果代码里稍有不慎修改了成绩的原始排序,就会出现驴唇对马嘴的情况。
class 作为一种复杂的数据结构,能够帮我们记录下一个综合体及其各个属性的关系,以支持更复杂的操作。
此外还能够提高代码可读性,减少重复代码块粘贴,提高效率。
class 数据结构还能继承,不断延展功能的同时,不干扰旧的代码功能,用尽量少的代码支持更多操作。
基本语法
class A(object):
def __init__(self):
print('A init called')
self.num=1
class B(A):
def __init__(self):
print('B init called')
A.__init__(self)
a=A()
b=B()
b.num
首先我们定了一个class 结构A,它的初始化/构造函数(类似于C++里的constructor)打印一句话,并且给自己多了一个成员 num,值为1
然后定义了一个class 结构B,B继承了A,B的构造函数打印了一句话,然后调用了A的构造函数,这里的调用,等同于把A的_init_函数完全复制到B里,并且执行,也就是继承了A的init
结果是
A init called #a=A()
B init called#b=B()的构造函数print
A init called#b的init里 call A的init,并且给B增加了一个成员self.num
1 #b.num
更规范的继承写法
上边的例子中,在B的init我们指定继承A的init,形如baseclass._init_(self),这种写法在一层继承中没有问题,但是在多层继承中,会丧失灵活性,因为此时,parent class被hard-coded,而不是按照继承链自动追溯,丧失了灵活性。
参考这篇文章的例子What is the difference between old style and new style classes in Python?stackoverflow.com
class SomeBaseClass(object):
def __init__(self):
print('SomeBaseClass.__init__(self) called')
class UnsuperChild(SomeBaseClass):
def __init__(self):
print('UnsuperChild.__init__(self) called')
SomeBaseClass.__init__(self)
class SuperChild(SomeBaseClass):
def __init__(self):
print('SuperChild.__init__(self) called')
super(SuperChild, self).__init__()
上边的代码中,Unsuperchild class的继承写法是固定的,和上边的class B一样。而superchild class的继承方法是使用了super(括号可以省略)关键词,两者都继承自somebaseclass。那么到底有什么后果呢?我们再加入一层继承
class InjectMe(SomeBaseClass):
def __init__(self):
print('InjectMe.__init__(self) called')
super(InjectMe, self).__init__()
class UnsuperInjector(UnsuperChild, InjectMe): pass
class SuperInjector(SuperChild, InjectMe): pass
InjectMe class 也继承自somebaseclass。同时UnsuperInjector继承UnsuperChild,和InjectMe,SuperInjector继承SuperChild,和InjectMe
现在的继承关系有三级
somebaseclass (grandparent)
unsuperchild superchild InjectMe (parent)
unsuperinjector superinjector (child)
根据Method Resolution Order (MRO)法则,当我生成一个第三级class 的实例时,会按照“先左后右再向上”的顺序调用super
比如我创建一个UnsuperInjector的实例,它的左边parent是UnsuperChild,先调用了UnsuperChild的init,UnsuperChild的init里写了,固定调用sombaseclass 的init,因此,不用super()关键字继承时,会受到固定parent class的限制,不会再调用injectme class
o = UnsuperInjector()
# 打印 UnsuperChild.__init__(self) called
#打印 SomeBaseClass.__init__(self) called
而如果run下边一句结果就不同
o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called
SuperInjector上一级左边是SuperChild,SuperChild的init里有super().init,,右边是InjectMe,也有super().init,因此SuperChild的super 指向InjectMe,InjectMe的super指向SomeBaseClass,因此打印结果如上边代码块所示。
总结
在multiple inheritance模式下,super().继承方法能够避免固定继承导致其他parent class继承失效的问题,增加了代码灵活性。同时注意继承的顺序由MRO决定,遵循同级先左后右,再增加深度的原则。