面向对象开发: 封装, 继承, 多态
1. 基本知识:
# (1) 类的定义: 使用class关键字定义,类名最好采用大驼峰体 class MyCar1: #可以不加括号 pass class MyCar2(): # 此种方法推荐 pass class MyCar3(object): pass # (2) 类的实例化: obj = MyCar2() # (3) 类的基本结构: 类中值包含两种成员,一种是成员属性(相当于变量),一种是成员方法(相当于函数) class MyHouse(): size = "big" def func1(self): print("It's a dream")
2. 定义一个类: 封装的基本操作,类里面主要封装公有属性和方法,私有属性和方法
class Travel(): didian = "海南" # 公有属性 __jiudian = "海韵" # 私有属性 def luxian(self): # 公有方法,形参self,也叫绑定方法(绑定到对象) print("先去南海观音,再去天涯海角") def __shijian(self): # 私有方法 print("明天启程")
3. 对象的相关操作:
3.1 实例化对象访问公有成员的属性和方法,若是调用私有的属性或方法,程序会报错
obj = Travel() print(obj.didian) # 海南 obj.luxian() # 先去南海观音,再去天涯海角 # (2) 实例化对象动态的添加公有成员属性和方法 obj.didian = "西藏" print(obj.didian) # 西藏 # 通过__dict__可以查看对象或者类里面的成员,返回一个字典 print(obj.__dict__) # {'didian': '西藏'}
3.2 通过对象添加成员方法, 一共有三种方法
# 方法一: 动态添加无参方法 def nanhai(): print("南海观音真雄伟,让人惊叹!") # 通过对象.属性(自定义的) = 方法 添加 obj.nanhai = nanhai obj.nanhai() # 南海观音真雄伟,让人惊叹! # 方法二: 动态添加有参方法 # 形式一: def menpiao(price): print("进门票价{}元".format(price)) obj.menpiao = menpiao obj.menpiao(160) # 进门票价160元 # 形式二: def menpiao(obj,price): print("进门票价{}元,地点是{}".format(price,obj.didian)) obj.menpiao = menpiao obj.menpiao(obj,160) # 进门票价160元,地点是西藏 # 形式三: 通过types.MethodType来创建一个方法,自动传递obj对象 import types #(推荐) def menpiao(self,price): print("进门票价{}元,地点是{}".format(price,self.didian)) obj.menpiao = types.MethodType(menpiao,obj) # 调用此方法时,自动把对象当做参数传递给该方法 obj.menpiao(180) # 进门票价180元,地点是西藏 # 方法三: 动态添加lambda匿名函数 obj.money = lambda : print("一共准备5万") obj.money() # 一共准备5万
4. 类的相关操作: 访问公有的属性和方法,动态的添加方法
class Travel(): didian = "海南" # 公有属性 __jiudian = "海韵" # 私有属性 def luxian(): # 公有无参方法 print("先去南海观音,再去天涯海角") def __shijian(): # 私有无参方法 print("明天启程") # (1)定义的类访问共有成员和方法,若访问私有的会报错 print(Travel.didian) # 海南 Travel.luxian() # 先去南海观音,再去天涯海角 # (2) 定义的类动态的添加公有的属性和方法 # 方法一: 动态的添加无参方法 def shan(): print("去桂林看山") Travel.shan = shan Travel.shan() # 去桂林看山 # 方法二: 动态的添加有参方法 def shui(name): print("去看看{}".format(name)) Travel.shui = shui Travel.shui("西湖") # 去看看西湖 # 方法三: 动态的添加lambda匿名函数 Travel.cao = lambda : print("去西藏看大草原") Travel.cao() # 去西藏看大草原 Travel.visit = lambda name : print("一起看世界,和{}".format(name)) Travel.visit("喜欢的人") # 一起看世界,和喜欢的人
* 类与对象的区别: 类里面的成员只归属于类本身, 对象可以调用类里面公有的属性和方法,但是不能修改, 对象在调用成员时,先查找自己本身有没有这个成员,没有的话再从类里面找, 如果类里面也没有,程序报错. 类无法调用对象里面的成员.
5. 查看私有成员: 两种方法
class Travel(): didian = "海南" # 公有属性 __jiudian = "海韵" # 私有属性 def luxian(): # 公有无参方法 print("先去南海观音,再去天涯海角") def __tongxing(): # 私有无参方法 print("不能透露") def shijian(self): # 公有有参方法 print("明天启程") def __fanghao(self): print("我会告诉你是888号吗?") obj = Travel() # 方法一: 根据python存私有变量的规律[_类名__成员名](不推荐) print(obj._Travel__jiudian) # 海韵 obj._Travel__fanghao() # 我会告诉你是888号吗? print(Travel._Travel__jiudian) Travel._Travel__tongxing() # 不能透露
# 方式二: 通过类内的共有方法间接调用私有成员(推荐) class Travel(): didian = "海南" # 公有属性 __jiudian = "海韵" # 私有属性 def luxian(): # 公有无参方法 print("先去南海观音,再去天涯海角") def __tongxing(): # 私有无参方法 print("不能透露") def shijian(self): # 公有有参方法 print("明天启程") def __fanghao(self): print("我会告诉你是888号吗?") def fangfa(self): print(self.__jiudian) self.__fanghao() def fangfa2(): print(Travel.__jiudian) Travel.__tongxing() obj = Travel() obj.fangfa() # 海韵 我会告诉你是888号吗? Travel.fangfa2() # 海韵 不能透露
6. 删除相关成员: 实例化的对象删除公有的属性和方法, 定义的类删除公有的成员和方法
class Travel(): didian = "海南" # 公有属性 __jiudian = "海韵" # 私有属性 def luxian(): # 公有无参方法 print("先去南海观音,再去天涯海角") def __tongxing(): # 私有无参方法 print("不能透露") def shijian(self): # 公有有参方法 print("明天启程") def __fanghao(self): print("我会告诉你是888号吗?") def fangfa(self): print(self.__jiudian) self.__fanghao() def fangfa2(): print(Travel.__jiudian) Travel.__tongxing() obj = Travel() # 实例化的对象删除公有的属性和方法 obj.didian = "上海" print(obj.didian) # 上海 del obj.didian print(obj.didian) # 海南 obj.zou = lambda : print("说走就走") obj.zou() # 说走就走 del obj.zou # obj.zou() # 报错,因为方法已删除 # 定义的类删除公有的成员和方法 del Travel.didian # print(Travel.didian) # 报错 del Travel.luxian # Travel.luxian() # 报错 del Travel.__fanghao # 报错
7. 继承: 一个类除了自身有的属性方法之外,也能调用另外一个类的属性和方法. a类继承b类,那么a类就叫子类(衍生类),b类就叫父类(基类, 超类),python里所有的类都默认继承父类object
7.1 单继承: 子类只继承了一个类
class Kids(): hobby = "play" __food = "everything" def book(self): print("必须是儿童书") def watch(self): print("必须是动画片") def fangfa(self): print(Kids.__food) class Teenage(Kids): # 单继承 pass # (1) 子父继承之后, 子类可以调用父类所有公有的成员,私有成员是不能调用的,调用的话程序报错 obj = Teenage() print(obj.hobby) # play obj.book() # 必须是儿童书 # print(obj.__food) # 报错 # 通过定义公有方法获取私有成员 obj.fangfa() # everything # (2) 子父继承之后,子类可以重新父类的同名方法 class Adults(Kids): hobby = "sleep" def watch(self): print("新闻和电影") obj = Adults() print(obj.hobby) # sleep obj.watch() # 新闻和电影 obj.book() # 必须是儿童书 # 子类在调用属性或者方法时,先看自己本身有没有,有的话用自己的,没有的话就去父类里面找,如果父类也没有,程序就报错
7.2 多继承: 子类继承多个父类
class Kids(): hobby = "eat and sleep" def k_book(self): print("必须是儿童书") class Teenage(): hobby = "eat and paly" def t_book(self): print("必须是武侠和漫画书") class Adults(Kids,Teenage): pass obj = Adults() print(obj.hobby) # eat and sleep obj.k_book() # 必须是儿童书 obj.t_book() # 必须是武侠和漫画书 # (1) 用类来调用父类的成员 class Adults(Kids,Teenage): def func1(self): print(Kids.hobby) Kids.k_book(self) Teenage.t_book(self) obj = Adults() obj.func1() # eat and sleep # 必须是儿童书 # 必须是武侠和漫画书 # (2) 用对象来调用父类的成员 # self在调用父类的成员时,先找自己的,自己没有再去找父类的,默认先找括号里左边的父类 class Adults(Kids,Teenage): def func2(self): print(self.hobby) self.t_book() obj = Adults() obj.func2() # eat and sleep # 必须是武侠和漫画书 # (3) 用super()来调用父类的成员 """ super本身就是一个类,super()本身就是一个对象,用于调用父类的绑定方法,如果不是绑定方法,程序会报错 super()默认传递self对象,前提是super所在作用域存在self super用途:解决复杂的多用途的调用顺序 super()在调用的时候只会调用父类的成员,如果父类里没有这个成员,就会报错 """ class Adults(Kids,Teenage): def func3(self): print(super().hobby) super().t_book() super().k_book() obj = Adults() obj.func3() # eat and sleep # 必须是武侠和漫画书 # 必须是儿童书
7.3 菱形继承:
class Tree(): num = 4 def func1(self): print(" 8 我是大树") print(self.num) # 1 此时self接收的是Seed的对象,所以这里打印1 print(" 7 我能进行光合作用") class Grass(Tree): num = 3 def func1(self): print(" 6 我是小草") super().func1() print(" 5 我有美化作用") class Flower(Tree): num = 2 def func1(self): print(" 4 我是花儿") super().func1() print(" 3 我比较好看") class Seed(Grass,Flower): num = 1 def func1(self): print(" 2 我是种子") super().func1() print(" 1 我能变成大树,小草,小花") obj = Seed() obj.func1() """ 打印顺序如下: 2 我是种子 6 我是小草 4 我是花儿 8 我是大树 7 我能进行光合作用 3 我比较好看 5 我有美化作用 1 我能变成大树,小草,小花 """ # 可以用类.mro()方法得到类调用方法的顺序,返回列表.针对于多继承里面的同名方法 print(Seed.mro()) # 此方法根据C3算法得来 # [<class '__main__.Seed'>, <class '__main__.Grass'>, <class '__main__.Flower'>, <class '__main__.Tree'>, <class 'object'>] """ 在新式类中,查找属性或者方法,依照广度优先原则查找,即mro()方法得出的类的顺序 在经典类中,查找属性或者方法,会依照深度优先查找,python3默认都是新式类 """ # issubclass() 判断是否是子父关系,只要在一个继承链里面即可,应用于类,返回bool值 print(issubclass(Seed,Tree)) # True print(issubclass(Tree,Grass)) # False print(issubclass(Grass,Tree)) # True print(issubclass(Grass,Flower)) # False print(issubclass(Grass,(Flower,Seed))) # False print(issubclass(Grass,(Flower,Seed,Tree))) # True # isinstance(): 判断类和对象的关系,在一个继承链上即可,返回bool值 print(isinstance(obj,Seed)) # True print(isinstance(obj,Tree)) # True
8. 多态: 不同的子对象,调用相同的父类方法,产生了不同的结果
class Tree(): def grow(self): pass def fruit(self): pass class Tao(Tree): def grow(self): print("长大成桃树") def fruit(self): print("结出甜甜的桃子") class Li(Tree): def grow(self): print("长大成梨树") def fruit(self): print("结出甜甜的梨子") class Apple(Tree): def grow(self): print("长大成苹果树") def fruit(self): print("结出甜甜的苹果") tao = Tao() li = Li() apple = Apple() farmer_list = [tao,li,apple] choose = input("请在123中选择,看看得到什么样的水果:") if "1" in choose: tao.grow() tao.fruit() if "2" in choose: li.grow() li.fruit() if "3" in choose: apple.grow() apple.fruit()
9 单态(例)模式: 无论实例化多少次,都只有一个对象.这样仅仅调用类中的成员,可以节省内存空间,适用于不用给对象额外添加成员的情况
# 其实就是用new方法每次都返回同一个自身对象 class Singerton(): __obj = None def __new__(cls, *args, **kwargs): if cls.__obj is None: cls.__obj = object.__new__(cls) return cls.__obj obj1 = Singerton() print(obj1) # <__main__.Singerton object at 0x0000019C17FDC8D0> obj2 = Singerton() print(obj2) # <__main__.Singerton object at 0x0000019C17FDC8D0> # 单态模式加构造方法 class Singerton(): __obj = None def __new__(cls, *args, **kwargs): if cls.__obj is None: cls.__obj = object.__new__(cls) return cls.__obj def __init__(self,name): self.name = name obj_singer1 = Singerton("bob") obj_singer2 = Singerton("jack") print(obj_singer1.name) # jack print(obj_singer2.name) # jack # 因为单态模式只创建了一个对象,这两个对象都是指向同一个值,同一个值打印了两次.单态模式适用于数据库的增删改查操作
10. 面向对象案例之小人射击(推荐使用包的形式书写,此处为了方便查看)
class BulletBox(): def __init__(self,bulletcount): self.bulletcount = bulletcount class Gun(): def __init__(self,bulletbox): self.bulletbox = bulletbox def shout(self,shoutcount): if self.bulletbox.bulletcount < shoutcount: print("子弹不够,还差{}颗".format(shoutcount-self.bulletbox.bulletcount)) else: self.bulletbox.bulletcount -= shoutcount print("打"*shoutcount,"子弹还剩{}颗".format(self.bulletbox.bulletcount))
class Person(): def __init__(self,gun): self.gun = gun def fire(self,firecount): self.gun.shout(firecount) def addcount(self,num): self.gun.bulletbox.bulletcount += num bulletbox = BulletBox(100) gun = Gun(bulletbox) sheji = Person(gun) sheji.fire(20) # 打打打打打打打打打打打打打打打打打打打打 子弹还剩80颗 sheji.addcount(30) sheji.fire(50) # 打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打打 子弹还剩60颗 sheji.fire((70)) # 子弹不够,还差10颗