d22 封装 property装饰器 接口 抽象类 鸭子类型

封装

封装指的是隐藏对象的属性和实现细节,仅对外公开接口,控制程序中属性的访问权限;

python中的权限分为两种

1.公开 外界可以直接访问和修改

2.私有 外界不能直接访问和修改,在当前类中可以直接修改和访问

封装的目的

1.提高安全性

封装属性

2.隔离复杂度

封装方法

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

class Person:
    def __init__(self, id_number, name, age):
        self.__id_number = id_number
        self.name = name
        self.age = age
        
    def show_id(self):
        print(self.__id_number)

p = Person("1111111111111", "jack", 29)

p.__id_number = "222"
print(p.__id_number)

p.show_id()
View Code

封装属性

class PC:
    def __init__(self,price,kind,color):
        self.price = price
        self.kind = kind
        self.color = color

    def open(self):
        print("接通电源")
        self.__check_device()
        print("载入内核")
        print("初始化内核")
        self.__start_services()
        print("启动GUI")
        self.__login()


    def __check_device(self):
        print("硬件检测1")
        print("硬件检测2")
        print("硬件检测3")
        print("硬件检测4")

    def __start_services(self):
        print("启动服务1")
        print("启动服务2")
        print("启动服务3")
        print("启动服务4")

    def  __login(self):
        print("login....")
        print("login....")
        print("login....")

pc1 = PC(20000,"草莓","红色")
# pc1.open()

pc1.login()
View Code

被封装的内容的特点:

1.外界不能直接访问

2.内部依然可以使用

如何访问被封装的属性

属性虽然被封装了,但是还是需要使用的,在外界如何访问

通过定义方法类完成对私有属性的修改和访问

"""
这是一个下载器类,需要提供一个缓存大小这样的属性
缓存大小不能超过内存限制

"""
class Downloader:
    def __init__(self,filename,url,buffer_size):
        self.filename = filename
        self.url = url
        self.__buffer_size= buffer_size

    def start_download(self):
        if self.__buffer_size <= 1024*1024:
            print("开始下载....")
            print("当前缓冲器大小",self.__buffer_size)
        else:
            print("内存炸了! ")


    def set_buffer_size(self,size):
        #可以在方法中添加额外的逻辑
        if not type(size) == int:
            print("大哥 缓冲区大小必须是整型")
        else:
            print("缓冲区大小修改成功!")
            self.__buffer_size = size

    def get_buffer_size(self):
        return self.__buffer_size

d = Downloader("葫芦娃","http://www.baicu.com",1024*1024)


# 通过函数取修改内部封装的属性
d.set_buffer_size(1024*512)

# 通过函数访问内部封装的属性
print(d.get_buffer_size())
print(d.filename)

d.start_download()
View Code

对私有属性的访问以及修改

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

封装的原理

python是通过 变形的方式来实现封装:
就是在加载类的时候,把\_\_替换成了 \_类名\__
 在名称带有双下划线开头的变量名字前添加_类名  如_Person__id_card
当然通过变形后的名字可以直接访问被隐藏的属性  但通过不应该这么做
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,
然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。
2.变形仅在类的定义阶段发生一次 后续再添加的带有双下划线的任何属性都不会变形  就是普通属性

 例子

class A:
    def __init__(self,key):
        self.__key = key

    @property
    def key(self):
        return self.__key

    @key.deleter
    def key(self):
        del self.__key


a = A("123")
# print(a.key)
# print(a.__dict__)

print(a._A__key)

a.__name = 1

print(a.__dict__)


# print("__key".replace("__","_A__"))
View Code
封装:

对外部隐藏内部的实现细节,并提供访问的接口 

好处:

  1.提高安全性 

  2.隔离复杂度  

语法:将要封装的属性或方法名称前加上双下划线

访问被隐藏的属性:

  提供用于访问和修改的方法  

使用property装饰器可以将一个方法伪装成普通属性,使得属性之间调用方法一致

封装的实现原理 ,替换变量名称 
小结:

roperty装饰器

将一个方法伪装成普通属性,将访问私有属性和普通属性的方式变得一致

通过方法来修改或访问属性,本身没什么问题,但是这给对象的使用者带来了麻烦.

使用必须知道哪些是普通属性,哪些是私有属性,需要使用不同的方式来调用他们

property装饰就是为了使得调用方式一致

