1.初识封装
什么是封装
就是将复杂的丑陋的隐私的细节隐藏到内部,对外提供简单的使用接口
为什么需要封装
1.为了保证关键数据的安全性
2.对外部隐藏实现的细节,隔离复杂度
什么时候应该封装
当有一些数据不希望外界可以直接修改时
当有一些函数不希望外界使用时
封装的语法:需要封装的属性和方法前加__
1 # 封装的小例子 2 class Person: 3 def __init__(self,name,age,id): 4 self.name = name 5 self.age = age 6 self.__id = id # 把id封装了 7 8 def say_id(self): 9 print('我的id是:%s'%self.__id) 10 11 p1 = Person('sxc',18,11111) 12 # print(p1.id) # 外界无法访问 13 # print(p1.__id) # 外界无法访问 14 p1.id = 555 15 print(p1.id) 16 p1.say_id() # 外界无法修改
2.封装方法
封装的方法:需要封装的属性和方法前加__
被封装的内容的特点
1.外界不能直接访问
2.内部依然可以使用
模拟电脑开机的例子
1 # 模拟电脑的开机 2 class PC: 3 def __init__(self,brand,price,color): 4 self.brand = brand 5 self.price = price 6 self.color = color 7 8 # def check_device(self): # 封装前可以直接调用 9 def __check_device(self): # 封装后不可以在外部调用,可以在内部调用 10 print("硬件检测1") 11 print("硬件检测2") 12 print("硬件检测3") 13 print("硬件检测4") 14 15 # def start_services(self): # 封装前可以直接调用 16 def __start_services(self): # 封装后不可以在外部调用,可以在内部调用 17 print("启动服务1") 18 print("启动服务2") 19 print("启动服务3") 20 print("启动服务4") 21 22 # def login(self): # 封装前可以直接调用 23 def __login(self): # 封装后不可以在外部调用,可以在内部调用 24 print("login....") 25 print("login....") 26 print("login....") 27 28 # 定义一个打开电脑的方法,类比电源键 29 def open(self): 30 print('接通电源') 31 self.__check_device() 32 print("载入内核") 33 print("初始化内核") 34 self.__start_services() 35 print("启动GUI") 36 self.__login() 37 38 p1 = PC('apple',10000,'red') 39 40 # 比如未封装前要经历三步才能打开电脑 41 # p1.check_device() 42 # p1.start_services() 43 # p1.login() 44 45 # 封装之后只需经历一步就能打开电脑,就像按电源键一样 46 p1.open()
封装可以控制属性的权限
在python中只要两种权限
1.公开的,默认就是公开的
2.私有的,只能由当前类自己使用
3.在外界访问被封装的属性
类中的属性虽然被封装起来了,但是还是需要使用的,如果不使用就失去了意义
在外界是通过定义方法类完成对私有属性的修改和访问
对人的id的封装与修改
1 # 通过类中的方法即函数访问/修改/删除被封装的属性 2 class Person: 3 def __init__(self,name,age,id): 4 self.name = name 5 self.age = age 6 self.__id = id # 把id封装起来 7 8 def get_id(self): # 获取id的方法 9 return self.__id 10 11 12 def set_id(self,new_id): # 修改id的方法 13 if type(new_id) == int: 14 str_id = str(new_id) 15 if len(str_id) == 7: 16 self.__id = new_id 17 print('新的id是%s'%self.__id) 18 return 19 print('请输入正确的id') 20 21 def del_id(self): # 删除id的方法 22 del self.__id 23 print('删除成功') 24 25 p1 = Person('sxc',18,1818299) 26 # print(p1.self.__id) # id已被封装,外界不能直接访问 27 28 print(p1.get_id()) # 访问被封装的属性 29 p1.set_id('1818211') # 可以判断修改的属性的对错 30 p1.set_id(1818211) # 修改被封装的属性 31 p1.del_id() # 删除被封装的属性 32 # print(p1.get_id()) # 已经删除,报错 33 print(p1.__dict__) # 结果中没有id属性
在通过函数访问或者修改类内部封装的属性时可以做一些逻辑判断
4.property装饰器的使用和封装的原理
通过方法来访问或者修改属性时,我们必须使用调用方法的格式,但这却给使用者带来了麻烦
使用者必须知道哪些属性是普通属性,哪些是私有属性,然后再使用不同的方法调用他们,这对使用者不友好
这时候我们可以使用property装饰器来装饰,可以解决这个问题
装饰器的语法,有三个相关的装饰器
@property # 用在获取被封装的属性的方法前 @属性名.setter # 用在修改被封装的属性的方法前 @属性名.deleter # 用在删除被封装的属性的方法前
注意:属性名是被property装饰的方法的名称,可以是任意的,但为了让使用者能方便的使用,默认使用原来的被封装的属性名,会在类的名称空间的内部创建一个对象,变量名就是函数名称,在使用.setter和.deleter方法时,必须保证使用对象的名称调用方法,所以是属性名.setter
刚才的例子的修改
1 # property装饰器的使用 2 class Person: 3 def __init__(self,name,age,id): 4 self.name = name 5 self.age = age 6 self.__id = id # 把id封装起来 7 8 @property 9 def id(self): # 获取id的方法 10 return self.__id 11 12 @id.setter 13 def id(self,new_id): # 修改id的方法 14 if type(new_id) == int: 15 str_id = str(new_id) 16 if len(str_id) == 7: 17 self.__id = new_id 18 print('新的id是%s'%self.__id) 19 return 20 print('请输入正确的id') 21 22 @id.deleter 23 def id(self): # 删除id的方法 24 del self.__id 25 print('删除成功') 26 27 p1 = Person('sxc',18,1818299) 28 29 print(p1.id) # 访问被封装的属性 30 p1.id = 1111 # 可以判断修改的属性的对错 31 p1.id = 1818211 # 修改被封装的属性 32 del p1.id # 删除被封装的属性 33 print(p1.__dict__) # 结果中没有id属性
python实现封装的原理
在加载类的时候,将被封装的属性__双下划替换成了_类名__
1 # 封装的实现原理 2 class Person: 3 def __init__(self,name,age,id): 4 self.name = name 5 self.age = age 6 self.__id = id 7 8 p1 = Person('sxc',18,1818299) 9 print(p1.__dict__) 10 ''' 11 输出结果是{'name': 'sxc', 'age': 18, '_Person__id': 1818299} 12 封装的__id本质是_Person__id,即内部帮我们做了'__id'.replace('__','_Person__') 13 ''' 14 print(p1._Person__id) # 这样就算封装了也能输出
5.计算属性
property可以用来实现计算属性
计算属性指的是:属性的值,不能直接获得,需要进行计算才能获得
例如通过正方形的边长求周长和面积
1 # 计算属性 2 # 计算一个正方形的周长和面积 3 class Square: 4 def __init__(self,side): 5 self.side = side 6 7 @property 8 def perimeter(self): 9 square_perimeter = self.side *4 10 return square_perimeter 11 12 @property 13 def area(self): 14 area = self.side **2 15 return area 16 17 s1 = Square(5) 18 print(s1.side) # 调用属性 19 print(s1.perimeter) # 可以像属性一样调用他的方法 20 print(s1.area) # 可以像属性一样调用他的方法
6.接口类、抽象类和鸭子类型(了解)
接口是一组功能的集合,但是接口中仅包含功能的名字,不包含具体的实现代码
接口本质是一套协议标准,遵循这个标准的对象就能被调用
接口的目的就是为了提高扩展性.
1 # 生成一个接口类 2 class USB: 3 def open(self): 4 pass 5 6 def read(self): 7 pass 8 9 def write(self): 10 pass 11 12 def close(self): 13 pass 14 15 # 鼠标这个硬件要继承这个接口类 16 class Mouse(USB): 17 def open(self): 18 print('打开鼠标') 19 20 def read(self): 21 print('获取光标位置') 22 23 def write(self): 24 print('不支持该操作') 25 26 def close(self): 27 print('关闭鼠标') 28 29 # pc操作 30 def pc(usb_device): 31 usb_device.open() 32 usb_device.read() 33 usb_device.write() 34 usb_device.close() 35 36 m = Mouse() 37 pc(m)
抽象类
指的是包含抽象方法(没有函数体的方法)的类
作用:可以限制子类必须使用父类中定义的抽象方法
1 # 抽象类 2 import abc 3 class A(metaclass=abc.ABCMeta): 4 @abc.abstractmethod # 生成一个限制条件,他的子类必须要有run这个方法 5 def run(self): 6 pass 7 8 9 class B(A): 10 def run(self): # 子类拥有父类这个方法 11 print('gogogogo!') 12 13 b = B() 14 b.run()
鸭子类型
接口是一套协议规范,明确子类们应该具备哪些功能
抽象类是用于强制要求子类必须按照协议中规定的来实现
然而,python不推崇限制程序员的语法,我们可以设计成鸭子类型,即让多个不同类和对象具备相同的属性和方法,对于使用者可以轻松的使用各种对象.
22