封装,封装的原理,Property ,setter ,deleter

2020-01-15 20:23:31 浏览数 (2)

1,封装

代码语言:javascript复制
## 什么是封装 what 

对外隐藏内部的属性,以及实现细节,并给外部提供使用的接口
学习封装的目的:就是为了能够限制外界对内部数据的方法
注意 :封装有隐藏的意思,但不是单纯的隐藏

python 中属性的权限分为两种:  
  1,分开的
    没有任何限制,谁都可以访问
  2,私有的
    只有当前类本身能够访问
  默认为公共的

##如何封装 how 

为什么要封装:

  1,提高安全性  (封装属性)
       
  2,隔离复杂度  (封装方法)

    
一个类中分为两种数据,属性和方法:
代码语言:javascript复制
封装属性:

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 show_id_card(self):
        # 可以在这里添加额外的任何逻辑代码 来限制外部的访问
        
        #在类的内部 可以访问
        print(self.__id_card)
代码语言:javascript复制
对私有属性的访问以及修改:

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):
        #身份证必须是字符串类型
        #长度必须是18位
        if isinstance(new_id,str) and len(new_id) == 18:
            self.__id_card = new_id
        else:
            raise  Exception('身份证号码必须是字符串,且长度为18')


s = Student('rose',10,'man','111111111111111111')
print(s.get_id_card('123'))     #111111111111111111
#设置属性
s.set_id_card('222222222222222222')
print(s.get_id_card('123'))       #222222222222222222
代码语言:javascript复制
什么样的方法应该被封装起来 :

  当这个对象存在一个机密性的属性 例如 人的身份证 银行卡密码等等,这样属性不应该被外界直接 访问当 ,那就封装起来。

​    一个为内部提供支持的方法,不应该让外界直接访问,那就封装起来 ,如下例中的 user_auth等...


class ATM:
    #提供给外部访问的一个接口
    def withdraw(self):
        #输入账号和密码
        self.__user_auth()
        #输入取款金额
        self.__input_money()
        #保存记录
        self.__save_record()

    #封装内部的实现细节
    def __user_auth(self):
        print('输入账号和密码')

    def __input_money(self):
        print('输入取款金额')

    def __save_record(self):
        print('记录流水')

a = ATM()
a.withdraw()

  #输入账号和密码   #输入取款金额   #记录流水

2,封装的原理:

代码语言:javascript复制
'''
python 是通过变形的方式来实现的封装
如何变形 : 在名称带有双下划线开头的变量名字前添加_类名  如:_Person_id_card
当然通过变形后的名字可以直接访问被隐藏的属性 ,但通常不这样做  如: p._Person__id_card
变形仅在类的定义阶段发生一次,后续再添加在带有双下划线的任何属性都不会变形,就是普通函数
'''''

class Person:
    def __init__(self,name,age,id_card):
        self.name = name
        self.age = age
        self.__id_card = id_card    #_Person__id_card

    def get_id_card(self):
        return self.__id_card



p = Person('rose',20,'12321323123123')
print(p.__dict__)       #{'name': 'rose', 'age': 20, '_Person__id_card': '12321323123123'}
print(p.name)

p.__id_card = '123'     #重新开辟了一块空间存储
print(p.__dict__)       #{'name': 'rose', 'age': 20, '_Person__id_card': '12321323123123', '__id_card': '123'}

print(p._Person__id_card)   #直接强制访问  12321323123123

p.__gender = 'man'
print(p.__dict__)       #{'name': 'rose', 'age': 20, '_Person__id_card': '12321323123123', '__id_card': '123', '__gender': 'man'}

3,Property ,setter ,deleter

代码语言:javascript复制
Property 作用 : 将一个方法伪装成普通属性 

为什么用 property  : 希望将访问私有属性和普通属性的方式变得一致

与property相关的两个装饰器: 

  setter : 用点语法 给属性赋值时触发   

  deleter: 用点语法删除属性时触发 



class Teacher:
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.__salary = salary

    @property         #getter  用于访问私有属性的值,也可以访问普通属性
    def salary(self):
        return self.__salary

    @salary.setter      #用于设置私有属性的值,也可以设置普通属性
    def salary(self,new_salary):
        self.__salary = new_salary

    @salary.deleter     #用于设置私有属性的,也可以删除普通属性
    def salary(self):
        print(self.__dict__)        #{'name': 'owen', 'age': 29, '_Teacher__salary': 88888}
        del self.__dict__['_Teacher__salary']



