day26 封装、多态、内置函数、反射、动态导入

今日内容

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)

在上例中:

动物类、狗类、猫类、人类等产生的不同对象可以同时调用barksleeprun等方法,但是这些方法的执行结果是不相同的,这种就称之为多态

从上述例子可以看出多态的优点:

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()

如果调用了abc模块,那么子类继承后在进行实例化时,如果不将类中含有abstractmethod的方法全部重新定义,就会报错,其中没有abstractmethod的方法不受限制

 

虽然在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__

_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 即导入成功的模块

该方式常用在框架中 因为框架设计者不可能提前预知后续需要的模块和类

 

猜你喜欢

转载自www.cnblogs.com/lice-blog/p/10897039.html