python学习之面向对象(二)
6.2 类的空间角度研究类
6.2.1 添加对象属性
【总结】对象的属性不仅可以在__init__
里面添加,还可以在类的其他方法或者类的外面添加。
class A:
address = '召唤师峡谷'
def __init__(self, name):
self.name = name
def func(self,type):
self.type = type
# 类的外部可以添加
obj = A('盖伦')
obj.age = 20
print(obj.__dict__)
# 在类的内部可以添加
obj.func('战士')
print(obj.__dict__)
6.2.2 添加类的属性
【总结】类的属性添加方式也是比较灵活的,不仅可以在类内部添加,还可以在类的外部添加。
class A:
aaa = 'aaa'
def __init__(self,name):
self.name = name
def func(self):
A.ccc = 'ccc'
# 在类的外部可以添加
A.bbb = 'bbb'
print(A.__dict__)
# 在类的内部可以添加
A.func(1)
print(A.__dict__ )
6.2.3 对象如何查找类的属性
对象在实体化的空间内,有一个指向类地址空间的指针
对象查找顺序:先从对象空间找--> 类的空间找--> 父类空间找.....
类名查找顺序:先从本类空间找--> 父类空间.....
6.3 类与类的关系
python中类与类之间的关系:
依赖关系
组合关系
继承关系
6.3.1 依赖关系
将一个类名或者类的对象传给另一个类的方法中
模拟一个场景,淘金者要进入OnePeace,淘金者要念口号让大门打开,进入进入OnePeace后要念口号,然后门关上。
class Fossicker:
def __init__(self,name):
self.name = name
def ask_open(self,se):
print(f'{self.name}大喊:“芝麻开门”')
re.open_door() #调用SecretPath里的open_door方法
print(f"{self.name}进入了{se.name}里面")
def ask_close(self,se):
print(f'{self.name}又说:“芝麻关门”')
re.closs_door() #调用SecretPath里的closs_door方法
print(f"{self.name}把{se.name}的门关了")
class SecretPath:
def __init__(self,name):
self.name = name
def open_door(self):
print('门开了')
def closs_door(self):
print('门关了')
e1 = Fossicker('阿加')
se1 = SecretPath('OnePeace')
e1.ask_open(se1)
e1.ask_close(se1)
上述场景,Fossicker的这个类中用到了SecretPath的对象,而SecretPath的对象并没有用到其他类的任何方法或是属性,我们可以说Fossicker类依赖SecretPath类,二者构成依赖关系。
6.3.2 组合关系
将一个类的对象封装到另一个类的对象的属性中
情景模拟:相亲
class Boy:
def __init__(self,name):
self.name = name
def meet(self,gril):
if gril.age <= 25:
self.grilfriend = gril
print(f'{self.name}跟{self.grilfriend.name}很是合得来')
gril.boyfriend = self
else:
self.grilfriend = None
print('不合适,没成')
def have_dinner(self):
if self.grilfriend:
print(f"{self.name}请{self.grilfriend.name}吃大餐")
else:
print('算了回家吃泡面吧')
class Gril:
def __init__(self,name,age):
self.name = name
self.age = age
def shopping(self):
print(f"{self.name}和{self.boyfriend.name}一起去购物")
gailun = Boy('盖伦')
nvjing = Gril('凯特琳',24)
gailun.meet(nvjing)
gailun.have_dinner()
nvjing.shopping()
【总结】不管是依赖关系还是组合关系,在类与类之间进行相互调用时,我们希望的是,某些情况下不要把所有的因素写死,而是获取到其他对象的整个空间内的信息,所以在传参时可以,应注意这一点
6.3.3 练习
场景模拟:模拟英雄联盟的游戏人物
- 创建一个 Game_role的类.
- 构造方法中给对象封装name,ad(攻击力),hp(血量).三个属性.
- 创建一个attack方法,此方法是实例化两个对象,互相攻击的功能:
例: 实例化一个对象 盖伦,ad为10, hp为100
实例化另个一个对象 剑豪 ad为20, hp为80
盖伦通过attack方法攻击剑豪,此方法要完成 '谁攻击谁,谁掉了多少血, 还剩多少血'的提示功能
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp
def attack(self,p1):
p1.hp = p1.hp - self.ad
print(f'{self.name}攻击{p1.name},{p1.name}掉了{self.ad}血,还剩{p1.hp}血')
gailun = GameRole('盖伦',10,100)
jianhao = GameRole('剑豪',20,80)
gailun.attack(jianhao) #依赖关系的应用
升级:创建一个武器类,让对象借助武器进行攻击
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp
class Weapon:
def __init__(self, name, ad):
self.name = name
self.ad = ad
def weapon_attack(self, p1, p2):
# print(self)
p2.hp = p2.hp - self.ad
print(f'{p1.name}利用{self.name}给了{p2.name}一下,{p2.name}还剩{p2.hp}血')
gailun = GameRole('盖伦', 10, 100)
jianhao = GameRole('剑豪', 20, 80)
great_sword = Weapon('黑切', 30)
spear = Weapon('长枪', 40)
great_sword.weapon_attack(gailun, jianhao) #此时进行攻击动作的执行者是great_sword
此时进行攻击动作的执行者是great_sword,而我们希望的是GameRole的对象使用武器进行攻击,下面进行优化
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp
def equip_weapon(self,wea): #
self.weapon = wea
class Weapon:
def __init__(self, name, ad):
self.name = name
self.ad = ad
def weapon_attack(self, p1, p2):
p2.hp = p2.hp - self.ad
print(f'{p1.name}利用{self.name}给了{p2.name}一下,{p2.name}还剩{p2.hp}血')
gailun = GameRole('盖伦', 10, 100)
jianhao = GameRole('剑豪', 20, 80)
great_sword = Weapon('黑切', 30)
spear = Weapon('长枪', 40)
gailun.equip_weapon(great_sword) #依赖关系
gailun.weapon.weapon_attack(gailun,jianhao)
6.4 继承
面向对象的三大特性:封装,继承,多态
6.4.1 继承的初识
B类继承A类,B就叫做A的子类,派生类,子类可以调用父类的属性和方法
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Dog:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Cat:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
看上面的代码,建立了三个类,出现了大量的重复代码,利用继承对这些代码进行整合,如下:
# 继承
class Animal:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class Person(Animal):
pass
class Dog(Animal):
pass
class Cat(Animal):
pass
6.4.2 继承的特性
继承的优点:
- 节省代码
- 增强代码的耦合性
- 代码更加规范
- 重构父类的属性或方法
继承的缺点:
- 降低了代码的灵活性
- 过多的耦合性,使得在父类的常量、变量和方法被修改时,需要考虑子类的修改,甚至导致大段的代码需要重构
6.4.3 单继承
只有一个父类(基类,超类)
子类以及对象可以调用父类的属性方法
class Animal:
live = '有生命的'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('需要进食')
class Person(Animal):
pass
# 从类名执行父类的属性
print(Person.__dict__)
print(Person.live)
对象执行父类的一切
实体化对象首先执行__init__
,自己没有就去父类找,根据__init__
函数传入相应的参数
class Animal:
live = '有生命的'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('需要进食')
class Person(Animal):
pass
# 从对象执行父类的一切
p1 = Person('盖伦',24,'男')
print(p1.__dict__)
p1.eat()
【注意】子类以及子类的对象只能调用父类的属性和方法,不能进行操作(增删改)。
子类可以重写父类的方法
就是在子类的空间内,创建一个新的与父类函数重名的函数
class Animal:
live = '有生命的'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('需要进食')
class Person(Animal):
def eat(self):
print('人要吃饭')
p1 = Person('盖伦',24,'男')
p1.eat()
【注意】如遇子类属性与父类重名的话,先从本身进行执行,父类不执行
如何执行子类方法有执行父类方法
一种是无绑定的调用父类的函数
一种是使用super自动绑顶
class Animal:
live = '有生命的'
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print('需要进食')
class Person(Animal):
# 方法一:
def __init__(self,name,age,sex,type):
Animal.__init__(self,name,age,sex) #调用父类的方法,需要手动传入self
self.type = type
def eat(self):
print('人要吃饭')
p1 = Person('盖伦',24,'男','战士')
print(p1.__dict__)
# 方法二:super方法
class Person(Animal):
def __init__(self,name,age,sex,type):
# super(Person, self).__init__(name,age,sex) #super第一个括号里的内容可以省略,
super().__init__(name,age,sex)
self.type = type
def eat(self):
print('人要吃饭')
p1 = Person('盖伦',24,'男','战士')
print(p1.__dict__)
# super可以继承父类的方法
class Person(Animal):
def __init__(self,name,age,sex,type):
# super(Person, self).__init__(name,age,sex)
super().__init__(name,age,sex) #super第一个括号的内容可以省略
self.type = type
def eat(self):
print('人要吃饭')
super().eat()
p1 = Person('盖伦',24,'男','战士')
p1.eat()
6.4.4 多继承
一个子类继承了多个父类,难点在于继承的顺序。
面向对象:python2.2之前,都是经典类;python2.2-2.7之间,存在两种类型,一种是经典类另一种是新式类;python3只有新式类。
经典类:基类不继承object,查询规则满足深度优先原则
新式类:基类继承object,查询规则满足mro算法,不是单纯的广度优先
mro算法
表头:列表的第一个元素
表尾:表头以外的所有元素
解题过程:
1.先从上到下,列出每个类的线性表达式.
2.列出的线性表达式中如果有多个表达式的话,需要进行化简.
3.最终的子类也使用线性表达式表示,由于有多个父类,也需要进行化简.
线性表达式有两个部分组成:第一个类名称为头部,剩余的部分称为尾部.
先从上到下求出每个类的线性表达式,期间需要化简
G = [GO]
F = F + [GO] = [FGO]
D = D + [GO] = [DGO]
B = B + [FGO] = [BFGO]
C = C + [E] = [CE]
再求最终的类的线性表达式,需要化简
A = [BFGO] + [CE] + [DGO]
AB = [FGO] + [CE] + [DGO]
ABF = [GO] + [CE] + [DGO]
由于第一个表达式的G是第三个表达式的尾部,所以不能抽取,则从第二个表达式的头部开始抽取C
ABFC = [GO] + [E] + [DGO]
再次从第一个表达式的头部开始抽取,依然不能抽取,则从第二个表达式抽取
ABFCE = [GO] + [DGO]
第一个表达式的头部依然不能抽取,从第二个表达式的头部开始抽取
ABFCED = [GO] + [GO]
剩下的部分相同了,就可以直接写
ABFCEDGO # 最终的mro解析顺序
【注意】在实际项目中,查看mro顺序可以使用 类名.mro来获取类的查找顺序
6.5 封装和多态
6.5.1 封装
给对象封装属性或方法的过程
通过对象或者self获取被封装的属性方法
6.5.2 多态
python默认支持多态
python 设置变量时,不需要设置数据类型
6.5.3 鸭子类型
俗话你看起来像鸭子,那么你就是鸭子
两个相互独立的A,B两个类,但内部功能相似,python一般会将类似于A,B里面的相似功能的命名设置成相同。
举例:A,B虽然没有联系,默认使用一种规范,使用起来更方便。像是列表里的index与字符串的index,两个index没有关系,但是使用的时候比较方便。
6.6 super
super(类名,self).function() 执行本类的父类的方法
类名:多继承中,严格按照self的从属类的mro顺序执行,即执行本类名的父类里的方法;单继承中,按照父类的方法执行
class A:
def func(self):
print('in A')
class B(A):
def func(self):
print('in B')
class C(A):
def func(self):
print('in C')
class D(B,C):
def func(self):
print('in D')
super(D,self).func()
t = D()
t.func()
#输出
in D
in B
# 改变一下class D的super参数
class D(B,C):
def func(self):
print('in D')
super(B,self).func()
t = D()
t.func()
输出
in D
in C #按照mro顺序执行下一父类的方法
6.7 类的约束
约束是对类的约束。
场景模拟:设计一个战士和刺客的英雄类型
class Zhanshi:
def type_role(self,name):
self.name = name
print(f"{self.name}是一个战士")
class Cike:
def type_role(self, name):
self.name = name
print(f"{self.name}是一个刺客")
gailun = Zhanshi()
# g.type_role('战士')
jianhao = Cike()
# j.type_role('刺客')
# 设计一个规范化的接口
def type_role(obj,role): #归一化设计
obj.type_role(role)
type_role(gailun,'盖伦')
type_role(jianhao,'剑豪')
当增加一个法师的类型
# 在上述的基础上增加新的英雄属性
class Fashi:
def type_role(self,name):
self.name = name
print(f"{self.name}是一个法师")
moganna = Fashi()
type_role(moganna,'莫甘娜') #调用规范化接口使用
如果我要按照归一化设计的规范继续增加新的英雄类型时,那么怎么让程序设计者知道要使用这一规范呢?
目前有两种方式:1.在父类建立一种约束;2.模拟抽象类的概念,建立一种强制约束
第一种方式
在父类建议约束,主动抛出错误,非强制性
class Hero:
def type_role(self,name): #定义一种规范,子类要定义type_role方法
raise Exception('请定义type_role方法')
class Zhanshi(Hero):
def type_role(self,name):
self.name = name
print(f"{self.name}是一个战士")
class Cike(Hero):
def type_role(self, name):
self.name = name
print(f"{self.name}是一个刺客")
class Fashi(Hero):
def type_ro(self,name): #没有定义type_role而是定义了一个type_ro
self.name = name
print(f"{self.name}是一个法师")
pass
def type_role(obj,role): #归一化设计
obj.type_role(role)
gailun = Zhanshi()
jianhao = Cike()
moganna = Fashi()
type_role(moganna,'莫甘娜') #调用接口函数时,主动报错:raise Exception('请定义type_role方法')
第二种方式
类似于抽象类的概念,定义一种强制约束,你只要是我的派生类,那么这个方法必须要有。
from abc import ABCMeta,abstractmethod
class 父类名(metaclass=ABCMeta):
@abstractmethod
def 函数名
from abc import ABCMeta,abstractmethod
class Hero(metaclass=ABCMeta):
@abstractmethod
def type_role(self,name): #定义一种规范,子类要定义type_role方法
pass
class Zhanshi(Hero):
def type_role(self,name):
self.name = name
print(f"{self.name}是一个战士")
class Cike(Hero):
def type_role(self, name):
self.name = name
print(f"{self.name}是一个刺客")
class Fashi(Hero):
def type_ro(self,name):
self.name = name
print(f"{self.name}是一个法师")
pass
gailun = Zhanshi()
jianhao = Cike()
moganna = Fashi()
moganna.type_ro('莫甘娜')
# 函数执行前,直接报错TypeError: Can't instantiate abstract class Fashi with abstract methods type_role
python学习之面向对象(二)的更多相关文章
- python学习day20 面向对象(二)类成员&成员修饰符
1.成员 类成员 类变量 绑定方法 类方法 静态方法 属性 实例成员(对象) 实例变量 1.1实例变量 类实例化后的对象内部的变量 1.2类变量 类中的变量,写在类的下一级和方法同一级. 访问方法: ...
- Python学习之==>面向对象编程(二)
一.类的特殊成员 我们在Python学习之==>面向对象编程(一)中已经介绍过了构造方法和析构方法,构造方法是在实例化时自动执行的方法,而析构方法是在实例被销毁的时候被执行,Python类成员中 ...
- Python学习笔记(二)——列表
Python学习笔记(二)--列表 Python中的列表可以存放任何数据类型 >>> list1 = ['Hello','this','is','GUN',123,['I','Lov ...
- 【Python学习笔记之二】浅谈Python的yield用法
在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生 ...
- Python学习笔记(二):条件控制语句与循环语句及常用函数的用法
总结的内容: 1.条件控制语句 2.while循环语句 3.for循环语句 4.函数的用法 一.条件控制语句 1.介绍 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决 ...
- Python学习系列(二)(基础知识)
Python基础语法 Python学习系列(一)(基础入门) 对于任何一门语言的学习,学语法是最枯燥无味的,但又不得不学,基础概念较繁琐,本文将不多涉及概念解释,用例子进行相关解析,适当与C语言对比, ...
- python学习02python入门二
学前须知:1.本文档有关内容均建立在python3.x版本上,python2.x已经成为历史,如有需要,文内会特别说明. 2.本文使用的编辑器多为架构在Windows上的pycharm,如需了解Lin ...
- Python学习笔记(十二)—Python3中pip包管理工具的安装【转】
本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...
- Python学习笔记(二)网络编程的简单示例
Python中的网络编程比C语言中要简洁很多,毕竟封装了大量的细节. 所以这里不再介绍网络编程的基本知识.而且我认为,从Python学习网络编程不是一个明智的选择. 简单的TCP连接 服务器代码如 ...
随机推荐
- 关于客户端连接mysql的授权问题
mysql远程连接 Host * is not allowed to connect to this MySQL server的错误. 是因为mysql需要授权才能访问.授权方式: 授权给某一个ip: ...
- k8s管理pod资源对象(上)
一.容器于pod资源对象 现代的容器技术被设计用来运行单个进程时,该进程在容器中pid名称空间中的进程号为1,可直接接收并处理信号,于是,在此进程终止时,容器即终止退出.若要在一个容器中运行多个进程, ...
- Java技术综述
自己打算好好学习下Java,所以想先明晰Java开发中到底有哪些技术,以便以后学习的过程中,可以循序渐进,随着学习的深入,本文将不断更新. Java基础教程将Java的入门基础知识贯穿在一个实例中,逐 ...
- linux下使用 TC 对服务器进行流量控制
tc 介绍 在linux中,tc 有二种控制方法 CBQ 和 HTB.HTB 是设计用来替换 CBQ 的.HTB比CBQ更加灵活,但是CPU 开销也更大,通常高速的链路会使用CBQ,一般而言HTB使用 ...
- autofs 自动挂载.
autofs 自动挂载. 操作环境:redhat 6 一.autofs 说明 自动挂载器是一个监视目录的守护进程,并在目标子目录被引用时,自动执行预定义的挂载 自动挂载器由autofs服务脚本管理 自 ...
- TCP超时与重传机制与拥塞避免
TCP超时与重传机制 TCP协议是一种面向连接的可靠的传输层协议,它保证了数据的可靠传输,对于一些出错,超时丢包等问题TCP设计的超时与重传机制. 基本原理:在发送一个数据之后,就开启一个定时器,若是 ...
- LINUX学习之一基础篇
1.计算机硬件五大单元:运算器.控制器.存储器.I/O设备 2.CPU种类:精简指令集(RISC)和复杂指令集(CISC) 3.1Byte=8bit,扇区大小为512bytes 4.芯片组通常分为两个 ...
- 树莓派设定笔记(Raspberry Pi 3 B+)
树莓派默认用户名密码 pi / raspberry 一.启用root用户 设置root用户密码 sudo passwd root 开启root账户 sudo passwd --unlock root ...
- Git 下拉项目
1.[VCS]> [Checkout from Version Control] > [Git] 2.项目Git路径 > [Clone]
- jquery preventDefault()方法 语法
jquery preventDefault()方法 语法 作用:preventDefault() 方法阻止元素发生默认的行为(例如,当点击提交按钮时阻止对表单的提交).大理石平台价格 语法:event ...