• 008---封装


    封装

    引子

    从封装本身的字面意思理解就是把一大堆事物用麻袋封装起来,达到隐藏的效果,其实这种理解是片面的。

    • 广义上的封装:

      • 把共有的属性和方法封装到类中:类属性和绑定方法
      • 把独有的属性封装到对象中:__init__()
    • 狭义上的封装

      • 私有化,只能在类的内部访问
        • 单下划线(_name):保护变量,只有类对象和子类对象本身能访问到这些变量。不能用于from...import ...
        • 双下划线(__name):私有属性,只有类对象自己能访问,连子类对象也不能访问到这个数据。
      class A:
          # 会变形为:_A__x,并不是真正意义外部不能访问,仅仅只是一种语法上的变形。
          __x = 1
          _y = 2
      
          def __init__(self, name):
              self.__name = name
      
          # 变形为_A__foo
          def __foo(self):
              return 'foo已运行'
      
          def bar(self):
              print(self.__foo())
              return 'bar已运行'
      
      
      # 类访问私有成员
      print('验证变形之后的__x:', A.__dict__.get('_A__x'))
      print('验证保护属性_y是否变形:', A.__dict__.get('_y'))
      print('验证变形之后的__foo:', A.__dict__.get('_A__foo'))
      
      print('外部类访问私有属性:', A._A__x)
      print('外部类执行私有方法:', A._A__foo(1))
      print('外部类访问保护属性:', A._y)
      
      # 对象访问私有成员
      a = A('alex')
      print('外部对象访问私有属性:', a._A__name)
      print('外部对象访问私有属性:', a._A__x)
      print('外部对象访问保护属性:', a._y)
      print('外部对象执行私有方法:', a._A__foo())
      print('外部对象间接访问私有方法:', a.bar())
      
      '''
      这种变形的特点:
      1、外部无法直接obj.__AttrName
      2、在类的内部obj.__AttrName可调用,定义阶段
      3、子类中无法覆盖__开头的属性,也就是说子类不可以访问。
      
      保护属性虽然没有变形。但也请把它视为私有属性
      '''
      
    • 在继承中,父类如果不想子类覆盖自己的方法。可以将其定义为私有的。

    class C:
        def __foo(self):  # _C.__foo
            print('C.foo')
    
        def bar(self):
            print('A.bar')
            # 如果需求是调用父类的foo,就可以使用封装
            self.__foo()
    
    
    class D(C):
        def __foo(self):  # _D.__foo
            print('D.foo')
    
    
    d = D()
    d.bar()
    

    封装的意义

    • 明确的区分内外,控制外部对内部私有成员的操作行为
    • 隔离复杂度
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__ = "ziya"
    # Date: 2018-08-26
    
    # 一、封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为
    
    class People:
        def __init__(self, name, age):
            self.__name = name
            self.__age = age
    
        def tell_info(self):
            print('Name:%s> Age:%s' % (self.__name, self.__age))
    
        def set_info(self, name, age):
            if not isinstance(name, str):
                return
            if not isinstance(age, int):
                return
            self.__name = name
            self.__age = age
    
        def __func1(self):
            print(111)
    
    
    p = People('engo', 18)
    
    # 提供接口供外部访问
    p.tell_info()
    
    # 表面上修改了name的值,其实不是,只是增加了一个name的属性。因为原来的变形了。。。
    p.name = 1
    print(p.__dict__)
    
    
    # 二、封装方法的目的,隔离复杂度
    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.__print_bill()
            self.__take_money()
    
    
    a = ATM()
    
    a.withdraw()
    '''
    插卡
    用户认证
    输入取款金额
    打印账单
    取款
    '''
    
    '''
    取款是功能,而这个功能有很多小功能组成,
    对使用者来说,只要知道取款这个功能即可。其余功能隐藏起来。很明显隔离了复杂度,同时也提升了安全性
    '''
    
    

    特性:property

    • 什么是特性property
      property是一种特殊的属性,访问它时会执行一端功能(函数),然后返回值。调用时,像调用属性一样。
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    # __author__ = "ziya"
    # Date: 2018-08-27
    
    '''
    property是一种特殊的属性,访问它时会执行一段功能,然后返回值。调用时,直接像调属性一样
    '''
    
    '''
    例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
    
    成人的BMI数值:
    
    过轻:低于18.5
    
    正常:18.5-23.9
    
    过重:24-27
    
    肥胖:28-32
    
    非常肥胖, 高于32
    
    体质指数(BMI)=体重(kg)÷身高^2(m)
    
    EX:70kg÷(1.75×1.75)=22.86
    '''
    
    
    class People:
        def __init__(self, name, weight, height):
            self.name = name
            self.weight = weight
            self.height = height
    
            # 表面效果一样,其实不一样,如果实例化对象后,修改了对象的属性。bmi初始化的时候已经定型了,后面不会重新计算
            self.bmi1 = self.weight / (self.height ** 2)
    
        @property
        def bmi(self):
            return self.weight / (self.height ** 2)
    
    
    p = People('jw', 60, 1.70)
    
    print('通过property动态得到bim属性:', p.bmi)
    print('通过初始化方法计算得到bim属性:', p.bmi1)
    p.weight = 120
    print('修改后', p.bmi)
    print('修改后', p.bmi1)
    
    
    class Person:
        def __init__(self, name):
            self.__name = name
    
        @property
        def name(self):
            return self.__name
    
        @name.setter
        def name(self, val):
            if not isinstance(val, str):
                print('名字必须时字符串类型')
                return
            self.__name = val
    
        @name.deleter
        def name(self):
            print('不允许删除')
    
    
    p = Person('江子牙')
    
    print(p.name)
    # 尝试设置name的值,提示必须为字符串类型
    p.name = 1
    print(p.name)
    # 尝试删除name的值,提示不允许删除
    del p.name  # 触发deleter  不允许删除
    
    
    • 为什么要使用特性:遵循了统一访问的原则。

    封装与拓展性

    class Room:
        def __init__(self,name,owner,width,length,height):
            self.name = name
            self.owner = owner
    
            self.__width = width
            self.__length = length
            self.__height = height
        def area(self):
            # 求体积
            # return self.__width * self.__length * self.__height
            # 求面积
            return self.__width * self.__length
    
    r = Room('卫生间','alex',10,10,8)
    # 调用接口获取房子体积,而不用管内部怎么实现。
    print(r.area())
    
    • 对于类实现者可以修改封装内的东西而不影响外部调用者的代码。而外部使用者只知道一个接口(函数),只要接口名不变、参数不变,使用者的代码就永远无需改变。
  • 相关阅读:
    Kubernetes k8s 安装 配置
    golang 笔记
    基于docker容器部署Prometheus服务 普罗米修斯 监控
    hbase hadoop
    es安装日志云elk
    通达OA工作流主要表的数据结构
    利用jPrintArea做页面打印
    linux后端服务启动与停止
    CAD命令
    Stream流Collectors.toMap用法
  • 原文地址:https://www.cnblogs.com/xjmlove/p/10322076.html
Copyright © 2020-2023  润新知