python 删除父类方法_Python开发基础-Day20继承实现原理、子类调用父类的方法、封装...

继承实现原理

python中的类可以同时继承多个父类,继承的顺序有两种:深度优先和广度优先。

一般来讲,经典类在多继承的情况下会按照深度优先的方式查找,新式类会按照广度优先的方式查找

示例解析:

没有共同头部父类的类型

1 classE:2 deftest(self):3 print('from E')4 #pass

5 classF:6 deftest(self):7 print('from F')8 #pass

9

10 classC:11 deftest(self):12 print('from C')13 #pass

14

15 classB(C):16 deftest(self):17 print('from B')18 #pass

19

20 classD(E):21 deftest(self):22 print('from D')23 #pass

24 classA(B,D,F):25 deftest(self):26 print('from A')27 #pass

28 obj=A()29 obj.test()

在这种模型下,新式类和经典类的继承顺序都一样。

调用obj.test(),首先找boj对象的__dict__字典,然后找生成类A的__dict__字典,如果这两个都没有,会按照以下顺序进行查找,找到为止:

ClassA->ClassB->ClassC->ClassD->ClassE->ClassF

如果都找不到,抛出异常错误。

有共同头部父类的类型

1 classD(object):2 deftest(self):3 print('from D')4 #pass

5 classC(D):6 deftest(self):7 print('from C')8 #pass

9 classB(C):10 deftest(self):11 print('from B')12 #pass

13 classF(D):14 deftest(self):15 print('from F')16 #pass

17 classE(F):18 deftest(self):19 print('from E')20 #pass

21 classH(D):22 deftest(self):23 print('from H')24 #pass

25 classG(H):26 deftest(self):27 print('from G')28 #pass

29

30 classA(B,E,G):31 deftest(self):32 print('from A')33 #pass

34

35 obj=A()36 obj.test()37 print(A.mro())

在这种模型下,新式类和经典类查找继承顺序不同。

新式类使用的是广度优先的方式,调用obj.test(),首先找boj对象的__dict__字典,然后找生成类A的__dict__字典,如果这两个都没有,会按照以下顺序进行查找,找到为止:

classA->classB->classC->classE->classF->classG->classH->classD->-classobject

1 #经典类不继承object

2 classD:3 deftest(self):4 print('from D')5 #pass

6 classC(D):7 deftest(self):8 print('from C')9 #pass

10 classB(C):11 deftest(self):12 print('from B')13 #pass

14 classF(D):15 deftest(self):16 print('from F')17 #pass

18 classE(F):19 deftest(self):20 print('from E')21 #pass

22 classH(D):23 deftest(self):24 print('from H')25 #pass

26 classG(H):27 deftest(self):28 print('from G')29 #pass

30

31 classA(B,E,G):32 deftest(self):33 print('from A')34 #pass

35

36 obj=A()37 obj.test()

经典类(python2中才有经典类的概念,python3中都是新式类)使用的是深度优先的方式,调用obj.test(),首先找boj对象的__dict__字典,然后找生成类A的__dict__字典,如果这两个都没有,会按照以下顺序进行查找,找到为止:

ClassA->ClassB->ClassC->ClassD->ClassE->ClassF->ClassG

mro方法

python的继承顺序,是按照一定的算法生成的mro表进行顺序查找继承的,只有在新式类中才有该方法:该方法有以下三个特点:

1.子类会先于父类被检查:

2.多个父类会根据它们在列表中的顺序被检查

3.如果对下一个类存在两个合法的选择,选择第一个父类

例如示例二有共同头部父类的模型,新式类mro输出表如下,按照表顺序进行继承:

1 print(A.mro())2 [, , , , , , , , ]

子类调用父类的方法(内置函数super)

low版调用方法,还是那个teacher还是那个people:

1 classPeople:2 def __init__(self,name,age,sex):3 self.name=name4 self.age=age5 self.sex=sex6 deffoo(self):7 print('from parent')8

9 classTeacher(People):10 def __init__(self,name,age,sex,salary,level):11 People.__init__(self,name,age,sex) #指名道姓地调用People类的__init__函数

12 self.salary=salary13 self.level=level14 deffoo(self):15 print('from child')16

17 t=Teacher('bob',18,'male',3000,10)18 print(t.name,t.age,t.sex,t.salary,t.level)19 t.foo()

low版调用方法,在更改父类的名字之后,需要改动的地方除了子类继承的父类名字,还要改子类里面调用的父类名,比较麻烦

高端大气调用方式:只需要改动子类继承的父类名,即括号里的父类名字

1 classPeople:2 def __init__(self,name,age,sex):3 self.name=name4 self.age=age5 self.sex=sex6 deffoo(self):7 print('from parent')8

