Python学习笔记【第十篇】:Python面向对象进阶
保护对象的属性
如果有一个对象,当需要对其进行修改属性时,有2种方法
- 对象名.属性名 = 数据 ---->直接修改
- 对象名.方法名() ---->间接修改
为了更好的保存属性安全,即不能随意修改,一般的处理方式为
- 将属性定义为私有属性
- 添加一个可以调用的方法,供调用
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 class Person(object): def __init__(self, name, age, sex, nationality): self.name = name self.age = age self.sex = sex # 私有属性只能在类的内部访问。 self.__nationality = nationality def say(self): # 在内的方法内部就可以调用__nationality属性 print("大家好我叫:%s 今年%d岁了,我是一名%s生来自%s。" % (self.name, self.age, self.sex, self.__nationality)) if __name__ == "__main__": p1 = Person('李四', 25, '女', '中国') p1.say() print(p1.__dict__) # 是由属性调用报错 # print(p1.nationality)
如果需要在类外修改类属性
,必须通过类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性
,这种方式修改的是实例属性
,不会影响到类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性
,除非删除了该实例属性
。
继承
继承是新式类才有的特性,新建一个类,这个可以继承一个或多个父类,父类也称为基类。新建的这个类又叫做派生类或子类。
子类会自动拥有父类的一些属性和方法,从而起到代码重用
代码说明
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 # 定义一个父类Person class Person(object): def __init__(self, name, age, sex, nationality): self.name = name self.age = age self.sex = sex self.nationality = nationality def say(self): print("大家好我叫:%s 今年%d岁了,我是一名%s生来自%s。" % (self.name, self.age, self.sex, self.nationality)) # 继承Person class Chinese(Person): pass # 继承Person class American(Person): pass # 继承Person class Korean(Person): pass # 继承Person class Japanese(Person): pass if __name__ == "__main__": p1 = Person('李四', 25, '女', '中国') p1.say() n = p1.name print(n)
提示:如果没有指定基类,python3中的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
属性查找
像p1.name之类的属性引用,会先从实例中找name然后去类中找,然后再去父类中找...直到最顶级的父类。如果没有找到就报错
当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 # 定义一个父类Person class Person(object): def __init__(self, name, age, sex, nationality): self.name = name self.age = age self.sex = sex self.nationality = nationality def say(self): print("大家好我叫:%s 今年%d岁了,我是一名%s生来自%s。" % (self.name, self.age, self.sex, self.nationality)) # 继承Person class Chinese(Person): # 没有定义__init__方法 def say(self): print("大家好我是%s" % self.name) # 继承Person class American(Person): pass # 继承Person class Korean(Person): pass # 继承Person class Japanese(Person): pass if __name__ == "__main__": p1 = Person('李四', 25, '女', '中国') p1.say() n = p1.name print(n) # ============子类初始化======== c1 = Chinese('雷锋', 25, '男', '中国') c1.say() # c1实例的属性集合 print(c1.__dict__) # Chinese类属性集合 print(Chinese.__dict__)
c1 这个实例是由Chinese类初始化的,但是这个类没有实现__init__方法,就是去父类Person查找,这时侯在父类中找到了,就调用父类的__init__方法初始化。c1.say() 首先在自己的__dict__属性集合中查找,然后去类的__dict__属性集合中查询。当在Chinese类的__dict__属性集合中找到了,就直接调用自己类的say()方法。
属性查找顺序
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 class D(object): def bar(self): print('D.bar') class C(D): def bar(self): print('C.bar') class B(D): def bar(self): print('B.bar') class A(B, C): def bar(self): print('A.bar') a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar() """ 经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错 新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 """
继承原理:
经典类型:深度优先
没有继承object类以及它的子类
新式类:广度优先
python3以后都是新式类,或者在python2中继承了object类
调用父类方法关键字:supper()
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 """调用父类方法关键字:supper()""" class Person(object): def __init__(self, name, sex, age, country): self.name = name self.sex = sex self.age = age self.country = country def say(self): print("大家好我是:%s,今年%d 岁了,是一名%s生。我是个%s人。" % (self.name, self.age, self.sex, self.country)) def eat(self): print("%s 人在吃饭"%self.country) class Chinese(Person): def __init__(self, name, sex, age, country): # 第一种 # Person.__init__(self, name, sex, age, country) # Person.eat(self) # 第二种 super().__init__( name, sex, age, country) super().eat() # def say(self): # print('中国人说。') class American(Person): pass class Korean(Person): pass class Japanese(Person): pass if __name__ == "__main__": c1 = Chinese("王二小", '男', 15, "中国") c1.say()
super方法补充:
python3中有个mro查找列表如下:
super()是依赖继承但是是根据mro列表里的顺序去查找的
多态
继承的表现新式
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 """ 不同的类实例化不同的实例调用父类相同的方法,执行不同的逻辑""" class Person(object): # 同一事物--人类 def __init__(self, name, country): self.name = name self.country = country def say(self): print("我是%s 我是%s人" % (self.name, self.country)) class Chinese(Person):# 人类形态之一:中国人 pass class American(Person):# 人类形态之一:美国人 pass class Korean(Person):# 人类形态之一:韩国人 pass class Japanese(Person):# 人类形态之一:日本人 _addr = '小岛国' __add = '小小岛国。。。。' def say(obj): obj.say() if __name__ == "__main__": pass c1 = Chinese("王二小", "中国") a1 = American("Josns", "美国") j1 = Japanese('博古一朗', '小岛') # c1.say() # a1.say() # j1.say() say(c1) say(a1) say(j1) # 下面的 __len__()和len() 和上面的 say 一样 s1 = "hello" s2 = str([1, 3, 4, 5]) print(s1.__len__()) print(len(s2)) print("\r\n=============================\r\n") j2 = Japanese("刚播一次", '到过') print(j2._addr) print(j2._Japanese__add)
其实大家从上面多态性的例子可以看出,我们并没有增加什么新的知识,也就是说python本身就是支持多态性的,这么做的好处是什么呢?
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
封装
1:封装数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。
2:封装方法:目的是隔离复杂度
提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。
面向对象的封装有三种方式:
【public】 这种其实就是不封装,是对外公开的 【protected】 这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开 【private】 这种封装对谁都不公开
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
class Foo: def __init__(self,val): self.__NAME=val #将所有的数据属性都隐藏起来 @property def name(self): return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在设定值之前进行类型检查 raise TypeError('%s must be str' %value) self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME @name.deleter def name(self): raise TypeError('Can not delete') f=Foo('egon') print(f.name) # f.name=10 #抛出异常'TypeError: 10 must be str' del f.name #抛出异常'TypeError: Can not delete'
综合案例
# -*- coding: utf-8 -*- # 声明字符编码 # coding:utf-8 class Person(object): '''人类''' def __init__(self, name): self.name = name self.hp = 100 self.gun = None def use_bullet(self, clip_temp, bullet_temp): clip_temp.use_bullet(bullet_temp) def use_clip(self,gun_temp, clip_temp): gun_temp.use_clip(clip_temp) def pick_up_gun(self, gun_temp): self.gun = gun_temp def shoot(self, enemy_temp): self.gun.fire(enemy_temp) def __str__(self): if self.gun: return "%s 的生命值为:%d,%s" % (self.name, self.hp, self.gun) else: if self.hp>0: return "%s 的生命值为:%d,他没有枪"%(self.name,self.hp) else: return '%s 挂了'%self.name class Clip(object): '''弹夹''' def __init__(self, bullet_number): self.bullet_number = bullet_number self.lethality_list = [] def __str__(self): return '弹夹的信息为:%d/%d' % (len(self.lethality_list), self.bullet_number) def use_bullet(self,bullet_temp): self.lethality_list.append(bullet_temp) def extraction_bullets(self): '''弹夹提取子弹''' if self.lethality_list: return self.lethality_list.pop() else: return None class Bullet(object): '''子弹''' def __init__(self,lethality): self.lethality = lethality def shooting(self,enempy_temp): '''子弹射击敌人,敌人减少响应血量''' if enempy_temp.hp > 0: enempy_temp.hp -= self.lethality else: enempy_temp.hp = 0 class Gun(object): '''枪''' def __init__(self, name): self.name = name self.clip = None def __str__(self): if self.clip: return '枪的信息:%s,%s' % (self.name, self.clip) else: return '枪的信息:%s,无弹夹'%self.name def use_clip(self,clip_temp): self.clip = clip_temp def fire(self,enempy_temp): '''枪从弹夹中获取字段''' zi_dan = self.clip.extraction_bullets() if zi_dan: # 子弹伤害敌人 zi_dan.shooting(enempy_temp) else: print("弹夹中没有子弹了.....") # 创建老王对象 laowang = Person('老王') # 创建抢对象 gun = Gun('M16') # 创建弹夹 clip = Clip(30) # 创建一些子弹对象 for i in range(15): bullet = Bullet(10) # 老王把子弹装进弹夹 laowang.use_bullet(clip, bullet) # 老王把弹夹装进枪里 laowang.use_clip(gun, clip) # # 测试弹夹信息 # print(clip) # # 测试枪信息 # print(gun) #老王拿起枪 laowang.pick_up_gun(gun) #print(laowang) # 创建敌人 enemy = Person('敌人'); #print(enemy) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang) # 老王开枪打敌人 laowang.shoot(enemy) print(enemy) print(laowang)