python类的继承原理

一、类的继承顺序

class A(object):
def test(self):
print('from A')
pass
class B(A):
# def test(self):
# print('from B')
pass
class C(A):
# def test(self):
# print('from C')
pass
class D(A):
# def test(self):
# print('from D')
pass
class E(B):
# def test(self):
# print('from E')
pass
class F(C):
# def test(self):
# print('from F')
pass
class G(D):
# def test(self):
# print('from G')
pass
class H(E,F,G):
# def test(self):
# print('from H')
pass
h=H()
# h.test=1
# print h.__dict__
#新式类的在这中继承结构下,属性的查找关系
# h.test()

# H->E->B->F->C-G-D-A 广度优先
#mro
print(H.mro())

二、类的继承原理

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类

三、子类调用父类的方法

两种方式 :一个是直接指明道姓的调用父类的函数属性还有一个是调用父类的__init__的功能,实际上用的是绑定方法。

class People:
# def __init__(self,name,age,sex):
# self.name=name
# self.age=age
# self.sex=sex
# def foo(self):
# print('from parent')
#
# class Teacher(People):
# def __init__(self,name,age,sex,salary,level):
# # People.__init__(self,name,age,sex) #指名道姓地调用People类的__init__函数
#
# #在python3中
# super().__init__(name,age,sex) #调用父类的__init__的功能,实际上用的是绑定方法
#
# #在python2中
# # super(Teacher,self).__init__(name,age,sex)
#
#
# self.salary=salary
# self.level=level
# def foo(self):
# super().foo()
# print('from child')
#
#
# t=Teacher('egon',18,'male',3000,10)
# # print(t.name,t.age,t.sex,t.salary,t.level)
# t.foo()

注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

四封装

第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装

注意:对于这一层面的封装(隐藏),类名.和实例名.就是访问隐藏属性的接口

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

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

类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

 
#封装数据
#封装功能
class Teacher:
__school='oldboy' #_Teacher__school
def __init__(self,name,salary):
self.name=name
self.__salary=salary #self._Teacher__salary
 
def __foo(self):
print('====>')
t=Teacher('egon',3000)
 
# print(t.__school)
# print(Teacher.__dict__)
 
# t.foo()
# t._Teacher__foo()
 
# print(t.salary)
# print(t.__salary)
# print(t.__dict__)
#
# print(t._Teacher__salary)
 
#这种变形操作只在定义阶段发生
# Teacher.__N=111111
# print(Teacher.__dict__)
#
#
# t.__x=1
# print(t.__dict__)
 
#在类的外部,无法直接使用变形的属性,但是在类的内部可以直接使用
class Teacher:
__school='oldboy' #_Teacher__school='oldboy'
def __init__(self,name,salary):
self.name=name
self.__salary=salary #self._Teacher__salary=salary
 
def foo(self):
print('====>',self.__salary)
# print('====>',self._Teacher__salary)
t=Teacher('egon',3000)
 
print(t.__salary)
t.foo()

这种自动变形的特点:

1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果

2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

2.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

注意:对于这一层面的封装(隐藏),我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了

