python 全栈开发,Day20(object类,继承与派生,super方法,钻石继承)
先来讲一个例子
老师有生日,怎么组合呢?
class Birthday: # 生日
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day class Teacher: # 老师
def __init__(self,name,birth):
self.name = name
self.birthday = birth alex = Teacher('alex','2018-7-14')
print(alex.birthday)
执行输出:
2018-7-14
但是这么传日期不好,需要分开,使用组合方式。
class Birthday:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day class Teacher:
def __init__(self,name,birth):
self.name = name
self.birthday = birth birth = Birthday(2018,7,14)
alex = Teacher('alex',birth)
print(birth.year)
print(alex.birthday.year) # 调用组合对象中的属性
执行输出:
2018
2018
定义一个方法,查看完整的生日
class Birthday:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day def fmt(self):
return '%s-%s-%s'%(self.year,self.month,self.day) class Teacher:
def __init__(self,name,birth):
self.name = name
self.birthday = birth birth = Birthday(2018,7,14)
alex = Teacher('alex',birth)
print(birth.year)
print(alex.birthday.year) # 调用组合对象中的属性
print(alex.birthday.fmt()) # 调用组合对象中的方法,要加括号
执行输出:
2018
2018
2018-7-14
Teacher也可以定义一个方法,执行Birthday类里面的方法
class Birthday:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day def fmt(self):
return '%s-%s-%s'%(self.year,self.month,self.day) class Teacher:
def __init__(self,name,birth):
self.name = name
self.birthday = birth def birth_month(self):
return self.birthday.fmt() # 引用组合对象的方法 birth = Birthday(2018,7,14)
alex = Teacher('alex',birth)
print(birth.year)
print(alex.birthday.year) # 调用组合对象中的属性
print(alex.birthday.fmt()) # 调用组合对象中的方法,要加括号
print(alex.birth_month())
执行输出:
2018
2018
2018-7-14
2018-7-14
组合就是把一个对象,作为另外一个类的属性
讲一个继承的例子:
猫
属性 性别 品种
方法 吃 喝 爬树
狗
属性 性别 品种
方法 吃 喝 看门
从上面可以看出,狗和猫有共同的属性和方法,唯独有一个方法是不一样的。
那么是否可以继承呢?
class Animal: # 动物
def __init__(self,name,sex,kind):
self.name = name
self.sex = sex
self.kind = kind
def eat(self): # 吃
print('%s is eating'%self.name) def drink(self): # 喝
print('%s is drinking'%self.name) class Cat(Animal): # 猫
def climb(self): # 爬树
print('%s is climbing'%self.name) class Dog(Animal): # 狗
def watch_door(self): # 看门
print('%s is watching door'%self.name) tom = Cat('tom','公','招财猫') # 实例化对象
hake = Dog('hake','公','藏獒')
print(Cat.__dict__) # Cat.__dict__ Cat类的命名空间中的所有名字
print(tom.__dict__) # tom.__dict__ 对象的命名空间中的所有名字
tom.eat() # 先找自己对象的内存空间 再找类的空间 再找父类的空间
tom.climb() # 先找自己的内存空间 再找类的空间
执行输出:
{'__doc__': None, 'climb': <function Cat.climb at 0x000001C95178AAE8>, '__module__': '__main__'}
{'sex': '公', 'name': 'tom', 'kind': '招财猫'}
tom is eating
tom is climbing
实例化猫,需要4个步骤
1.确认自己没有init方法
2.看看有没有父类
3.发现父类Animal有init
4.看着父类的init方法来传参数
__dict__只有对象的命名中的所有名字
一、object类
class A:pass
A()
实例化的过程
1.创建一个空对象
2.调用init方法
3.将初始化之后的对象返回调用处
那么问题来了,A调用了init方法了吗?答案是 调用了
why?它明明没有啊?
所有的类都继承了object类
查看object的源码,可以找到__init__方法
def __init__(self): # known special case of object.__init__
""" Initialize self. See help(type(self)) for accurate signature. """
pass
既然A继承了object类,那么它肯定执行了父类object的__init__方法
加一段注释
class A:
'''
这是一个类
'''
pass a = A()
print(A.__dict__) # 双下方法 魔术方法
执行输出:
{'__doc__': '\n 这是一个类\n ', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>}
可以看到__doc__方法获取注释信息
object,带双下划线的方法,有2个名字,比如 双下方法,魔术方法
任何类实例化都经历3步。如果类没有init,由object完成了。
二、继承与派生
比如人工大战,人类和狗有共同属性,比如名字,血量,攻击了。还有共同的方法吃药
class Animal:
def __init__(self,name,hp,ad):
self.name = name # 名字
self.hp = hp # 血量
self.ad = ad # 攻击力 def eat(self):
print('%s吃药回血了' % self.name) class Person(Animal):
def attack(self,dog): # 派生类
print('%s攻击了%s' %(self.name,dog.name)) class Dog(Animal):
def bite(self,person): # 派生类
print('%s咬了%s' %(self.name,person.name)) alex = Person('alex',100,10)
print(alex.__dict__)
执行输出:
{'ad': 10, 'hp': 100, 'name': 'alex'}
但是还有不同的,比如人类有性别,狗类有品种
怎么办呢?可以在子类init里面加属性
Person增加init方法
class Animal:
def __init__(self,name,hp,ad):
self.name = name # 名字
self.hp = hp # 血量
self.ad = ad # 攻击力 def eat(self):
print('%s吃药回血了' % self.name) class Person(Animal):
def __init__(self,sex):
self.sex = sex
def attack(self,dog): # 派生类
print('%s攻击了%s' %(self.name,dog.name)) class Dog(Animal):
def __init__(self,kind):
self.kind = kind
def bite(self,person): # 派生类
print('%s咬了%s' %(self.name,person.name)) # 人 sex
alex = Person('alex') # 此时只能传一个参数,否则报错
print(alex.__dict__)
执行输出:
{'sex': 'alex'}
发现和上面的例子少了一些属性,animal继承的属性都没有了。what?
因为子类自己有init方法了,它不会执行父类的init方法
那么如何执行父类的init呢?同时保证自己的init方法也能执行?
class Animal:
def __init__(self,name,hp,ad):
self.name = name # 名字
self.hp = hp # 血量
self.ad = ad # 攻击力 def eat(self):
print('%s吃药回血了' % self.name) class Person(Animal):
def __init__(self,name,hp,ad,sex):
Animal.__init__(self,name,hp,ad) # 执行父类方法
self.sex = sex
def attack(self,dog): # 派生类
print('%s攻击了%s' %(self.name,dog.name)) class Dog(Animal):
def __init__(self,name,hp,ad,kind):
Animal.__init__(self, name, hp, ad)
self.kind = kind
def bite(self,person): # 派生类
print('%s咬了%s' %(self.name,person.name)) # 人 sex
alex = Person('alex',100,10,'female') # 实例化
print(alex.__dict__)
执行输出:
{'ad': 10, 'name': 'alex', 'hp': 100, 'sex': 'female'}
三、super方法
Animal.__init__(self, name, hp, ad) 是直接使用类名.方法名 这样执行的。
第二种写法,使用super
class Animal:
def __init__(self,name,hp,ad):
self.name = name # 名字
self.hp = hp # 血量
self.ad = ad # 攻击力 def eat(self): # 吃药
print('%s吃药回血了' % self.name)
self.hp += 20 class Person(Animal):
def __init__(self,name,hp,ad,sex):
#Animal.__init__(self,name,hp,ad) # 执行父类方法
# super(Person,self).__init__(name,hp,ad) # 完整写法.在单继承中,super负责找到当前类所在的父类,在这个时候不需要再手动传self
super().__init__(name, hp, ad) # 简写.效果同上。它不需要传参数Person,self。因为它本来就在类里面,自动获取参数
self.sex = sex
def attack(self,dog): # 派生类
print('%s攻击了%s' %(self.name,dog.name)) class Dog(Animal):
def __init__(self,name,hp,ad,kind):
super().__init__(name, hp, ad)
self.kind = kind
def bite(self,person): # 派生类
print('%s咬了%s' %(self.name,person.name)) # 人 sex
alex = Person('alex',100,10,'female') # 实例化
print(alex.__dict__)
执行输出:
{'ad': 10, 'name': 'alex', 'hp': 100, 'sex': 'female'}
类外层调用eat方法
#父类有eat,子类没有
alex.eat() #找父类
执行输出:
alex吃药回血了
比如人吃药要扣钱,狗吃药,不要钱。
在Animal类中,eat方法,执行时,没有扣钱。
那么就需要在人类中添加eat方法,定义扣钱动作
class Animal:
def __init__(self,name,hp,ad):
self.name = name # 名字
self.hp = hp # 血量
self.ad = ad # 攻击力 def eat(self):
print('%s吃药回血了' % self.name) class Person(Animal):
def __init__(self,name,hp,ad,sex):
#Animal.__init__(self,name,hp,ad) # 执行父类方法
# super(Person,self).__init__(name,hp,ad) # 完整写法.在单继承中,super负责找到当前类所在的父类,在这个时候不需要再手动传self
super().__init__(name, hp, ad) # 简写.效果同上。它不需要传参数Person,self。因为它本来就在类里面,自动获取参数
self.sex = sex # 性别
self.money = 0 # 增加默认属性money
def attack(self,dog): # 派生类
print('%s攻击了%s' %(self.name,dog.name))
def eat(self): # 重新定义eat方法
super().eat() # 执行父类方法eat
print('eating in Person')
self.money -= 50 # 扣钱
class Dog(Animal):
def __init__(self,name,hp,ad,kind):
super().__init__(name, hp, ad)
self.kind = kind
def bite(self,person): # 派生类
print('%s咬了%s' %(self.name,person.name)) # 人 sex
alex = Person('alex',100,10,'female') # 实例化
alex.eat() # 子类有eat 不管父类中有没有,都执行子类的
执行输出:
alex吃药回血了
eating in Person
父类方法,如果子类有个性化需求,可以重新定义次方法
在类外面
当子类中有,但是想要调父类的
alex = Person('alex',100,10,'female') # 实例化
Animal.eat(alex) # 指名道姓
super(Person,alex).eat() # 效果同上,super(子类名,子类对象)方法,一般不用
执行输出:
alex吃药回血了
alex吃药回血了
一般不会在类外面,执行super方法。都是在类里面调用父类方法
super是帮助寻找父类的
在外部,super没有简写
四、钻石继承
父类是新式类,那么子类全是新式类
在python3里面没有经典类
这个形状,像一个钻石
老外喜欢浪漫,有些书籍写的叫钻石继承
代码如下:
class A:
def func(self):
print('A')
class B(A):
def func(self):
print('B')
class C(A):
def func(self):
print('C')
class D(B,C):
def func(self):
print('D') d = D()
d.func()
执行输出:D
把D的代码注释
class D(B,C):
pass
# def func(self):
# print('D')
执行输出:B
把B的代码注释
class B(A):
pass
# def func(self):
# print('B')
执行输出:C
把C的代码注释
class C(A):
pass
# def func(self):
# print('C')
执行输出:A
看图,查看顺序
在看一个龟壳模型
代码如下:
class A:
def func(self):
print('A')
class B(A):
pass
def func(self):
print('B')
class C(A):
pass
def func(self):
print('C')
class D(B):
pass
def func(self):
print('D')
class E(C):
pass
def func(self):
print('E')
class F(D,E):
pass
def func(self):
print('F') f = F()
f.func()
执行输出: F
看图,查看顺序
在这个例子中,A为顶点,因为有2个类继承了A
在执行第3步时,由于B继承了A,B并没有直接去找A。而是在这这一层中断查找。
由同层的E去查找,然后到C,最后到A
这就是广度优先算法
广度优先搜索算法(英语:Breadth-First-Search,缩写为BFS),又译作宽度优先搜索,或横向优先搜索,是一种图形搜索算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。
宽度优先搜索,请参考链接
https://baike.baidu.com/item/%E5%AE%BD%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2/5224802?fr=aladdin&fromid=542084&fromtitle=BFS
广度优先算法有点复杂,Python直接提供了方法mro,可以查看搜索循环
f = F()
f.func()
print(F.mro())
执行输出:
F
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
新式类 多继承 寻找名字的顺序 遵循广度优先
经典面试题
class A:
def func(self):
print('A')
class B(A):
def func(self):
super().func()
print('B')
class C(A):
def func(self):
super().func()
print('C')
class D(B,C):
def func(self):
super().func()
print('D') d = D()
d.func()
执行输出:
A
C
B
D
super():
在单继承中就是单纯的寻找父类
在多继承中就是根据子节点 所在图的mro循环找寻下一个类
在上的例子中,super不是找父类的,它是找下一个节点的
遇到多继承和super
对象.方法
找到这个对象对应的类
将这个类的所有父类都找到画成一个图
根据图写出广度优先的顺序
再看代码,看代码的时候,要根据广度优先顺序图来找对应的super
深度优先
深度优先是“一路摸到黑”,也就是说深度优先搜索会不假思索地一直扩展一个状态直到到达不能被扩展的叶子状态。
要用Python2测试,代码如下:
class A:
def func(self):
print('A')
class B(A):
def func(self):
print('B')
class C(A):
def func(self):
print('C')
class D(B,C):
def func(self):
print('D') d = D()
d.func()
使用cmd执行
在python2里面,不手动继承object,比如class A(object)
就是经典类,比如class A
在这个例子中B继承了A,B再去找A,执行输出A
所有的路线,不走重复的
深度优先,一条路走到黑
找不到,就会回来找其他的.
总结
经典类 :在python2.*版本才存在,且必须不继承object
遍历的时候遵循深度优先算法
没有mro方法
没有super()方法
新式类 :在python2.X的版本中,需要继承object才是新式类
遍历的时候遵循广度优先算法
在新式类中,有mro方法
有super方法,但是在2.X版本的解释器中,必须传参数(子类名,子类对象)
今日内容总结:
python 全栈开发,Day20(object类,继承与派生,super方法,钻石继承)的更多相关文章
- python 全栈开发,Day19(组合,组合实例,初识面向对象小结,初识继承)
一.组合 表示的一种什么有什么的关系 先来说一下,__init__的作用 class Dog: def __init__(self, name, kind, hp, ad): self.name = ...
- Python全栈开发【面向对象进阶】
Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...
- Python全栈开发【面向对象】
Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...
- python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)
python全栈开发笔记第二模块 第四章 :常用模块(第二部分) 一.os 模块的 详解 1.os.getcwd() :得到当前工作目录,即当前python解释器所在目录路径 impor ...
- python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)
昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...
- Python全栈开发【基础二】
Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...
- 学习笔记之Python全栈开发/人工智能公开课_腾讯课堂
Python全栈开发/人工智能公开课_腾讯课堂 https://ke.qq.com/course/190378 https://github.com/haoran119/ke.qq.com.pytho ...
- python 全栈开发之路 day1
python 全栈开发之路 day1 本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...
- Win10构建Python全栈开发环境With WSL
目录 Win10构建Python全栈开发环境With WSL 启动WSL 总结 对<Dev on Windows with WSL>的补充 Win10构建Python全栈开发环境With ...
随机推荐
- Scala进阶之路-Spark独立模式(Standalone)集群部署
Scala进阶之路-Spark独立模式(Standalone)集群部署 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们知道Hadoop解决了大数据的存储和计算,存储使用HDFS ...
- centos7下安装nginx的方法
没有用tar包的方法,太麻烦,还需要找,还需要编译,还需要下乱七八糟的依赖模块.麻烦的一逼,看网上说的.就采用了在线安装的方法.很快.注意一下,这种方法安装是安装到系统默认的位置.我也不知道怎么换.留 ...
- Linux记录-自动删除几天前的日志文件
#!/bin/sh DDIR=/usr/local/appserver/tomcat_risk/logs/risk_manage find $DDIR -mtime +30 -name "* ...
- Easyui和IE浏览器的兼容问题
现在Easyui版本已经更新到1.3.4,今天下载最新的版本,同时下载了IETestert软件,分别模拟IE浏览器5.5,6.0,7.0,8.0和9.0的环境进行测试. Easyui1.3.3与1.3 ...
- 用 Lua 控制 MIDI 合成器来播放自定义格式乐谱
用 Lua 控制 MIDI 合成器来播放自定义格式乐谱 作者: FreeBlues 最新: https://www.cnblogs.com/freeblues/p/9936844.html 说明: 本 ...
- 求幂运算、多项式乘法及Horner法则的应用
一,两种不同的求幂运算 求解x^n(x 的 n 次方) ①使用递归,代码如下: private static long pow(int x, int n){ if(n == 0) return 1; ...
- spfa算法----最短路
题目链接:https://cn.vjudge.net/contest/66569#problem/A 代码: vis数组代表是否还有用,首先初始化为0,首先第一个点入队列,标记为1,然后刚入队列的时候 ...
- Activity生命周期函数、onSaveInstanceState()和onRestoreInstanceState()的介绍
http://www.cnblogs.com/tianzhijiexian/p/3885472.html
- winform程序生成条形码并且并且保存到本地文件中。
今天公司让做一个输入数字.字母生成条形码并且可以以图片格式保存到本地.当看到这个需求时候感觉很搞笑,明明可以用文本框搞定的东西非得做个程序.哎,寄人篱下,不多说了,这就是养兵千日用兵一时. 我在网上找 ...
- SRS+flv.js打造兼容性较高的直播、点播平台
**************************************************************************************************** ...