t = Teacher('owen',29,88888)
# del t.salary

print(t.salary)     #88888

t.salary = 100000
print(t.salary)     #100000

'''
总结:
    被封装的属性在访问时,需要调用方法,而普通属性直接点就OK,这样一来对于对象的使用者而言
    必须知道要访问的属性,是私有还是公开,然后在调用对应的方法.用起来就会比较麻烦
    此时,我们的目的就是让访问私有属性和访问普通属性的方式一样通过点来调用
    
  property装饰器:就是用来将一个方法伪装成属性,可以直接通过点来调用
'''
代码语言:javascript复制
'''
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('owen',1.78,60)
print(p.BMI)        #18.93700290367378

p.weight = 90
print(p.BMI)        #28.40550435551067



#被封装的内容(私有的) 不可以被继承
class A:
    __name = 'rose'     #变形后  _A__name

    def __say_hi(self):
        print('A say hi')

class B(A):
    def test(self):
        # print(super().__name)   #变形后 _B__name 在哪个类就以哪个类变形
        print(A._A__name)         #通过super()不能使用父类的私有属性,可以通过类名直接访问
b = B()
b.test()

4,多态

代码语言:javascript复制
"""
多态 :
多态不是一个具体的技术  或代码
指的是 多个不同类型对象 可以响应同一个方法 ,产生不同结果

某种事物具备多个不同形态
例如 水:   气态  固态  液态
     动物: 人  猫  猪
     汽车人:  汽车 飞机  人型

OOP中 标准解释: 多个不同类型对象 可以响应同一个方法  并且产生不同结果

多态的带来的好处:
    只需要学习基类中的使用方法即可, 不需要关心具体的哪一个类 以及实现的,以不变应万变 ,提高了灵活性
    提高扩展性


如何实现多态:
    鸭子类型 就是典型的多态 多种不同类型 使用方法一样

"""

# import abc
# class Animal(metaclass=abc.ABCMeta):
# 
#     @abc.abstractmethod
#     def bark(self):
#         pass
# 
#     @abc.abstractmethod
#     def run(self):
#         pass
# 
#     @abc.abstractmethod
#     def sleep(self):
#         pass



class Person():

    def bark(self):
        print("Hello!")

    def run(self):
        print("两条腿跑!")

    def sleep(self):
        print("躺着睡!")


class Cat():

    def bark(self):
        print("喵喵喵")

    def run(self):
        print("四条腿跑!")

    def sleep(self):
        print("趴着睡!")

class Pig():
    def bark(self):
        print("哼哼哼!")

    def run(self):
        print("四条腿跑!")

    def sleep(self):
        print("侧躺着睡!")


person1 = Person()
cat1 = Cat()
pig1 = Pig()

person1.sleep()
cat1.sleep()
pig1.sleep()

# 如果没有多态  需要分别学习 person  cat pig 的不同使用方法 这对于使用者而言太麻烦了


# 一个用来管理动物的方法   只要你传入是一个动物 我就按照动物的标准来使用 完全不用考虑你具体是什么类型

def management_animal(animal):
    print("==================正在溜%s=============" % animal.__class__.__name__)
    animal.bark()
    animal.run()
    animal.sleep()

# 来了一堆动物
person1 = Person()
cat1 = Cat()
pig1 = Pig()

management_animal(person1)
management_animal(cat1)
management_animal(pig1)



class Dog():
    def bark(self):
        print("汪汪汪...")

    def run(self):
        print("四条腿跑!")

    def sleep(self):
        print("趴着睡!")

dog1 = Dog()
management_animal(dog1)


class Fish():
    def bark(self):
        print("超声波...")

    def run(self):
        print("游来游去.....")

    def sleep(self):
        print("睁眼睡觉....")

f = Fish()
management_animal(f)



#  无处不在的多态

# def MY_LEN(obj):
#     return obj.__len__()
#
# print(MY_LEN("abcdefg"))
# print(MY_LEN([1,2,3,4]))
# print(MY_LEN({"name":123}))

print(len("1231212121"))

5,常用的内置函数 ,__str__ , __del__

代码语言:javascript复制
__str__ :

'''
类中的__str__:
    该方法在object中有定义,默认行为,返回对象类型以及地址  
    在将对象转为字符串时执行
    注意:返回值必须为字符串类型
    子类可以覆盖该方法来完成,对打印内容的自定义
