继承顺序


  1. '''
  2. 一点需要注意
  3. '''
  4. class Father:
  5. def f1(self):
  6. print("test func followed ==>")
  7. self.test()
  8. def test(self):
  9. print("from Father test")
  10. class Son(Father):
  11. def test(self):
  12. print("from son test")
  13. res=Son()
  14. res.f1()
  1. 结果 >>>:
  2. test func followed ==>
  3. from son test
  4. '''
  5. 子类调用 self.test() 的时候任然要重新从自己开始找 .test()方法。
  6. 子类调用父类的属性,每次调用,都是优先从自己这里开始找,按照mro算法的顺序依次找下去,知道找到第一个符合的,所以不能只看定义的时候的单个类的情况。
  7. '''

新式类继承:广度优先。

经典类继承:深度优先。

继承了object的类以及其子类,都是新式类
没有继承object的类以及其子类,都是经典类
Python3中默认继承object,所以Python3中都是新式类
Python2中不会默认继承object

  1. class A(object):
  2. def test(self):
  3. print('from A')
  4.  
  5. class B(A):
  6. def test(self):
  7. print('from B')
  8.  
  9. class C(A):
  10. def test(self):
  11. print('from C')
  12.  
  13. class D(B):
  14. def test(self):
  15. print('from D')
  16.  
  17. class E(C):
  18. def test(self):
  19. print('from E')
  20.  
  21. class F(D,E):
  22. # def test(self):
  23. # print('from F')
  24. pass
  25. f1=F()
  26. f1.test()
  27. print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
  28.  
  29. #新式类继承顺序:F->D->B->E->C->A
  30. #经典类继承顺序:F->D->B->A->E->C
  31. #python3中统一都是新式类
  32. #pyhon2中才分新式类与经典类
  33.  
  34. 继承顺序

