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())
  • 对于类实现者可以修改封装内的东西而不影响外部调用者的代码。而外部使用者只知道一个接口(函数),只要接口名不变、参数不变,使用者的代码就永远无需改变。
posted @ 2019-01-25 22:26  爬呀爬Xjm  阅读(107)  评论(0编辑  收藏  举报