'''''


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)

'''#在打印前都会先将要打印的内容转为字符串,通过调用__str__函数'''
print(p)    #my name is rose.age is 20

str(p)      #str run
代码语言:javascript复制
'''
__del__:

    当对象被删除前会自动调用该方法
    声明的时候会删除对象?
        1,程序运行结束,解释器退出,将自动删除所有数据
        2,手动调用del 时也会删除对象
        
    注意: 该函数不是用来删除对象的
    
使用场景:
    当你的对象在创建时,开启了不属于解释器的资源 ,例如: 打开了一个文件
    必须保证当前对象被删除时,同时关闭额外的资源,如:文件
    
也称之为析构函数(一个个拆分,解析)  构造 的反义词
    构造:指的是从无到有
    析构:指的是有到无
    简单的说就对象所有数据全部删除
     
总结:__del__该函数 用于在对象删除前做的一些清理操作(或提醒)

'''''

#假设要求每一个person对象都要绑定一个文件
class Person:
    def __init__(self,name,path):
        self.name = name
        self.file = open(path,encoding='utf-8')

    #读取数据的方法
    def read_data(self):
        return self.file.read()

    def __del__(self):
        print('del run1')       #当程序执行完后才会执行这个
        self.file.close()

p = Person('owen','封装方法.py')
print(p.read_data())

6,反射

代码语言:javascript复制
英文中叫反省 (自省)   

面向对象中的反省 指的是,一个对象必须具备,发现自身属性,以及修改自身属性的能力;   

一个对象在设计初期,可能考虑不够周全后期需要删除或修改已经存在的属性, 和增加属性 

## 反射就是通过字符串来操作对象属性

涉及到的方法:

hasattr   判断是否存在某个属性

getattr    获取某个属性的值

setattr    新增或修改某个属性 

delattr   删除某个属性 


案例1:

import os

class Student:
    def __init__(self,name):
        self.name = name
    def www(self):
        print('123')

stu = Student('rose')

#反射的四个方法
print(hasattr(stu,'name'))  #True
print(getattr(stu,'name'))  #rose

setattr(stu,'name','123')
print(getattr(stu,'name'))  #123

delattr(stu,'name')
print(hasattr(stu,'name'))  #False


print(hasattr(stu,'www'))    #True


案例2:

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)       #绑定方法: <bound method MY_CMD.dir of <__main__.MY_CMD object at 0x0000021FFF8765F8>>
        method()
    else:
        print('sorry this method is not exists....!')

7,动态导入模块:

代码语言:javascript复制
'''
直接写import 称之为静态导入,建立在一个基础时,提前已经知道有这个模块

动态导入:指的是在需要的任何时候,通过指定字符类型,包名称来导入需要的模块

import importlib
mk = importlib.import_module(m_name)
mk 即导入成功的模块
'''''


#动态导入模块.py

import importlib

m_name = input('请输入要导入的模块名称:').strip()
mk = importlib.import_module(m_name)
print(mk)       #<module '计算属性' from 'D:\Python_Project\ClassRoom\day26\练习\计算属性.py'>

from day26.练习.build_house import conf
print(conf)



#build_house/conf.py

decoration = [
    'build_house.my_decoration.Table',
    'build_house.my_decoration.Light',
    'build_house.my_decoration.Bed',
    'other_decoration.Sofa'
]


#build_house/core.py

import importlib
from day26.练习.build_house import conf

def building():
    print('毛坯房建造完成.....')
    #读取用户提供配置信息,来加载用户所需要的一系列装饰品
    for cls_info in conf.decoration:
        # print(cls_info)
        #切割类名和模块路径
        m_name,cls_name = cls_info.rsplit('.',1)
        # print(m_name,cls_name)    #build_house.my_decoration Table

        #导入模块
        mk = importlib.import_module(m_name)
        print(mk)
        #从模块中获取类
        if not hasattr(mk,cls_name):
            continue
        cls = getattr(mk,cls_name)
        obj = cls()
        print(obj)


building()


#build_house/my_decoration.py

class Table:
    pass

class Light:
    pass

class Bed:
    pass

0 人点赞