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决定,遵循同级先左后右,再增加深度的原则。