继承原理(python如何实现的继承)

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

  1. >>> F.mro() #等同于F.__mro__
  2. [<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. 1 class People:
  2. 2 def __init__(self,name,age,sex):
  3. 3 self.name=name
  4. 4 self.age=age
  5. 5 self.sex=sex
  6. 6 def foo(self):
  7. 7 print('from parent')
  8. 8
  9. 9 class Teacher(People):
  10. 10 def __init__(self,name,age,sex,salary,level):
  11. 11 People.__init__(self,name,age,sex) #指名道姓地调用People类的__init__函数
  12. 12 self.salary=salary
  13. 13 self.level=level
  14. 14 def foo(self):
  15. 15 print('from child')
  16. 16
  17. 17 t=Teacher('bob',18,'male',3000,10)
  18. 18 print(t.name,t.age,t.sex,t.salary,t.level)
  19. 19 t.foo()

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

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

  1. 1 class People:
  2. 2 def __init__(self,name,age,sex):
  3. 3 self.name=name
  4. 4 self.age=age
  5. 5 self.sex=sex
  6. 6 def foo(self):
  7. 7 print('from parent')
  8. 8
  9. 9 class Teacher(People):
  10. 10 def __init__(self,name,age,sex,salary,level):
  11. 11 #在python3中
  12. 12 super().__init__(name,age,sex) #调用父类的__init__的功能,实际上用的是绑定方法,用到了mro表查询继承顺序,只能调用一个父类的功能
  13. 13 #在python2中
  14. 14 # super(Teacher,self).__init__(name,age,sex) #super(Teacher,self)是一个死格式
  15. 15 self.salary=salary
  16. 16 self.level=level
  17. 17 def foo(self):
  18. 18 super().foo()
  19. 19 print('from child')
  20. 20
  21. 21 t=Teacher('bob',18,'male',3000,10)
  22. 22 print(t.name,t.age,t.sex,t.salary,t.level)
  23. 23 t.foo()

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

访问限制


在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。

但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的namescore属性:

  1. >>> bart = Student('Bart Simpson', 98)
  2. >>> bart.score
  3. 98
  4. >>> bart.score = 59
  5. >>> bart.score
  6. 59

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:

  1. class Student(object):
  2. def __init__(self, name, score):
  3. self.__name = name
  4. self.__score = score
  5. def print_score(self):
  6. print('%s: %s' % (self.__name, self.__score))

改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name实例变量.__score了:

  1. >>> bart = Student('Bart Simpson', 98)
  2. >>> bart.__name
  3. Traceback (most recent call last):
  4. File "<stdin>", line 1, in <module>
  5. 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变量:

  1. >>> bart._Student__name
  2. 'Bart Simpson'

但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。

总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。

最后注意下面的这种错误写法:

  1. >>> bart = Student('Bart Simpson', 98)
  2. >>> bart.get_name()
  3. 'Bart Simpson'
  4. >>> bart.__name = 'New Name' # 设置__name变量!
  5. >>> bart.__name
  6. 'New Name'

表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量。不信试试:

  1. >>> bart.get_name() # get_name()内部返回self.__name
  2. 'Bart Simpson'

但是如果外部代码要获取name和score怎么办?可以给Student类增加get_nameget_score这样的方法:

  1. class Student(object):
  2. ...
  3. def get_name(self):
  4. return self.__name
  5. def get_score(self):
  6. return self.__score

如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score方法:

  1. class Student(object):
  2. ...
  3. def set_score(self, score):
  4. self.__score = score

你也许会问,原先那种直接通过bart.score = 59也可以修改啊,为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数:

  1. class Student(object):
  2. ...
  3. def set_score(self, score):
  4. if 0 <= score <= 100:
  5. self.__score = score
  6. else:
  7. raise ValueError('bad score')

但是这样来更改一个属性还是显得比较诡异,因为一个原本 self.name=[名字] 的操作,变成了一个函数来操作。

但是你任然可以将隐藏的属性伪装成一个正常属性,类的内部对这个变量的操作进行限制,或者参数检查等功能。

  1. class People:
  2. def __init__(self,name,permission=False):
  3. self.__name=name
  4. self.permission=permission
  5.  
  6. @property #将 self.name() 变成了 self.name
  7. def name(self):
  8. return self.__name
  9.  
  10. @name.setter #让 self.name 可以像正常属性一样可以用‘=’操作设置属性的新值 self.name='[新值]'
  11. def name(self,val):
  12. if not isinstance(val,str):
  13. raise TypeError('must be str') #raise 定义错误信息
  14. self.__name=val
  15.  
  16. @name.deleter #可以 del self.name 删除属性
  17. def name(self):
  18. if not self.permission:
  19. raise PermissionError('不让删')
  20. del self.__name
  21.  
  22. snow=People('***snow***')
  23. print(snow.name)
  24. print(snow.permission)
  25. snow.permission=True
  26. del snow.name
  27. print(snow.name)
  1. class Foo:
  2. def __init__(self,val):
  3. self.__NAME=val #将所有的数据属性都隐藏起来
  4.  
  5. def getname(self):
  6. return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
  7.  
  8. def setname(self,value):
  9. if not isinstance(value,str): #在设定值之前进行类型检查
  10. raise TypeError('%s must be str' %value)
  11. self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
  12.  
  13. def delname(self):
  14. raise TypeError('Can not delete')
  15.  
  16. name=property(getname,setname,delname) #不如装饰器的方式清晰
  17.  
  18. 一种property的古老用法

一种property的古老用法

  1.  
  1.  

绑定方法与非绑定方法


类中定义的函数分成两大类:

  一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    1. 绑定到类的方法:用classmethod装饰器装饰的方法。

                为类量身定制

                类.boud_method(),自动将类当作第一个参数传入

              (其实对象也可调用,但仍将类当作第一个参数传入)

    2. 绑定到对象的方法:没有被任何装饰器装饰的方法。

               为对象量身定制

               对象.boud_method(),自动将对象当作第一个参数传入

             (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

  二:非绑定方法:用staticmethod装饰器装饰的方法

     1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

    注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

1 staticmethod

statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果,python为我们内置了函数staticmethod来把类中的函数定义成静态方法

  1. import hashlib
  2. import time
  3. class MySQL:
  4. def __init__(self,host,port):
  5. self.id=self.create_id()
  6. self.host=host
  7. self.port=port
  8. @staticmethod
  9. def create_id(): #就是一个普通工具
  10. m=hashlib.md5(str(time.clock()).encode('utf-8'))
  11. return m.hexdigest()
  12.  
  13. print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
  14. conn=MySQL('127.0.0.1',3306)
  15. print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数

2 classmethod

  classmehtod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数(即便是对象来调用也会将类当作第一个参数传入),python为我们内置了函数classmethod来把类中的函数定义成类方法

  1. import settings
  2. import hashlib
  3. import time
  4. class MySQL:
  5. def __init__(self,host,port):
  6. self.host=host
  7. self.port=port
  8.  
  9. @classmethod
  10. def from_conf(cls):
  11. print(cls)
  12. return cls(settings.HOST,settings.PORT)
  13.  
  14. print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
  15. conn=MySQL.from_conf()
  16.  
  17. print(conn.host,conn.port)
  18. conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类

Python(面向对象编程4——继承顺序、封装)的更多相关文章

  1. Python面向对象编程-类的封装,继承、多态

    面向对象是一种程序设计思想,对象作为程序基本单元,包含了数据和操作数据的函数. 面向对象的三大特点--数据封装.多态和继承. #类的创建,class关键字,类名大写,object表示从哪个类继承而来, ...

  2. Python面向对象编程(下)

    本文主要通过几个实例介绍Python面向对象编程中的封装.继承.多态三大特性. 封装性 我们还是继续来看下上文中的例子,使用Student类创建一个对象,并修改对象的属性.代码如下: #-*- cod ...

  3. python 面向对象编程(一)

    一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义: class c ...

  4. python面向对象编程进阶

    python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 1 ...

  5. python面向对象编程学习

    python面向对象编程 基本概念理解 面向对象编程--Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作 ...

  6. Python面向对象编程——继承与派生

    Python面向对象编程--继承与派生 一.初始继承 1.什么是继承 继承指的是类与类之间的关系,是一种什么"是"什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创 ...

  7. python 面向对象编程学习

    1. 问题:将所有代码放入一个py文件:无法维护 方案:如果将代码才分放到多个py文件,好处: 1. 同一个名字的变量互相不影响 2.易于维护 3.引用模块: import module 2.包:解决 ...

  8. Python面向对象编程指南

    Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...

  9. Python 面向对象编程——访问限制

    <无访问限制的对象> 在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑.但是,从前面Student类的定义来看(见:Py ...

  10. Python 面向对象编程 继承 和多态

    Python 面向对象编程 继承 和多态 一:多继承性 对于java我们熟悉的是一个类只能继承一个父类:但是对于C++ 一个子类可以有多个父亲,同样对于 Python一个类也可以有多个父亲 格式: c ...

随机推荐

  1. go 语言学习笔计之结构体

    go 语言中的结构体方法 结构体名称的大小写有着不同的意义: 小写表示不能被别的包访问 package main import "fmt" type Rect struct { w ...

  2. javascript属性详解

    在js中,没有公共属性和私有属性之分,只有全局变量(全局属性)和局部变量以及对象属性.但是,程序员可以通过一定的技巧来实现面向对象语言的功能.下面我们来介绍这几种属性(变量) 全局变量也叫全局属性 / ...

  3. redis-3.0.6安装

    此redis用来缓存跨屏账户绑定信息,安装步骤如下: ssh root@redis.td.com ,注意是root用户 tar -xzvf /nfs/install/softs/redis-3.0.4 ...

  4. bootstrap基础学习十篇

    bootstrap字体图标(Glyphicons) a.什么是字体图标 字体图标是在 Web 项目中使用的图标字体.虽然,Glyphicons Halflings 需要商业许可,但是您可以通过基于项目 ...

  5. tplink 703刷固件

    1.软件下载: ImageBuilder链接 如果是全新刷机的话,使用:http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/open ...

  6. vmware 虚拟机下 ubuntu 与主机共享锐捷

    一直以来.想要学习 linux ,在 vm 虚拟机下安装了 ubuntu 系统. 可是这个系统并不能上网.原因就是 vm 虚拟机的虚拟网卡会和锐捷冲突.锐捷会检測到多网卡,断开网络,所以不得不禁用 v ...

  7. iOS开发之 -- 判断tableview/scrollview的滑动方法,及导航栏渐变的实现代码

    开发的过程中,肯定会用到在视图想上滑动的时候,在导航处做一些操作,比如向上滑动的时候,做个动画,出现一个搜索框,或者其他的操作,那么我们怎么来判断它的滑动方向呢? 首先我们应该知道tableview继 ...

  8. 分享申请IDP账号的过程,包含duns申请的分享

    本文转载至 http://www.cocoachina.com/ios/20140325/8038.html 5月份接到公司要申请开发者账号的任务,就一直在各个论坛找申请的流程,但都是一些09年10年 ...

  9. iOS-Pods里三方文件导入找不到头文件

    解决办法: 在target 里面 选择 build setting 搜索User Header Search Paths 然后 里面添加一条 ${SRCROOT}     recursive//后面填 ...

  10. [.NET网格计算框架] Alchemi

      Alchemi [.NET网格计算框架] 是 一个以使用简易为目的的Windows下的网格计算框架.它提供了:a)开发网格软件的编程环境 和 b)建造网格和运行网格软件的运行机制.       A ...