1.property   该装器用在获取属性的方法上 
2.@key.setter  该装器用在修改属性的方法上 ,用点语法 给属性赋值时触发
3.@key.deleter 该装器用在删除属性的方法上 ,用点语法删除属性时触发

  

注意:key是被property装饰的方法的名称 也就是属性的名称 
内部会创建一个对象 变量名称就是函数名称  
所以在使用setter和deleter时 必须保证使用对象的名称取调用方法 
所以是 key.setter

 

class A:
    def __init__(self,name,key):
        self.__name = name
        self.__key = key

    @property
    def key(self):
        return self.__key

    @key.setter
    def key(self,new_key):
        if new_key <= 100:
            self.__key = new_key
        else:
            print("key 必须小于等于100")

    @key.deleter
    def key(self):
        print("不允许删除该属性")
        del self.__key

    # @property
    # def name(self):
    #     return self.__name
    #
    # @name.setter
    # def name(self,new_name):
    #     self.__name = new_name

a = A("jack",123)
# print(a.name)
# # print(a.get_key())
#
print(a.key)
a.key = 321
print(a.key)
del a.key
print(a.key)

a.name = "xx"
print(a.name)
View Code

 

property 可以用来实现计算属性

计算属性指的是:属性的值,不能直接获得,必须通过计算才能获取

, 一个属性 它的值不是固定的 而是通过计算动态产生的 结果

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

class Square:

    def __init__(self,width):
        self.width = width
        # self.area = self.width * self.width

    @property
    def area(self):
        return self.width * self.width

s = Square(10)

print(s.area)

s.width = 20

print(s.area)

s.width = 2
print(s.area)

  

练习: 定义一个类叫做person
包含三个属性 身高 体重 BMI
BMI的值需要通过计算得来 公式 体重 / 身高的平方

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)
print(p.BMI)
p.BMI = 10
View Code

接口

接口是一堆功能的集合,是一套协议规范

具体表现形式: 有一堆函数   但是只明确了函数的名称 没有明确函数具体实现

class USB:

    def open(self):
        pass

    def close(self):
        pass

    def work(self):
        pass
View Code

使用接口可以提高程序的扩展性

只要对象按照接口规定方法来实现,使用者就可以无差别使用所有对象
接口主要是方便了对象的使用者,降低使用者的 学习难度,只要学习一套使用方法,就可以以不变应万变

抽象类

抽象:
  指的是不清楚,不具体,看不懂的
抽象方法
  指的是 没有函数体的方法   用@abc.abstractmethod 装饰器
  如果类中具备抽象方法 那么这个类就称之为抽象类 
抽象类的特点:

   不能直接实例化  必须有子类覆盖了所有抽象方法后才能实例化子类
接口的区别:

  接口是指只有方法声明而没有实现体 ,  接口中所有方法都是抽象的 

  

abc 不是随意取的 而是单词的缩写
abstract class
翻译为抽象类
抽象类的定义 :
类中包含 没有函数体的方法

 

import abc

class Test(metaclass= abc.ABCMeta):

    @abc.abstractmethod
    def say_hi(self):
        pass

class QW(Test):
    def say_hi(self):
        print('是 QW ')

q=QW()
q.say_hi()
View Code

例子

import abc

class AClass(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def run(self):
        pass
    @abc.abstractmethod
    def run1(self):
        pass


class B(AClass):

    def run(self):
        print("runrunrurn...")

b = B()
View Code

 

如果接口的子类没有实现接口中的方法,那是没有任何意义的

抽象类之所以出现的意义:通过抽象类来强行限制子类必须覆盖所有的抽象方法

鸭子类型

鸭子类型
说如果一个对象叫声像鸭子,走路像鸭子,长得像鸭子,那它就是鸭子 

是python 推荐的方式,python不喜欢强行限制你 
利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法
class PC():

    def content_device(self,usb_device):
        usb_device.open()
        usb_device.work()
        usb_device.close()

class Mouse:
# 实现接口规定的所有功能
    def open(self):
        print('mouse open')
    def work(self):
        print('mouse work')
    def close(self):
        print('mouse close')

mouse=Mouse()
pc=PC()

pc.content_device(mouse)

class KeyBoard:
    def open(self):
        print('KeyBoard open')

    def work(self):
        print('KeyBoard work')

    def close(self):
        print('KeyBoard close')

key=KeyBoard()

pc.content_device(key)
View Code

  

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/komorebi/p/11251076.html