一.封装方法
语法:方法名前加双下划线
优点:1.提高安全性;2.隔离复杂度(将复杂的内容隔离到内部,使用者通过简单的使用接口就可进行访问)
# 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()
二.封装的实现原理
python是通过语法的转换来实现封装的,具体就是在私有的属性和方法名前,自动加上了_类名,因此__属性名和__方法名就转换成了_类名__方法名和_类名__属性名。
注:
1.只有在类的内部的双下划线开头的属性和方法才会被自动转换,且这种转换过程只执行一次,在类定义完成之后,后续在外部添加的双下划线开头的属性和方法是不会进行转换的。
2.父类中被封装的方法,子类中无法进行访问和覆盖。
3.我们知道了封装的实现原理,也可以绕过封装的限制进行访问,但是这种做法是毫无意义的。
class Person: def __init__(self,name,sex,age,idCard): self.name = name self.sex = sex self.__age = age self.__idCard = idCard def get_idCard(self): return self.__idCard def __test(self): pass print("aaaaaaaaaa") # p = Person("比尔盖茨","男",20,"322323232332332") # # # print(p.__idCard) # p.__idCard = "XXXXXX" # print(p.__idCard) # # print(p.get_idCard()) # # print(p.__dict__) # print(Person.__dict__) # # # p.__xxxxxxxxxxxx = 1 # # print(p.__dict__) # 通过__dict__ 可以发现 # 1.私有的属性和方法名称 前自动加上了_类名 python就是通过这样的转换方式来实现封装 # 2.只有在类的内部的双下划线才会被自动转换,并且这个转换过程只执行一次,在类定义完成后 后续添加的双下划线开头的名称是不会自动转换的 # 3.父类中私有的方法 子类中无法使用 #在父类中定义的私有方法 能不能被子类所覆盖? # class A: # def __f1(self): # print("A __f1") # # class B(A): # def __f2(self): # # self.__f1() # super().__f1() # print("B __f2") # # def test(self): # self.__f2() # # b = B() # b.test() # 子类无法覆盖父类的私有方法 # 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.f2() # 之所以无法覆盖 是因为 子类和父类中的私有方法 名称必然不相同 所以无法覆盖 子类的方法一定子类独有的 因为名称不同 class A: __age = 10 # 绕过封装的限制直接访问 这是毫无意义的 print(A._A__age)
三.property装饰器
1.什么是property?
property是一种特殊的属性,访问它时会执行一段功能(函数),然后返回值。
2.为什么用property?
当一些属性的值,不是固定的,而是通过计算得来的,我们就需要为其添加方法才能完成计算。这样一来该属性的使用就会变成调用方法才能使用它,会给使用者造成困惑。这时候就需要用到property装饰器装饰该方法,使其伪装成一个属性。在使用者调用该方法时,会跟访问属性的形式一样,从而形成了访问属性的统一标准。
3.如何用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) # def bmi(self): # return self.weight / (self.height * self.height) @property def bmi(self): return self.weight / (self.height * self.height) p = Person("尔晴",50,1.5) # print(p.bmi) # p.weight = 90 # print(p.bmi) # 现在 虽然可以实现需求 但是我们把一个属性变成了一个行为 这是不合理的 # print(p.bmi()) # p.weight = 90 # print(p.bmi()) # 使用property装饰器 可以将一个方法伪装成一个属性 print(p.bmi) p.height += 0.2 print(p.bmi) 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 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
四.多态
1.什么是多态?
多态简单的来说就是一个事物有多种状态。在程序中,不同对象可以享有同一方法,并作出不同的行为,产生不同的结果。
2.为什么用多态?
1.增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都用同一种方式去调用。
2.增加了程序的可扩展性
通过继承基类创建一个新的类,使用者无需修改代码,还是使用之前的方式去调用。
3.如何实现多态?
让几个不同的类继承相同的父类,这样子类就可以拥有相同的方法,每个子类都要覆盖父类的方法,这样子类的对象行为都不同。
class Animal: def eat(self): print("动物在吃东西...") def sleep(self): print("动物在睡觉...") def drink(self): print("动物需要水.....") 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() #假设你学习了C1驾照 意味着 所有C1类的汽车都能开 因为每种C1汽车的驾驶方式相同 # 当使用了多态之后 对象的使用者不需要关系这个对象具体的实现,只需要知道该对象属于哪个基类,就能直接使用它 # 如此扩展性变高了 class Phone: def call(self): print("手机就能打电话..") def send_msg(self): print("手机能发短信..") class WindowsPhone(Phone): def call(self): print("拨号打电话..") def send_msg(self): print("输入号码发短信..") class IPhone(Phone): def call(self): print("拨号打电话..") def send_msg(self): print("输入号码发短信..") # 可以定义一个方法接收一个手机为参数 无论是是类型的手机 都可以被使用 def CALL(phone): phone.call() wp = WindowsPhone() ipx = IPhone() CALL(wp) CALL(ipx) # 系统内置的方法有很多都体现了多态 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}))
4.多态之ABC模块
abc模块(abstract class(抽象类)),使用abc模块来限制子类的步骤。
4.1.为类中指定元类为abc.ABCMeta
4.2.在相应的方法加上abc.abstractmethod装饰器
import abc # abstract class 是抽象类的缩写 抽象的意思是 不清晰 不具体 看不懂 #使用ABC模块来限制子类 的步骤 #1.为类中指定元类为abc.ABCMeta #2.在相应的方法上加上abc.abstractmethod装饰器 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) # 多态中的基类 相当于(协议 标准 规范) 要求子类必须满足这些标准
import abc # 电脑基类 class Computer(metaclass=abc.ABCMeta): @abc.abstractmethod def open(self): pass @abc.abstractmethod def shutdown(self): pass # 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)
五.鸭子类型
python推崇的是鸭子类型,推崇简单的编程方式。鸭子类型指的是如果一个对象叫声像鸭子,走路的姿势也像鸭子,那就把它当成鸭子。对应到代码中:只要你的行为一样,就把你当成同一个类型来对待。
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模块 也不需要基类 自觉地将方法名字都写成一样 同样可以实现多态 # 这种方式称之为鸭子类型
其实我们一直在享受着多态性带来的好处,比如Python的序列类型有多种形态:字符串,列表,元组,多态性体现如下:
#str,list,tuple都是序列类型 s=str('hello') l=list([1,2,3]) t=tuple((4,5,6)) #我们可以在不考虑三者类型的前提下使用s,l,t s.__len__() l.__len__() t.__len__() len(s) len(l) len(t)