Python(面向对象编程4——继承顺序、封装)
继承顺序
- '''
- 一点需要注意
- '''
- class Father:
- def f1(self):
- print("test func followed ==>")
- self.test()
- def test(self):
- print("from Father test")
- class Son(Father):
- def test(self):
- print("from son test")
- res=Son()
- res.f1()
- 结果 >>>:
- test func followed ==>
- from son test
- '''
- 子类调用 self.test() 的时候任然要重新从自己开始找 .test()方法。
- 子类调用父类的属性,每次调用,都是优先从自己这里开始找,按照mro算法的顺序依次找下去,知道找到第一个符合的,所以不能只看定义的时候的单个类的情况。
- '''
新式类继承:广度优先。
经典类继承:深度优先。
继承了object的类以及其子类,都是新式类
没有继承object的类以及其子类,都是经典类
Python3中默认继承object,所以Python3中都是新式类
Python2中不会默认继承object
- class A(object):
- def test(self):
- print('from A')
- class B(A):
- def test(self):
- print('from B')
- class C(A):
- def test(self):
- print('from C')
- class D(B):
- def test(self):
- print('from D')
- class E(C):
- def test(self):
- print('from E')
- class F(D,E):
- # def test(self):
- # print('from F')
- pass
- f1=F()
- f1.test()
- print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
- #新式类继承顺序:F->D->B->E->C->A
- #经典类继承顺序:F->D->B->A->E->C
- #python3中统一都是新式类
- #pyhon2中才分新式类与经典类
- 继承顺序
继承原理(python如何实现的继承)
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.如果对下一个类存在两个合法的选择,选择第一个父类
子类调用父类的方法(内置函数super)
low版调用方法,还是那个teacher还是那个people:

- 1 class People:
- 2 def __init__(self,name,age,sex):
- 3 self.name=name
- 4 self.age=age
- 5 self.sex=sex
- 6 def foo(self):
- 7 print('from parent')
- 8
- 9 class Teacher(People):
- 10 def __init__(self,name,age,sex,salary,level):
- 11 People.__init__(self,name,age,sex) #指名道姓地调用People类的__init__函数
- 12 self.salary=salary
- 13 self.level=level
- 14 def foo(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 class People:
- 2 def __init__(self,name,age,sex):
- 3 self.name=name
- 4 self.age=age
- 5 self.sex=sex
- 6 def foo(self):
- 7 print('from parent')
- 8
- 9 class Teacher(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=salary
- 16 self.level=level
- 17 def foo(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版方法。
访问限制
在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name
、score
属性:
>>> bart = Student('Bart Simpson', 98)
>>> bart.score
98
>>> bart.score = 59
>>> bart.score
59
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name
和实例变量.__score
了:
>>> bart = Student('Bart Simpson', 98)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
需要注意的是,在Python中,变量名类似__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。
有些时候,你会看到以一个下划线开头的实例变量名,比如_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
查看__dict__可以看出,Python实际上是在类、对象,在定义的时候,将这类变量转换成了类似 _Student__name 的格式
不能直接访问__name
是因为Python解释器对外把__name
变量改成了_Student__name
,所以,仍然可以通过_Student__name
来访问__name
变量:
>>> bart._Student__name
'Bart Simpson'
但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name
改成不同的变量名。
总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
最后注意下面的这种错误写法:
>>> bart = Student('Bart Simpson', 98)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'
表面上看,外部代码“成功”地设置了__name
变量,但实际上这个__name
变量和class内部的__name
变量不是一个变量!内部的__name
变量已经被Python解释器自动改成了_Student__name
,而外部代码给bart
新增了一个__name
变量。不信试试:
>>> bart.get_name() # get_name()内部返回self.__name
'Bart Simpson'
但是如果外部代码要获取name和score怎么办?可以给Student类增加get_name
和get_score
这样的方法:
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score
方法:
class Student(object):
...
def set_score(self, score):
self.__score = score
你也许会问,原先那种直接通过bart.score = 59
也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数:
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
但是这样来更改一个属性还是显得比较诡异,因为一个原本 self.name=[名字] 的操作,变成了一个函数来操作。
但是你任然可以将隐藏的属性伪装成一个正常属性,类的内部对这个变量的操作进行限制,或者参数检查等功能。
- class People:
- def __init__(self,name,permission=False):
- self.__name=name
- self.permission=permission
- @property #将 self.name() 变成了 self.name
- def name(self):
- return self.__name
- @name.setter #让 self.name 可以像正常属性一样可以用‘=’操作设置属性的新值 self.name='[新值]'
- def name(self,val):
- if not isinstance(val,str):
- raise TypeError('must be str') #raise 定义错误信息
- self.__name=val
- @name.deleter #可以 del self.name 删除属性
- def name(self):
- if not self.permission:
- raise PermissionError('不让删')
- del self.__name
- snow=People('***snow***')
- print(snow.name)
- print(snow.permission)
- snow.permission=True
- del snow.name
- print(snow.name)
- class Foo:
- def __init__(self,val):
- self.__NAME=val #将所有的数据属性都隐藏起来
- def getname(self):
- return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
- def setname(self,value):
- if not isinstance(value,str): #在设定值之前进行类型检查
- raise TypeError('%s must be str' %value)
- self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
- def delname(self):
- raise TypeError('Can not delete')
- name=property(getname,setname,delname) #不如装饰器的方式清晰
- 一种property的古老用法
一种property的古老用法
绑定方法与非绑定方法
类中定义的函数分成两大类:
一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):
1. 绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.boud_method(),自动将类当作第一个参数传入
(其实对象也可调用,但仍将类当作第一个参数传入)
2. 绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.boud_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
二:非绑定方法:用staticmethod装饰器装饰的方法
1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
1 staticmethod
statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
- import hashlib
- import time
- class MySQL:
- def __init__(self,host,port):
- self.id=self.create_id()
- self.host=host
- self.port=port
- @staticmethod
- def create_id(): #就是一个普通工具
- m=hashlib.md5(str(time.clock()).encode('utf-8'))
- return m.hexdigest()
- print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
- conn=MySQL('127.0.0.1',3306)
- print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数
2 classmethod
classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法
- import settings
- import hashlib
- import time
- class MySQL:
- def __init__(self,host,port):
- self.host=host
- self.port=port
- @classmethod
- def from_conf(cls):
- print(cls)
- return cls(settings.HOST,settings.PORT)
- print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
- conn=MySQL.from_conf()
- print(conn.host,conn.port)
- conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类
Python(面向对象编程4——继承顺序、封装)的更多相关文章
- Python面向对象编程-类的封装,继承、多态
面向对象是一种程序设计思想,对象作为程序基本单元,包含了数据和操作数据的函数. 面向对象的三大特点--数据封装.多态和继承. #类的创建,class关键字,类名大写,object表示从哪个类继承而来, ...
- Python面向对象编程(下)
本文主要通过几个实例介绍Python面向对象编程中的封装.继承.多态三大特性. 封装性 我们还是继续来看下上文中的例子,使用Student类创建一个对象,并修改对象的属性.代码如下: #-*- cod ...
- python 面向对象编程(一)
一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义: class c ...
- python面向对象编程进阶
python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 1 ...
- python面向对象编程学习
python面向对象编程 基本概念理解 面向对象编程--Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作 ...
- Python面向对象编程——继承与派生
Python面向对象编程--继承与派生 一.初始继承 1.什么是继承 继承指的是类与类之间的关系,是一种什么"是"什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创 ...
- python 面向对象编程学习
1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...
- Python面向对象编程指南
Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...
- Python 面向对象编程——访问限制
<无访问限制的对象> 在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑.但是,从前面Student类的定义来看(见:Py ...
- Python 面向对象编程 继承 和多态
Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...
随机推荐
- go 语言学习笔计之结构体
go 语言中的结构体方法 结构体名称的大小写有着不同的意义: 小写表示不能被别的包访问 package main import "fmt" type Rect struct { w ...
- javascript属性详解
在js中,没有公共属性和私有属性之分,只有全局变量(全局属性)和局部变量以及对象属性.但是,程序员可以通过一定的技巧来实现面向对象语言的功能.下面我们来介绍这几种属性(变量) 全局变量也叫全局属性 / ...
- redis-3.0.6安装
此redis用来缓存跨屏账户绑定信息,安装步骤如下: ssh root@redis.td.com ,注意是root用户 tar -xzvf /nfs/install/softs/redis-3.0.4 ...
- bootstrap基础学习十篇
bootstrap字体图标(Glyphicons) a.什么是字体图标 字体图标是在 Web 项目中使用的图标字体.虽然,Glyphicons Halflings 需要商业许可,但是您可以通过基于项目 ...
- tplink 703刷固件
1.软件下载: ImageBuilder链接 如果是全新刷机的话,使用:http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/open ...
- vmware 虚拟机下 ubuntu 与主机共享锐捷
一直以来.想要学习 linux ,在 vm 虚拟机下安装了 ubuntu 系统. 可是这个系统并不能上网.原因就是 vm 虚拟机的虚拟网卡会和锐捷冲突.锐捷会检測到多网卡,断开网络,所以不得不禁用 v ...
- iOS开发之 -- 判断tableview/scrollview的滑动方法,及导航栏渐变的实现代码
开发的过程中,肯定会用到在视图想上滑动的时候,在导航处做一些操作,比如向上滑动的时候,做个动画,出现一个搜索框,或者其他的操作,那么我们怎么来判断它的滑动方向呢? 首先我们应该知道tableview继 ...
- 分享申请IDP账号的过程,包含duns申请的分享
本文转载至 http://www.cocoachina.com/ios/20140325/8038.html 5月份接到公司要申请开发者账号的任务,就一直在各个论坛找申请的流程,但都是一些09年10年 ...
- iOS-Pods里三方文件导入找不到头文件
解决办法: 在target 里面 选择 build setting 搜索User Header Search Paths 然后 里面添加一条 ${SRCROOT} recursive//后面填 ...
- [.NET网格计算框架] Alchemi
Alchemi [.NET网格计算框架] 是 一个以使用简易为目的的Windows下的网格计算框架.它提供了:a)开发网格软件的编程环境 和 b)建造网格和运行网格软件的运行机制. A ...