一、面向对象的简介
面向对象
- 初识
- 一种新的编程思路
- 面向过程开发:想要一个结果 写代码 实现计算结果
- 面向对象开发:有哪些角色 角色的属性和技能之间是如何交互的
- 一种新的编程思路
- 什么时候用?
复杂的 拥有开放式结局的程序 比较适合使用面向对象开发
- 游戏
- 购物
- 例子:人狗大战
alex = { ‘name‘: ‘alex‘, ‘sex‘: ‘不详‘, ‘job‘: ‘搓澡工‘, ‘level‘: 0, ‘hp‘ : 250, ‘weapon‘:‘搓澡巾‘, ‘ad‘ : 1}?小白 = { ‘name‘:‘小白‘, ‘kind‘:‘泰迪‘, ‘hp‘:5000, ‘ad‘:249} # 1.你怎么保证所有的玩家初始化的时候都拥有相同的属性# 2.每来一个新的玩家,我们都自己手动的创建一个字典,然后向字典中填值# 3.人物和狗的技能如何去写?# 建立人物的模子:def Person(name,sex,job,hp,weapon,ad,level=0): # 人模子 def 搓(dog): dog[‘hp‘] -= dic[‘ad‘] print(‘%s 攻击了 %s,%s掉了%s点血‘ % (dic[‘name‘], dog[‘name‘], dog[‘name‘], dic[‘ad‘])) dic = { ‘name‘: name, ‘sex‘: sex, ‘job‘: job, ‘level‘: level, ‘hp‘ :hp, ‘weapon‘:weapon, ‘ad‘ : ad, ‘action‘:搓 } return dic??# 建立狗的模子:def Dog(name,kind,hp,ad): def 舔(person): # 函数不是一个公用的函数 是一个有归属感的函数 person[‘hp‘] -= dic[‘ad‘] print(‘%s 舔了 %s,%s掉了%s点血‘ % (dic[‘name‘], person[‘name‘], person[‘name‘], dic[‘ad‘])) dic = { ‘name‘: name, ‘kind‘: kind, ‘hp‘: hp, ‘ad‘: ad, ‘action‘:舔 } return dic?alex = Person(‘alex‘,‘不详‘,‘搓澡工‘,250,‘搓澡巾‘,1)wusir = Person(‘wusir‘,‘male‘,‘法师‘,500,‘打狗棍‘,1000)小白 = Dog(‘小白‘,‘泰迪‘,5000,249)小金 = Dog(‘小金‘,‘柯基‘,10000,499)?小白[‘action‘](alex) # 小白 舔了 alex,alex掉了249点血alex[‘action‘](小白) # alex 攻击了 小白,小白掉了1点血
以上的程序可以用面向对象的方法解决:
class Person: # 类名 def __init__(self,name,sex): # 必须叫 __init__这个名字,不能改变的,所有的在一个具体的人物出现之后拥有的属性都可以写在这里 self.name = ‘alex‘ self.sex = ‘man‘?alex = Person() # alex 就是对象 alex = Person()的过程 是通过类获取一个对象的过程 — 实例化# 类名()会自动调用类中的__init__方法??class Person: # 类名 def __init__(self,name,sex,job,hp,weapon,ad): # 必须叫__init__这个名字,不能改变的,所有的在一个具体的人物出现之后拥有的属性 self.name = name self.sex = sex self.job = job self.level = 0 self.hp = hp self.weapon = weapon self.ad = ad?alex = Person(‘alex‘,‘不详‘,‘搓澡工‘,260,‘搓澡巾‘,1) # alex 就是对象 alex = Person()的过程 是通过类获取一个对象的过程 - 实例化print(alex,alex.__dict__)# <__main__.Person object at 0x00000224364739B0> # {‘name‘: ‘alex‘, ‘sex‘: ‘不详‘, ‘job‘: ‘搓澡工‘, ‘level‘: 0, ‘hp‘: 260, ‘weapon‘: ‘搓澡巾‘, ‘ad‘: 1}?wusir = Person(‘wusir‘,‘male‘,‘法师‘,500,‘打狗棍‘,1000)# print(wusir,wusir.__dict__)print(alex.name) # print(alex.__dict__[‘name‘]) 属性的查看alex.name = ‘alexsb‘ # 属性的修改print(alex.name) # alexsbalex.money = 1000000 # 属性的增加print(alex.money) # 1000000print(alex.__dict__)# {‘name‘: ‘alex‘, ‘sex‘: ‘不详‘, ‘job‘: ‘搓澡工‘, ‘level‘: 0, ‘hp‘: 260, ‘weapon‘: ‘搓澡巾‘, ‘ad‘: 1, ‘money‘: 1000000}del alex.money # 属性的删除print(alex.__dict__)
- 类和对象之间的关系?
- 类 是一个大范围 是一个模子 它约束了事物有哪些属性 但是不能约束具体的值
- 对象 是一个具体的内容 是模子的产物 它遵循了类的约束 同时给属性附上具体的值
- Person是一个类 alex,wusir是这个类的对象
- 类有一个空间,存储的是定义在class中的所有名字
- 每一个对象又拥有自己的空间,通过对象名.
__dict__
就可以查看这个对象的属性和值 - 修改列表\字典中的某个值,或者面向对象的某个属性 都不会影响这个对象\字典\列表所在的内存空间
- 实例化所经历的步骤
- 实例 = 类名() 类名()之后的第一件事:开辟一块空间
- 首先开辟空间,调用init方法,把开辟的空间地址传递给self参数,调用
__init__
把空间的内存地址作为self参数传递到函数内部 - init方法中一般完成:把属性的值存储在self的空间里 — 对象的初始化,所有的这一对象需要使用的属性都要和self关联起来
- self这个地址会作为返回值,返回给“实例”,执行完
__init__
中的逻辑后,self变量会自动的被返回到调用处(发生实例化的地方)
- 实例变量:self.名字
二、面向对象中类的成员和命名空间
- 命名空间
- 在类的命名空间里:静态变量、绑定方法
- 在对象的命名空间里:类指针、对象的属性(实例变量)
- 调用的习惯
- 类名.静态变量
- 对象.静态变量(对象调用静态变量的时候,不能对变量进行赋值操作 )
- 绑定方法
- 对象.绑定方法() # ==> 类名.绑定方法(对象)
- 对象.实例变量
- 对象
- 对象 = 类名()
怎么用
- 类能做什么用:
- 1.实例化对象
- 2.操作静态变量
- 什么时候是对类中的变量赋值,或者去使用类中的变量
- 类名.名字 = ‘值’
- print(类名.名字 )
- print(对象名.名字 ) # 如果对象本身没有这个名字
- 什么时候是对对象中的变量赋值
- 对象.名字 的时候
- self.名字 的时候
- 类能做什么用:
# 例题1class A: Country = ‘中国‘ # 静态变量/静态属性 存储在类的命名空间里的 def __init__(self,name,age): # 绑定方法 存储在类的命名空间里的 self.name = name self.age = age def func1(self): print(self) def func2(self):pass def func3(self):pass def func4(self):pass def func5(self):pass?a = A(‘alex‘,83)print(a.name) # alexprint(a.Country) # 中国(先在a的属性的小空间找Country,没有就通过类指针在A类里找Country)?print(A.Country) # 中国a.func1() # == A.func1(a) # <__main__.A object at 0x000002171C4F87F0>?# 可以看出,a实例化开辟自己的空间,调用init方法在自己的空间建立name,age变量,所以调用a.name是用自己的,而调用a.Country时,自己内部没有,就用A类的??# 例题2class A: Country = ‘中国‘ # 静态变量/静态属性 存储在类的命名空间里的 def __init__(self,name,age,country): # 绑定方法 存储在类的命名空间里的 self.name = name self.age = age self.Country = country def func1(self): print(self) def func2(self):pass def func3(self):pass def func4(self):pass def func5(self):pass?a = A(‘alex‘,83,‘印度‘)print(a.name) # alexprint(a.Country) # 印度print(A.Country) # 中国?# 可以看出,a的空间里有name,Country,都用自己的??# 例题3class A: Country = ‘中国‘ # 静态变量/静态属性 存储在类的命名空间里的 def __init__(self,name,age,country): # 绑定方法 存储在类的命名空间里的 self.name = name self.age = age self.country = country def func1(self): print(self) def func2(self):pass def func3(self):pass def func4(self):pass def func5(self):pass?a = A(‘alex‘,83,‘印度‘)b = A(‘wusir‘,74,‘泰国人‘)a.Country = ‘日本人‘print(a.Country) # 日本print(b.Country) # 泰国print(A.Country) # 中国?# a本来自己的空间中有Country = ‘印度‘,但是a.Country = ‘日本人‘,就在a空间改变了Country的属性??# 例题4class A: Country = ‘中国‘ # 静态变量/静态属性 存储在类的命名空间里的 def __init__(self,name,age,country): # 绑定方法 存储在类的命名空间里的 self.name = name self.age = age # 注意,此处没有匹配country的参数 def func1(self): print(self) def func2(self):pass def func3(self):pass def func4(self):pass def func5(self):pass?a = A(‘alex‘,83,‘印度‘)b = A(‘wusir‘,74,‘泰国人‘)A.Country = ‘日本人‘print(a.Country)print(b.Country)print(A.Country)?# init的方法中没有Country的属性,所以实例化a,b的时候,调用init方法没有在自己的内部建立Country的属性,所以a.Country和b.Country都是用的A中的Country,就都是日本人
- 由此可得出静态变量的作用
- 如果一个变量,是所有的对象共享的值,那么这个变量应该被定义成静态变量
- 所有和静态变量相关的增删改查都应该使用类名来处理;
而不应该使用对象名直接修改静态变量
- 组合
- 一个类的对象是另一个类对象的属性
- 两个类之间 有 什么有什么的关系:班级有学生 学生有班级 图书有作者.....
三、面向对象的三大特性——继承、封装、多态
(一)、继承
- 目的:解决代码的重复性
- 继承中遇到的两种情况
- 当子类和父类的方法重名的时候,我们只使用子类的方法,而不会去调用父类的方法了
class Animal: def __init__(self,name): self.name = name def eat(self): print(‘%s is eating‘%self.name)class Cat(Animal): def eat(self): print(‘%s吃猫粮‘%self.name)小白 = Cat(‘小白‘)小白.eat() # 小白吃猫粮(因为在Cat类型中有eat属性,就不调用父类的)
- 子类想要调用父类的方法的同时还想执行自己的同名方法
在子类的方法中调用父类的方法 :父类名.方法名(self)
class Animal: def __init__(self,name,food): self.name = name self.food = food self.blood = 100 def eat(self): print(‘%s is eating %s‘%(self.name,self.food))class Cat(Animal): def eat(self): self.blood += 100 Animal.eat(self) print(f‘{self.name}此时的体力为{self.blood}‘)小白 = Cat(‘小白‘,‘猫粮‘)小白.eat()# 小白 is eating 猫粮# 小白此时的体力为200print(小白.__dict__) # {‘name‘: ‘小白‘, ‘food‘: ‘猫粮‘, ‘blood‘: 200}
- 总结:
- 继承语法: class 子类名(父类名):pass
- 父类和子类方法的选择:
- 子类的对象,如果去调用方法
永远先调用自己的
- 如果自己有 用自己的
- 自己没有 用父类的
- 如果自己有 还想用父类的:直接在子类方法中调父类的方法 父类名.方法名(self)
- 子类的对象,如果去调用方法
- 当子类和父类的方法重名的时候,我们只使用子类的方法,而不会去调用父类的方法了
- 多继承
一个子类有多个父类
单继承
- 调子类的 : 子类自己有的时候
- 调父类的 : 子类自己没有的时候
- 调子类和父类的 :子类父类都有,在子类中调用父类的
多继承
- 一个类有多个父类,在调用父类方法的时候,按照继承顺序,先继承的就先寻找
- 新式类和经典类
python3 所有的类都继承object类,都是新式类
在py2中 不继承object的类都是经典类
继承object就是新式类
在单继承方面(无论新式类,还是经典类,都是一样的)
在多继承的情况中:
- 对于新式类,在走到一个点,下一个点既可以从深度走,也可以从广度走,总是先走广度,广度优先
- 在经典类中,都是深度优先,总是在一条路走不通之后再换一条路,走过的点不会再走了
- 广度优先 C3算法
总之:
- 经典类 — 深度优先 新式类 — 广度优先
- 深度优先会看
- 广度预先遵循C3算法,要会用mro,会查看顺序
- 经典没有mro,但新式类有
- 通过继承实现的类的开发规范(工作中)
- 1.不用模块的:
class 父类 def 子类必须实现的方法名(self,参数们): raise NotImplementedError(‘提示信息‘) class 子类(父类): def 父类要求实现的方法(self,参数们): print(‘‘‘code‘‘‘)
- 2.用模块的
from abc import ABCMeta,abstractmethodclass 父类(metaclass = ABCMeta): @abstractmethod def 子类必须实现的方法名(self,参数们):passclass 子类(父类): def 父类要求实现的方法(self,参数们): print(‘‘‘code‘‘‘)
- 1.不用模块的:
(二)、多态
- 什么是多态:一个类表现出的多种状态,实际上是通过继承来完成的
- 例子:如果狗类继承动物类,猫类也继承动物类
- 那么我们就说猫的对象也是动物类型的
- 狗的对象也是动物类型的
- 在这个例子里,动物这个类型表现出了猫和狗的形态
- 鸭子类型
在py中,一个类可以是很多类的鸭子类型
- tuple 元组类
是可哈希的
但是又不依靠继承哈希来判定是不是可哈希类型
元组类是可哈希类型的鸭子类型
- 可迭代的类型
不是依靠继承迭代来判定是不是迭代类型
看他长得像(内部实现了
__init__
)
子类继承父类,我们说子类也是父类这个类型的
? 在Python中,一个类是不是属于某一个类型,不仅仅通过继承完成,还可以是不继承,但是如果这个满足了某些类型的特征条件,我们就说它长得像这个类型,那么它就是这个类型的鸭子类型
- tuple 元组类
(三)、封装
- 封装:就是把属性和方法装起来
- 广义上的封装:把属性和方法装起来,外面不能直接调用,要通过类的名字来调用
- 狭义上的封装:把属性和方法藏起来,外面不能调用了,只能在内部偷偷调用
- 给一个名字前面加上了双下划綫的时候,这个名字就变成了一个私有的
所有的私有的内容或者名字都不能在类的外部调用,只能在类的内部使用了
- 使用的三种情况
- 使用的三种情况
- 不想让你看,也不想让你改
class User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象?# 没有其他的方法,既不能看,也不能改
- 可以让你看,但不让你改
class User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 def get_pwd(self): # 表示的是用户不能改只能看 私有 + 某个get方法实现的 return self.__pwd alex = User(‘alex‘,‘sbsbsb‘)print(alex.get__pwd()) # sbsbsb (只能看不能改)
- 可以让你看,也可以让你改(但是要求你按照我的规则改)
class User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 def get_pwd(self): # 表示的是用户不能改只能看 私有 + 某个get方法实现的 return self.__pwd def change_pwd(self): # 表示用户必须调用我们自定义的修改方式来进行变量的修改 私用 + change方法实现 pass
- 不想让你看,也不想让你改
- 封装的语法
- 私有的静态变量
# 外部不可以调class User: __Country = ‘China‘ # 私有的静态变量 def func(self): print(User.__Country) # 在类的内部可以调用print(User.Country) # 报错 在类的外部不能调用print(User.__Country)# 报错 在类的外部不能调用??# 内部可以调class User: __Country = ‘China‘ # 私有的静态变量 def func(self): print(User.__Country) # 在类的内部可以调用User().func() # China 说明内部可以调用
- 私有的实例变量
class User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象
- 私有的绑定方法
import hashlibclass User: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量 def __get_md5(self): # 私有的绑定方法 md5 = hashlib.md5(self.usr.encode(‘utf-8‘)) md5.update(self.__pwd.encode(‘utf-8‘)) return md5.hexdigest() def getpwd(self): return self.__get_md5()alex = User(‘alex‘,‘sbsbsb‘)print(alex.getpwd()) # d6170374823ac53f99e7647bab677b92
- 私有化目的:所有的私有化都是为了让用户不在外部调用类中的某个名字
- 私有化作用:如果完成私有化,那么这个类的封装度就更高了,封装度越高各种属性和方法的安全性也越高,但是代码越复杂
- 私有的特点
- 能不能类的内部使用? 可以
- 能不能类的外部使用? 不可以
- 能不能类的子类中使用? 不可以
- 类中变量的级别,哪些是python支持的,哪些是python不支持的
- 共有的
public 公有的 类内类外都能用,父类子类都能用
python支持
- 保护的
protect 保护的 类内能用,父类子类都能用,类外不能用
python不支持
- 私有的
private 私有的 本类的类内部能用,其他地方都不能用
python支持
- 共有的
- 私有的静态变量
- 使用的三种情况
原文地址:https://www.cnblogs.com/yangzm/p/10993651.html
时间: 2024-11-08 17:34:51