今日内容
一、封装
什么是封装
对外不对内,不让类外部直接使用
对外部隐藏内部的属性以及实现的细节,隐藏并不是完全不给外部使用,给外部提供使用的接口
python中属性权限分两种:公开的【没有任何限制,外界可以操作数据】-- 默认
私有的【只有当前类本身能够访问】
为什么要学习封装
1.提高安全性【属性】,什么样的属性需要封装?
当这个对象存在一个机密必的属性,例如人的身份证、银行卡密码等
2.隔离复杂度【方法】,什么样的方法需要封装?
一个为内部提供支持的方法,不应该让外界直接访问的,如:在ATM项目中取款,只提供给外界一个入口,
类内部自调登入功能,成功后自调显示余额、输入取款金额、保存记录等。
还有如电脑开机键,就做了一系列的封装
如何封装
通过在类定义阶段对需要封装的属性/方法变形:__开头的属性/方法名会在类定义阶段变形添加_类名 如:_person__id_card
变形后实在要访问,可以用字典的方式访问:obj.__dic__['_person__id_card']
封装后还是可以给外界使用的
属性封装 --
1)类内定义一个接口函数,再把结果return回给外界调用者,这样return前可以添加一些条件限制,
2)修改属性:定义一个方法,将新值赋值给__开头的属性,达到修改属性的效果
问题:方问属性,变成调用函数,给调用者造成困扰?
class Student:
def __init__(self, name, age, gender, id_card):
self.name = name
self.age = age
self.gender = gender
self.__id_card = id_card
def get_id_card(self, pwd):
if pwd == '123':
return self.__id_card
raise Exception('密码错误')
def set_id_card(self, new_id):
if isinstance(new_id, str) and len(new_id) == 18:
self.__id_card = new_id
else:
raise Exception('身份证号码,必须是之符串,且长度必须为18!')
stu = Student('fanny', 18, 'female', 123456)
print(stu.get_id_card('123'))
stu.set_id_card('012345678945632101')
print(stu.get_id_card('123'))
解决方法: 用@property将一个方法伪装成一个属性,
用@id_card【名字等于@property下函数名】.setter来修改属性
用@
class Student:
def __init__(self, name, age, gender, id_card):
self.name = name
self.age = age
self.gender = gender
self.__id_card = id_card
@property #getter 用于访问私有属性的值,也可以设置普通属性【初始化不能定义,如BMI计算】
def id_card(self):
pwd =input('pwd:').strip()
if pwd == '123':
return self.__id_card
print('密码错误,不能访问')
@id_card.setter #用来设置私有属性的值,也可以设置普通属性
def id_card(self, new_id):
if isinstance(new_id, str) and len(new_id) == 18:
self.__id_card = new_id
else:
print('身份证号码,必须是之符串,且长度必须为18!')
@id_card.deleter #用来删除私有属性的值,也可以删除普通属性
def id_card(self):
print('can not delete salray!')
del self.__dict__['_Student__id_card']
stu = Student('fanny', 18, 'female', 123456)
# stu.id_card
stu.id_card = '123569874123654780'
# print(stu.id_card)
# print(stu.__dict__) #{'name': 'fanny', 'age': 18, 'gender': 'female', '_Student__id_card': '123569874123654780'}
del stu.id_card
stu.id_card # 'Student' object has no attribute '_Student__id_card'
Property的另一种使用场景【设置普通属性】:计算属性BMI
class Persoon:
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
# self.BMI = weight / (height ** 2)
@property
def BMI(self):
return self.weight / (self.height ** 2)
@BMI.setter
def BMI(self, nwe_BMI):
print('BMI 不支持自定义...')
p1 = Persoon('egon', 1.7, 80)
print(p1.BMI)
p1.BMI =10
二、多态:
多太指的是同一种事物的多种形态
多个不同类型对象,可以响应同一方法,并且产生不同结果, 【统一】以不变应万变,提高了灵活性、扩展性
多态的好处: 可以在不用考虑对象具体类型前提下而直接使用对象,不需要关心各个类型对象的具体实现细节
在多态背景下,父类不是让子类继续它的功能,而是建立了一种规范
python 崇尚鸭子类型 指的是子类并不需要从属于哪个类,只要多个类之间约定好都定义同名的函数
统一后就用一个接口中,使用方便
例:一个用来管理动物的方法,只要你传入是一个有同名方法的对象,就可以使用
class Cat:
def dark(self):
print('喵喵')
def run(self):
print('轻轻地跑')
def sleep(self):
print('趴着跑')
class Pig:
def dark(self):
print('哼哼')
def run(self):
print('四条腿跑')
def sleep(self):
print('侧躺着睡')
class Manger_anmal:
def dark(self, obj):
obj.dark()
def run(self, obj):
obj.run()
def sleep(self, obj):
obj.sleep()
cat = Cat()
pig1 = Pig()
obj = Manger_anmal()
obj.dark(cat)
obj.dark(pig1)
三、常用的内置函数: __str__ __del__ __setattr__ __getattr__
3.1 __str__ # 在对象被打印时自动触发,可以用来定义对象被打印时的输出信息
# 注意:必须返回一个字符串类型的值
class People:
def __init__(self, name, age ):
self.name = name
self.age = age
def __str__(self):
print('run....')
return '<name: %s age:%s>' %(self.name, self.age )
obj1 = People('egon', 18)
print(obj1) #print(obj1.__str__())
3.2 __del__ : # 该函数不是用来删除对象的,【在对象删除前做一些清理操作】是用来关闭清理回收对象以外其他相关资源,比如打开文件后,通知操作系统关闭文件爱你
触发时机:
1.程序运行结束 解释器退出时,自动删除所有数据 【例1】
2.在对象被删除时【del obj】触发该方法,【例2】
-----------------------------------
例1:
class Foo:
def __del__(self):
print('run ...')
obj = Foo()
print('===========')
输出:
===========
run ...
--------------------------------------------
例2:
class Foo:
def __del__(self):
print('run ...')
obj = Foo()
del obj
print('===========')
输出:
run ...
===========
3.3 __setattr__ 添加/修改属性会触发它的执行
3.4 __getattr__ 只有在使用点调用属性且属性不存在的时候才会触发
class Myclass:
name='egon'
def __getattr__(self, item):
print('这是__getattr__')
def __setattr__(self, key, value):
print('这是__setattr__')
s1= Myclass()
print(s1.name)
# print(s1.age)
# s1.name='fanny
四、反射 通过字符串来操作属性
hasatter(obj,'dir') 判断obj是否有某个属性 返回一个bool值 【obj是任何对象】
#print('dir' in MY_CMD.__dict__)
getattr(cmd, 'dir',None) 返回一个属性,可能是数据属性【变量名】,也可能是一个方法属性【函数对象】
返回一个dir绑定方法,加()可运行 #<bound method MY_CMD.dir of <__main__.MY_CMD object at 0x000001B73AAE9470>>
setattr(cmd, 'x', 222 ) 新增或修改某个属性,有'x'就修改该属性的值,没有就新增一个属性及值
print(cmd.x)
setattr(cmd, 'y', 666 )
print(cmd.y)
delattr(cmd, 'name') 删除某个属性,只能删除该对象自己私有的属性,如果该属性是类的,就不能删,可以将cmd变成
delattr(cmd, 'x') #AttributeError: x
delattr(MY_CMD, 'x')
import os
class MY_CMD:
x = 111
def __init__(self, name):
self.name = name
def dir(self):
os.system("dir")
def ipconfig(self):
os.system("ipconfig")
cmd = MY_CMD('fanny')
print(hasattr(MY_CMD, 'dir')) #print('dir' in MY_CMD.__dict__)
print('dir' in MY_CMD.__dict__)
print(hasattr(cmd, 'name'))
print('name' in cmd.__dict__)
print('name' in MY_CMD.__dict__) #$False
getattr(cmd, 'dir',None)()
print(getattr(cmd, 'name', None))
setattr(cmd, 'x', 222 )
print(cmd.x)
setattr(cmd, 'y', 666 )
print(cmd.y)
delattr(cmd, 'name')
delattr(cmd, 'x') #AttributeError: x
delattr(MY_CMD, 'x')
五、动态导入模块
直接写import为静态导入,建立在提前已经知道有这个模块
动态导入 指的是 在需要的任何时候,通过指定字符串类型的包名称来导入需要的模块
该方式常用在框架中,因为框架设计者不可能提前预知后续需要的模块和类
import importlib
mk = importlib.import_module(m_name)