今日内容
1、封装
什么是封装?
但是,在python中没有完全意义的封装,封装只是在内部进行了变形,使其使用原变量名不能进行访问
同时,有些父类的方法,如果不想让子类进行覆盖就将父类的方法设置为私有属性
封装的方法:
在python中如果需要对某个属性或方法进行封装是只需要在变量名前设置为__
开头的即可,此时在在外界进行访问时就不能访问这个属性或方法
封装的原理:
我们在进行封装时,实际上是对封装的属性或方法进行了变形,当类进行定义时,如果属性或者方法是以__
开头的就会对这个属性进行变形,变形方式为:
在__
前面加上_类名
变为_类名__属性名或方法名
注意:变形的操作只在定义类时执行一次
在python中,并不会限制用户访问私有属性及方法,私有的属性及方法还是可以被外界访问的,只要自己将方法前面添加_类名
就可以访问私有的属性及方法
其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket.socket,sys.home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的
class Person: def __init__(self,name,age): self.name = name self.__age = age p = Person("lee",20) print(p.name) # print(p.__age) # AttributeError: 'Person' object has no attribute '__age' p.__age = 18 print(p.__age) # 18 print(p.__dict__) # {'name': 'lee', '_Person__age': 20, '__age': 18}
封装的目的
封装不是单纯意义的隐藏,封装会明确的区分内外,在类的内部可以直接使用但是在外部就不可以直接使用
封装的目的:
1、提高安全性:提高安全性主要是针对属性而言的,封装后属性的值不能直接被外界访问及修改,可以保证数据的安全性
2、隔离复杂度:隔离复杂度是针对于方法而言的,我们将类中具体的逻辑私有化,再将其封装到一个方法中供外界访问,这样用户只需要进行一次调用就可以直接访问,对用户而言降低了复杂度
class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__take_money() self.__print_bill() atm = ATM() atm.withdraw() # 插卡 # 用户认证 # 输入取款金额 # 取款 # 打印账单
property装饰器
property是一个属性装饰器,可以将方法伪装成属性
实际上,property是一个类,在使用是就是调用这个类产生一个实例化对象,这个对象经常使用的方法有三种:`
getter setter deleter`
其中:
getter在调用property时就会自动调用
setter在设置方法时调用
deleter在删除方法时调用
property使用场景一:供私有属性使用
class Person: def __init__(self,name,age): self.name = name self.__age = age def get_age(self): print(self.__age) def set_age(self,new_age): self.__age = new_age p =Person("lee",18) p.name p.get_age()
通过上例中的方法可以进行私有属性的访问及修改,但是这样我们在访问name属性时可以使用 对象.name进行访问,但是访问age属性是就需要使用 对象.get_age() 来进行访问,同样都是属性但是访问方法却不相同,为了使用户对属性的访问方式相同,此时就需要使用 propety 装饰器
class Person: def __init__(self, name, age): self.name = name self.__age = age @property def age(self): return (self.__age) @age.setter def age(self, new_age): self.__age = new_age @age.deleter def age(self): del self.__dict__["_Person__age"] p = Person("lee", 18) print(p.name) print(p.age) p.age = 20 del p.age print(p.age) # 会报错
property使用场景二:计算属性
在初始化函数中,有时并不是所有的属性都是可以直接就能得出的,有时需要进行计算,但是如果在初始化函数中进行计算,那么在这个量只能在创建对象时初始化一次,不能进行修改,但是,如果将这个属性设置为方法,就可以进行重复的变化,但是这个值归根结底就是个属性,在访问时想以属性的方式进行访问,此时就需要使用property装饰器
class Person: 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,new_BMI): print("BMI 不支持自定义.....") p = Person("egon",1.7,80) # p.weight = 60 # print(p.BMI)
2、多态
什么是多态?
多态就是一种事物的多种形态
在oop
中多态就是不同类型的对象可以响应同一种方法,得到不同的响应结果
# 定义一个动物的类 class Animal: def bark(self): pass def sleep(self): pass def run(self): pass # 定义一个猫类 class Cat(Animal): def bark(self): print("喵喵喵") def sleep(self): print("躺着睡") def run(self): print("四条腿跑") # 定义一个人类 class Person(Animal): def bark(self): print("hi") def run(self): print("四条腿跑") def sleep(self): print("躺着睡") # 定义一个狗类 class Dog(Animal): def bark(self): print("汪汪汪") def sleep(self): print("侧躺着睡") def run(self): print('四条腿跑') # 定义一个管理员类用来管理动物 class ManagementAnimal: def Manage(self,animal): animal.bark() animal.sleep() animal.run() dog = Dog() p = Person() cat = Cat() m = ManagementAnimal() m.Manage(p) m.Manage(dog) m.Manage(cat)
在上例中:
动物类、狗类、猫类、人类等产生的不同对象可以同时调用bark
、sleep
、run
等方法,但是这些方法的执行结果是不相同的,这种就称之为多态
从上述例子可以看出多态的优点:
1、可以是对象的调用更加的灵活,不论对象如何改变,调用对象方法的方式是相同的
2、可以提高程序的扩展性,在创建一个新的动物类是不需要更改Manager
的代码就能进行使用
在python中如果想要强制性的规定动物类必须具有这些个属性,可以导入abc
模块
import abc class Animal(metaclass=abc.ABCmeta) @abc.abstractmethod def bark(self): pass def sleep(self): pass class Cat(Animal): def sleep(self): print("正在叫!") cat = Cat()
虽然在Python中可以使用abc
模块进行限制,但是python编程更多的是建议,而不是约束
所以python中更多的是建议用户使用鸭子类型
3、内置函数
__str__
_str_
是系统内置的函数,当我们需要对类转化成字符串时就会执行
类中的__str__
该方法在object中有定义 默认行为 返回对象类型以及地址 <__main__.Person object at 0x0000016F450C7390>
在将对象转为字符串时执行
注意:返回值必须为字符串类型
子类可以覆盖该方法来完成 对打印内容的自定义
class Person: def __init__(self,name,age): self.name = name self.age = age # 将对象转换为字符串时执行 def __str__(self): print("str run") return "my name is %s , age is %s" % (self.name,self.age) p = Person("rose",20) # print(p) #在打印前都会现将要打印的内容转为字符串 通过调用__str__函数 str(p)
_del_
是系统内置的函数,当我们需要删除对象时就会在删除的前一步自动执行
当对象被删除前会自动调用 该方法
声明时候会删除对象?
1.程序运行结束 解释器退出 将自动删除所有数据
2.手动调用del 时也会删除对象
注意:该函数不是用来删除对象的
使用场景
当你的对象在创建时,开启了不属于解释器的资源 例如打开了一个文件
必须保证当对象被删除时 同时关闭额外的资源 如文件
也称之为析构函数 构造 的反义词
构造 指的是从无到有
析构 值从有到无
简单的说就对象所有数据全部删除
总结:__del__该函数 用于 在对象删除前做一些清理操作
# 假设要求每一个person对象都要绑定一个文件 class Person: def __init__(self,name,path,mode="rt",encoding="utf-8"): self.name = name self.file = open(path,mode,encoding=encoding) # 读取数据的方法 def read_data(self): return self.file.read() def __del__(self): print("del run!") self.file.close() # p = Person("jack") # a = 10 # f = open("test.txt") # print(f.read()) # f.close() p2 = Person("rose","本周内容") print(p2.read_data())
4、反射
英文中叫反省 (自省)
面向对象中的反省 指的是,一个对象必须具备,发现自身属性,以及修改自身属性的能力;
一个对象在设计初期,可能考虑不够周全后期需要删除或修改已经存在的属性, 和增加属性
反射就是通过字符串来操作对象属性
涉及到的方法: hasattr 判断是否存在某个属性 getattr 获取某个属性的值 setattr 新增或修改某个属性 delattr 删除某个属性 案例: class MY_CMD: def dir(self): os.system("dir") def ipconfig(self): os.system("ipconfig") cmd = MY_CMD() while True: name = input("请输入要执行的功能:") if hasattr(cmd,name): method = getattr(cmd,name) print(method) method() else: print("sorry this method is not exists....!")
5、动态导入模块
直接写import 称之为静态导入 建立在一个基础上:提前已经知道有这个模块
动态导入 指的是 在需要的任何时候 通过指定字符串类型的包名称来导入需要的模块
import importlib mk = importlib.import_module(m_name) mk 即导入成功的模块
该方式常用在框架中 因为框架设计者不可能提前预知后续需要的模块和类