day24
一、面向对象之封装(续):
1、什么是封装
在程序中,封装就是将其内容隐藏起来,面向对象中就是将属性和功能隐藏起来。
注意:程序中的封装不是单纯的隐藏,而是指隐藏内部实现的细节,对外部提供使用接口。
2、为什么要封装
a、提高安全性
对于封装属性而言,是通过给访问和修改增加额外的逻辑判断来实现的。
b、封装的目的是为了明确的区分内外部
c、对于封装方法而言,可以隔离复杂度
#封装的隔离复杂度的示例: # ATM 的取款功能 # 1.插入银行卡 2.输入密码 3.选择取款金额 4.取款 class ATM: def __insert_card(self): print("插入银行卡...") def __input_pwd(self): print("输入密码...") def __select_money(self): print("选择取款金额...") def withdraw(self): #给外部提供一个简单方便的接口来调用一系列的功能 self.__insert_card() self.__input_pwd() self.__select_money() print("取款成功!....") atm = ATM() atm.withdraw() # 外部调用这个简单的接口 就能完成一系列复杂的操作 # atm.select_money() #直接调用内部的方法 是没有意义的,无法完成整个功能 # 用户按照流程一一调用也可以完成功能 但是太麻烦 # atm.insert_card() # atm.input_pwd() # atm.select_money()
3、如何使用封装
在属性或者方法的名称前,加上两个下划线,就可以将其设置为私有的属性或方法。
注:Python中的权限只有两种,公开(谁都能访问)的和私有(自由自己能访问)的
属性的封装,通过需要,提供相应的设置器和访问器。
4、封装的实现原理
class People: def __init__(self,name,age,sex,idCard): self.name = name self.age = age self.sex = sex self.__idCard = idCard #设置了一个私有属性 self._People__idCard = idCard def __tell(self): print("这是一个隐藏方法!") def get_idCard(self): return self.__idCard p1 = People("egon","18","male","131938918") print(p1.__dict__) #可以查看对象的名称空间中的所有属性,可以看见__idCard变成_People__idCard,如果在外部通过p1._People__idCard是可以访问到该属性的。 p1._xx = 22 print(p1.__dict__) #此时在来查看p1对象的名称空间中的所有属性发现,_xx在名称空间中并没有转变成为_People__xx。 class A: def f(self): self.__f1() #_A__f1 def __f1(self): print("A __f1") class B(A): def __f1(self): # _B__f1 print("B __f1") def f2(self): self.f() b = B() b.test() #子类的私有方法是无法覆盖父类的私有方法的,根据Python对隐藏属性的转换机制,子类和父类中的私有方法的名称必然不相同,所以无法覆盖 子类的方法一定子类独有的 因为名称不同 总结:# 1.在类的定义阶段,私有的属性和方法名称前会自动加上_类名,python就是通过这样的转换方式来实现封装的;
2.只有在类的内部的双下划线才会被自动转换,并且这个转换过程只执行一次(类定义时),即在类定义完成后,后续添加的双下划线开头的名称是不会自动转换的;
3.父类中私有的方法在子类中是无法使用的。
5、装饰器property
a、property的使用场景:
当一些属性的值 不是固定的而是通过计算得来的时候 我们必须为这个属性增加方法才能完成计算,但是一旦使用方法后 该属性的访问就变成了方法的调用 很明显与其他的属性访问方式不同,这样给使用者造成迷惑,所以需要将这个方法伪装成普通属性,此时就用到了Property。
b、property的特点:
property可以将方法伪装成属性,并且伪装后的对象调用此方法与调用普通属性的方式一致,同时,property还提供了 setter(用于修改属性的值) 和 deleter(删除属性的值)。
# BIM案例: --------初级(不能解决问题)------- class Person: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height self.bmi = weight/(height*height) p = Person("形象",50,1.5) print(p.bmi) #得到的结果为50/(1.5**2) p.weight = 90 print(p.bmi) #得到的结果仍为50/(1.5**2) 因为bim的值在定义阶段就已经确定,后续改变是不会变化的 --------中级(能解决问题,但是不完美)------- class Person: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height def bmi(self): return self.weight / (self.height * self.height) # 虽然可以实现需求 但是我们把一个属性变成了一个行为 这是不合理的 p = Person("形象",50,1.5) print(p.bmi()) #调用方式变成了掉用方法的形式,容易造成误解 p.weight = 90 print(p.bmi()) #结果会随着计算输入值的变化而变化,但调用方式变成了掉用方法的形式,容易造成误解 --------高级(完美解决)------- class Person: def __init__(self,name,weight,height): self.name = name self.weight = weight self.height = height @property def bmi(self): return self.weight / (self.height * self.height) # 使用property装饰器 可以将一个方法伪装成一个属性 print(p.bmi) p.height += 0.2 print(p.bmi)
property的使用方法案例:
class Student: def __init__(self,name,sex,idCard): self.name = name self.sex = sex self.__idCard = idCard def get_idCard(self): return self.__idCard def set_idCard(self,new_id): self.__idCard = new_id #----------第一种写法(新形式的写法)---------- @property # 需要掌握 def idCard(self): return self.__idCard @idCard.setter #了解的 def idCard(self,new_id): self.__idCard = new_id @idCard.deleter # 了解的 def idCard(self): print("身份证属性被删除了.....") del self.__idCard #----------第二种写法(老形式的写法)---------- def get_idCard(self): return self.__idCard def set_idCard(self, new_id): self.__idCard = new_id def del_idCard(self): print("身份证属性被删除了.....") del self.__idCard idCard = property(get_idCard,set_idCard,del_idCard) ------------------------------------------- stu = Student("尔康","男","323254554554") # print(stu.get_idCard()) # stu.set_idCard("xxxx") print(stu.get_idCard()) # 使用装饰器前 print(stu.name) # 普通属性的访问 print(stu.idCard) # 使用装饰器后 stu.idCard = "aaaaaaa" # 使用装饰器后的修改操作 print(stu.idCard) del stu.idCard print(stu.__dict__) print(Student.__dict__)
二、多态
1、什么是多态
多态即一种事物具备多种不同的形态和状态;Python中的多态,不同对象 可以响应同一方法,并作出不同的行为,产生不同结果。
2、如何实现多态
让几个不同类拥有相同父类,这样一来他们就具备了相同的方法,每个子类要覆盖父类的方法,从而每个类的对象的行为都不同。
通过代码提现多态:
class Animal: def eat(self): pass class Person(Animal): def eat(self): print("人吃粮食...") class Pig(Animal): def eat(self): print("猪吃饲料...") class Dog(Animal): def eat(self): print("狗吃骨头...") person = Person() pig = Pig() dog = Dog() person.eat() pig.eat() dog.eat() # people dog pig 同属于动物类,他们都具备有吃的方法,但是他们各自吃的方式有不一样,调用相同方法后,得到的结果各不相同,这就称为多态。 #---------系统中的内置多态的提现形式------- # 系统内置的方法有很多都体现了多态 print(len("abc")) #字符串类型 print(len([1,2,3,4,])) #列表类型 print(len({"name":"123","sex":"man"})) #字典类型 #不同的类型可以调用同一个方法,得到的结果不尽相同。 #以下为另一种调用形式 print("abc".__len__()) print([1,2,3,4,].__len__()) print({"name":"123","sex":"man"}.__len__()) print(len({1,23,4,5}))
3、abc模块
a、abc模块的使用场景:
多态是多个类的对象拥有相同的方法,但是我们没有从严格要求说必须提供这些方法,子类完全可以不提供这些方法,但是如果要严格要求子类必须实现父类声明的方法时可以导入abc模块来帮组解决此问题。
b、使用abc模块来限制子类时的使用步骤:
1、首先导入abc模块 (import abc);
2、为类中指定元类为abc.ABCMeta;
3、在相应的方法上加上abc.abstractmethod装饰器。
注:如果子类中没有实现父类中声明的方法是,调用时会报错。
import abc # abstract class 是抽象类的缩写 抽象的意思是 不清晰 不具体 看不懂 class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass @abc.abstractmethod def drink(self): pass class Cat(Animal): def eat(self): print("猫爱吃鱼肉...") def drink(self): print("用舌头舔..") class Dog(Animal): def eat(self): print("狗爱吃骨头...") def drink(self): print("用舌头舔..") class Pig(Animal): def eat(self): print("猪 爱吃草...") def drink(self): print("用嘴吸的..") p = Pig() # p.eat() c = Cat() # c.eat() # 多态的好处 完全不需要考虑得到的对象时声明类型 只要知道了其基类中的内容就能使用 def feeding(animal): animal.eat() animal.drink() feeding(c) feeding(p) # 多态中的基类 相当于(协议 标准 规范) 要求子类必须满足这些标准
多态练习
class DesktopComputer(Computer): def open(self): print("台式机正在启动....") def shutdown(self): print("台式机正在关机....") class Worker: def working(self,pc): # 先开机 pc.open() print("工作中.....") pc.shutdown() w1 = Worker() dp = DesktopComputer() w1.working(dp) # 增加了笔记本电脑 class BookComputer(Computer): def open(self): print("笔记本正在启动....") def shutdown(self): print("笔记本正在关机....") bc = BookComputer() w1.working(bc) # 增加了平板电脑 class PadComputer(Computer): def open(self): print("平板正在启动....") def shutdown(self): print("平板正在关机....") bc = PadComputer() w1.working(bc)
4、鸭子类型:
a、定义:鸭子类型 如果一个对象叫声像鸭子 走路也想鸭子 那就把它当成鸭子。
对应到代码中就是: 只要你的行为一样 那就把你当成同一个类型来看待。
b、代码示例:
class Duck: def bark(self): print("鸭子嘎嘎叫...") def run(self): print("摇摇晃晃走....") class Chicken: def bark(self): print("鸡咯咯叫...") def run(self): print("摇摇晃晃走....") def test(obj): obj.bark() obj.run() duck = Duck() c = Chicken() test(duck) test(c) # 如果在写程序的时候能够自觉的将相似类型的类的相同方法的名字写成一样,不使用abc模块,同样可以实现多态。 # 这种方式称之为鸭子类型