五、类的特性(property

1 什么是特性property

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

2 为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

上代码:

class People:
def __init__(self,name,age,sex,height,weight,permission=False):
self.__name=name
self.__age=age
self.__sex=sex
self.__height=height
self.__weight=weight
self.permission=permission @property
def name(self):
return self.__name
@name.setter
def name(self,val):
if not isinstance(val,str):
raise TypeError('名字必须为字符串!')
self.__name=val
@name.deleter
def name(self):
if not self.permission:
raise PermissionError('权限不够!')
del self.__name
@property
def bmi(self):
return self.__weight/(self.__height**2)
def tell_info(self):
print('''
---------%s info------------
Name:%s
Age:%s
Sex:%s
Height:%sM
Weight:%sKG
---------------------------------
'''%(self.__name,self.__name,self.__age,self.__sex,self.__height,self.__weight))
niubin=People('niubin',20,'male',1.70,68)
niubin.tell_info()
print(niubin.name)
print(niubin.bmi)

Python进阶---面向对象第二弹的更多相关文章

  1. [Python3] 022 面向对象 第二弹

    目录 6. 面向对象的三大特性 6.1 封装 6.1.1 私有 private 6.1.2 受保护 protected 6.1.3 公开 public 6.2 继承 6.2.1 继承的概念与作用 6. ...

  2. Python进阶---面向对象的程序设计思想

    Python的面向对象 一.面向过程与面向对象的对比 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西. 优 ...

  3. [Markdown] 04 进阶语法 第二弹

    [TOC] 接上一篇 [Mardkown] 03 进阶语法 第一弹 8. LaTeX 8.1 相关介绍 TeX:学术排版 LaTeX:相当于 TeX 的简化版本:对公式编辑精细至像素级别 MathJa ...

  4. Python进阶---面向对象第三弹(进阶篇)

    Python对象中一些方法 一.__str__ class Teacher: def __init__(self,name,age): self.name=name self.age=age self ...

  5. Python进阶【第二篇】多线程、消息队列queue

    1.Python多线程.多进程 目的提高并发 1.一个应用程序,可以有多进程和多线程 2.默认:单进程,单线程 3.单进程,多线程 IO操作,不占用CPU python的多线程:IO操作,多线程提供并 ...

  6. Python 进阶 - 面向对象

    Python 面向对象 面向过程 把完成某个需求的所有步骤,从头到尾逐步实现 根据开发需求,将某些功能独立的代码封装成一个又一个函数 最后完成的代码,就是顺序地调用不同的函数 面向过程特点: 注重步骤 ...

  7. Python进阶-面向对象

    类的成员 类的成员可以分为三类:字段.方法.属性 一:字段: 普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同 普通字段属于对象 静态字段属于类 字段的定义和使用 ...

  8. Python进阶(面向对象编程基础)(三)

    6.类属性和实例属性名字冲突怎么办 修改类属性会导致所有实例访问到的类属性全部都受影响,但是,如果在实例变量上修改类属性会发生什么问题呢? class Person(object): address ...

  9. Python进阶(面向对象编程基础)(二)

    1.初始化实例属性 #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'ziv·chan' #定义Person类的__init__方法 ...

随机推荐

  1. 在jsp页面的js中使用Cookie的原理介绍以及相应方法的代码

    1. 设置cookie 1.1 每个cookie都是一个名/值对,可以把下面这样一个字符串赋值给document.cookie: document.cookie="user_Id=828&q ...

  2. 利用python生成交换机的VRF配置文件

    为了快速生成有规律的VRF,写了一个python脚本,可以快速生成如下的VRF配置. ip vpn-instance  vpn0ipv4-family  route-distinguisher 600 ...

  3. 奥利奥好吃吗?Android 8.0新特性适配测试报告来啦!

    WeTest 导读 谷歌2017 I/O开发者大会上发布了Android 8.0的正式版, 其官方代号为Oreo(奥利奥).网上关于Android8.0新功能特性的介绍已铺天盖地,新功能特性会对程序应 ...

  4. TensorFlow问题:The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.

    1. 问题描述 The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available o ...

  5. bzoj1812 [Ioi2005]riv

    riv 几乎整个Byteland王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海.这条大河的入海口处有一个村庄--名叫B ...

  6. Spring读书笔记——bean创建(上)

    通过<Spring读书笔记--bean加载>和<Spring读书笔记--bean解析>,我们明白了两件事. Spring如何加载消化一个xml配置文件 Spring如何将xml ...

  7. (扩展根目录容量方法汇总)把Linux系统迁移到另一个分区或者硬盘

    Linux系统扩容方法汇总 相信很多朋友都有过这样的经历,本想装个Ubantu玩玩,没想到玩久了反而不习惯Windows了,然而开始装系统的时候只分配了非常小的空间,那应该怎样扩展我们的ubantu呢 ...

  8. javascript入门知识点总结(一)

    学了几天javascript,现总结一下原生javascript的基本知识点. 一.javascript基本知识 变量 定义: var a = 123; var b = 'abc'; //连写 var ...

  9. 【ASP.NET MVC 学习笔记】- 20 ASP.NET Web API

    本文参考:http://www.cnblogs.com/willick/p/3441432.html 1.ASP.NET Web API(本文简称Web API),是基于ASP.NET平台构建REST ...

  10. 加载web项目时报的错误:Tomcat version 6.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 Web modul

    用eclipse开发的java项目不能加载到tomcat6.0服务器,原因是:jst.web的版本高了 <installed facet="jst.web" version= ...