9 classTeacher(People):10 def __init__(self,name,age,sex,salary,level):11 #在python3中

12 super().__init__(name,age,sex) #调用父类的__init__的功能,实际上用的是绑定方法,用到了mro表查询继承顺序,只能调用一个父类的功能

13 #在python2中

14 #super(Teacher,self).__init__(name,age,sex) #super(Teacher,self)是一个死格式

15 self.salary=salary16 self.level=level17 deffoo(self):18 super().foo()19 print('from child')20

21 t=Teacher('bob',18,'male',3000,10)22 print(t.name,t.age,t.sex,t.salary,t.level)23 t.foo()

但是这种方式也有一个缺点,就是当一个子类继承了多个父类的时候,如果多个父类都包含了相同的属性名,当要调用该功能的时候,只能调用第一个父类的功能,无法实现多个父类同时调用。多个父类同时调用还是要用low版方法。

封装

封装是一种隐藏的方式,包括数据封装和功能封装,即类里的数据属性和功能属性,隐藏数据和功能是为了限制直接调用,通过人为的添加调用接口进行数据和功能的调用。

封装不是单纯意义的隐藏:(史上最lowB的解释)

1:封装数据的主要原因是:保护隐私(作为男人的你,脸上就写着:我喜欢男人,你害怕么?)

2:封装方法的主要原因是:隔离复杂度,提供简单的访问接口(快门就是傻瓜相机为傻瓜们提供的接口,该方法将内部复杂的照相功能都隐藏起来了,拍照只需要通过快门这个接口就可以了,再比如你不必知道你自己的尿是怎么流出来的,你直接掏出自己的接口就能用尿这个功能)

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

封装的两个层面

基础的封装(什么都不用做):创建类和对象会创建各自的名称空间,通过类名.或者对象.的方式去访问类或对象里面的数据属性和功能属性。

还是这个people

1 classPeople:2 def __init__(self,name,age,sex):3 self.name=name4 self.age=age5 self.sex=sex6 deffoo(self):7 print('from parent')8 print(People.__dict__)9 p=People('natasha',18,'female')10 print(p.name)11 p.foo()

通过p.name访问到了natasha,通过p.age访问到了18,这一类就是最基础的类和对象的封装,而p.name、p.foo()就是接口,访问数据属性和功能属性的接口。

二层封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

封装方式:在python中用双下划线的方式实现隐藏属性(设置成私有的)

1 classTeacher:2 __school='oldboy' #实际上转换成了_Teacher__school

3 def __init__(self,name,salary):4 self.name=name5 self.__salary=salary #实际上转换成了self._Teacher__salary

6 def __foo(self):7 print('====foo====')8 t=Teacher('egon',3000)9

10 #print(t.__school) #无法调用

11 print(Teacher.__dict__)12 #t.foo() #无法调用

13 t._Teacher__foo()14 #print(t.salary) #无法调用

15 #print(t.__salary) #无法调用

16 print(t.__dict__)17 print(t._Teacher__salary)

python中的隐藏并不是真正意义上的隐藏,而是通过语法这一层面进行转换,虽然无法直接通过例如t.__salary或t.salary的方式调用,但是实际上在类的__dict__中可以查看到变形后的调用方式

类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式,但是这种变形操作只在定义阶段发生,后边手动添加的不会自动变形

1 Teacher.__N=111111

2 print(Teacher.__dict__)3 t.__x=1

4 print(t.__dict__)5 输出结果:6 {'__module__': '__main__', '_Teacher__school': 'oldboy', '__init__': , '_Teacher__foo': , '__dict__': , '__weakref__': , '__doc__': None, '__N': 111111}7 {'name': 'egon', '_Teacher__salary': 3000, '__x': 1}

在类的外部,无法直接使用变形的属性,但是在类的内部可以直接使用

1 classTeacher:2 __school='oldboy' #_Teacher__school='oldboy'

3 def __init__(self,name,salary):4 self.name=name5 self.__salary=salary #self._Teacher__salary=salary

6

7 deffoo(self):8 print('====>',self.__salary) #内部可以调用

9 #print('====>',self._Teacher__salary)

10 t=Teacher('egon',3000)11

12 #print(t.__salary) #外部无法调用

13 t.foo()

当子类和父类有相同的功能属性,两个类里变形过的功能可以分别调用:

不变形的功能只能调用到子类里的,无法调用父类的func功能

1 classFoo:2 deffunc(self):3 print('from Foo')4 classBar(Foo):5 deffunc(self):6 print('from Bar')7 b=Bar()8 b.func()

变形后可以分别调用

1 classFoo:2 def __func(self): #_Foo__func

