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 练习

场景模拟:模拟英雄联盟的游戏人物

  1. 创建一个 Game_role的类.
  2. 构造方法中给对象封装name,ad(攻击力),hp(血量).三个属性.
  3. 创建一个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学习之面向对象(二)的更多相关文章

  1. python学习day20 面向对象(二)类成员&成员修饰符

    1.成员 类成员 类变量 绑定方法 类方法 静态方法 属性 实例成员(对象) 实例变量 1.1实例变量 类实例化后的对象内部的变量 1.2类变量 类中的变量,写在类的下一级和方法同一级. 访问方法: ...

  2. Python学习之==>面向对象编程(二)

    一.类的特殊成员 我们在Python学习之==>面向对象编程(一)中已经介绍过了构造方法和析构方法,构造方法是在实例化时自动执行的方法,而析构方法是在实例被销毁的时候被执行,Python类成员中 ...

  3. Python学习笔记(二)——列表

    Python学习笔记(二)--列表 Python中的列表可以存放任何数据类型 >>> list1 = ['Hello','this','is','GUN',123,['I','Lov ...

  4. 【Python学习笔记之二】浅谈Python的yield用法

    在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生 ...

  5. Python学习笔记(二):条件控制语句与循环语句及常用函数的用法

    总结的内容: 1.条件控制语句 2.while循环语句 3.for循环语句 4.函数的用法 一.条件控制语句 1.介绍 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决 ...

  6. Python学习系列(二)(基础知识)

    Python基础语法 Python学习系列(一)(基础入门) 对于任何一门语言的学习,学语法是最枯燥无味的,但又不得不学,基础概念较繁琐,本文将不多涉及概念解释,用例子进行相关解析,适当与C语言对比, ...

  7. python学习02python入门二

    学前须知:1.本文档有关内容均建立在python3.x版本上,python2.x已经成为历史,如有需要,文内会特别说明. 2.本文使用的编辑器多为架构在Windows上的pycharm,如需了解Lin ...

  8. Python学习笔记(十二)—Python3中pip包管理工具的安装【转】

    本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...

  9. Python学习笔记(二)网络编程的简单示例

    Python中的网络编程比C语言中要简洁很多,毕竟封装了大量的细节. 所以这里不再介绍网络编程的基本知识.而且我认为,从Python学习网络编程不是一个明智的选择.   简单的TCP连接 服务器代码如 ...

随机推荐

  1. CSS如何修改placeholder样式

    项目用经常遇到修改input的placeholder的颜色的需求,这里来看一下placeholder如何用css设置: 原文发布与我的个人博客>> 首先来看一下chrome默认的input ...

  2. HDU6736 2019CCPC秦皇岛赛区 F. Forest Program

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=6736思路:dfs+栈 判环           设图中环的大小分别为 c1, c2, ..., ck,不属 ...

  3. tp6中使用微信支付sdk

    一.下载微信支付sdk 二.将lib文件夹下的文件复制到目录:extend->WxPay 将example文件夹下的WxPay.Config.php文件也复制到:extend->WxPay ...

  4. iOS设置UITableViewCell的选中时的颜色

    1.系统默认的颜色设置 //无色   cell.selectionStyle = UITableViewCellSelectionStyleNone;     //蓝色   cell.selectio ...

  5. Coding 账户与 本地 Git 客户端的配置

    1.先创建cooding账户 ,注册地址:https://coding.net/ 2.创建好账户后登陆,在个人设置中  验证邮箱 和 验证手机 (邮箱很重要配置需要用到) 3.安装git 客户端 (在 ...

  6. 基于Python原生asyncio模块对DNS正向和反向的解析

    一.正向解析:域名解析IP地址 import asyncio import socket domains = [ ('www.baidu.com', 'https'), ('cn.bing.com', ...

  7. cdh本地源安装-自用

    yum 安装cm 参考官网 主机名 /etc/hosts hostnamectl set-hostname foo-1.data.com hostnamectl set-hostname foo-2. ...

  8. C# 遍历控件名称

    List<string> list = new List<string>(); list.Add("textBox2"); list.Add("t ...

  9. BAT批处理设置Shift右键cmd菜单

    Windows Registry Editor Version 5.00 [-HKEY_CLASSES_ROOT\Directory\shell\runas] [HKEY_CLASSES_ROOT\D ...

  10. python 数据库插入操作

    数据库插入操作 以下实例使用执行 SQL INSERT 语句向表 EMPLOYEE 插入记录: #!/usr/bin/python # -*- coding: UTF-8 -*- import MyS ...