3 print('from Foo')4

5 classBar(Foo):6 def __func(self): #_Bar__func

7 print('from Bar')8 b=Bar()9 b._Foo__func()10 b._Bar__func()

类里的功能属性和功能属性间调用:

A类和B类同时包含bar功能,A类通过foo功能调用自己的bar功能,通过B实例化b对象,当b对象调用foo的时候,由于B类没有foo功能,所以从A类中找foo功能,找到后调用,并在执行foo功能的过程中调用bar功能,按照mro表顺序查找,通过B类内找到bar功能并执行

1 classA:2 deffoo(self):3 print('from A.foo')4 self.bar()

5 def bar(self):

6 print('from A.bar')7 classB(A):8 def bar(self):

9 print('from B.bar')10 b=B()11 b.foo()12 输出结果13 fromA.foo14 from B.bar

变形后调用:定义的过程中已经变形了,所以foo功能在找bar函数的时候实际上找的是变形后的_A__bar()功能

1 classA:2 deffoo(self):3 print('from A.foo')4 self.__bar() #self._A__bar()

5 def __bar(self): #_A__bar()

6 print('from A.bar')7 classB(A):8 def __bar(self): #_B__bar

9

10 b=B()11 b.foo()

隐藏所有直接调用属性,通过接口的方式调用属性:又来了,还是那个people

1 classPeople:2 def __init__(self,name,age,sex,height,weight):3 self.__name=name4 self.__age=age5 self.__sex=sex6 self.__height=height7 self.__weight=weight8 #name、age、sex、height、weight都是经过变形后存储的,所以在调用的时候没办法直接调用,当然了要调用是可以的

9 deftell_name(self):10 print(self.__name)11 #通过手动创建接口的方式返回name的内容,屏蔽了直接调用

12 defset_name(self,val):13 if notisinstance(val,str):14 raise TypeError('名字必须是字符串类型')15 self.__name=val16 #通过手动创建修改接口修改name的属性值,屏蔽了直接调用

17 deftell_info(self):18 print('''

19 ---------%s info20 name:%s21 age:%s22 sex:%s23 height:%s24 weight:%s25 ''' %(self.__name,26 self.__name,27 self.__age,28 self.__sex,29 self.__height,30 self.__weight))31 #通过手动创建接口,展示所有的信息

测试验证:

1 bob=People('bob',18,'male','179cm','70kg') #实例化对象

2 bob.tell_info() #通过接口查看bob的所有信息

3 bob.tell_name() #通过接口查看name属性

4 #bob.set_name(123)

5 bob.set_name('natasha') #通过接口修改name属性值

6 bob.tell_info()

property:封装的特性之一

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

手动创建的接口都是函数接口,函数接口在调用的时候都需要加()执行才能调用,如上边的例子bob.tell_name()通过接口查询name属性,基于用户角度来讲,比较显得美好简单的调用方式是bob.name,用户心理毛病多:我只是想看一下名字,为什么要我执行的这个东西?

示例:计算bmi健康指数

1 classPeople:2 def __init__(self,name,age,sex,height,weight):3 self.__name=name4 self.__age=age5 self.__sex=sex6 self.__height=height7 self.__weight=weight8

9 @property #bmi=property(bmi),是一个内置函数,本质就是个装饰器

10 defbmi(self):11 res=self.__weight / (self.__height ** 2)12 return res

测试验证:

1 bob=People('bob',18,'male',1.79,70)2 print(bob.bmi)  #当调用bob.bmi时候,会返回res的值

使用这种方式,遵循了统一访问的原则,即用户感知不到我是执行了一个函数才获取的值。

但是仅仅这样,还是有问题,比如我想要删除一个属性,是无法删除的,比如del bmi,会提示AttributeError: can't delete attribute,想要通过bob.name='NAME'的方式修改内容也是不行的。

想要实现,需要继续加装饰器:

1 classPeople:2 def __init__(self,name,age,sex,height,weight,permission=False):3 self.__name=name4 self.__age=age5 self.__sex=sex6 self.__height=height7 self.__weight=weight8 self.permission=permission9

10 @property11 defname(self):12 return self.__name

13

14 @name.setter #支持obj.name='NAME'的方式执行

15 defname(self,val):16 if notisinstance(val,str):17 raise TypeError('must be str')18 self.__name=val19

20 @name.deleter #支持del删除操作

21 defname(self):22 if notself.permission:23 raise PermissionError('不让删')24 del self.__name

测试验证:

1 natasha.name=123

2 print(natasha.name)3 print(natasha.permission)4 natasha.permission=True #不改成True,if认证不通过会删除失败

5 delnatasha.name6 #print(egon.name) #无法查询,已删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值