面向对象之封装

一、封装--隐藏属性

  1、从封装的本意上理解,封装就是将一些东西放到一个盒子或袋子里包起来,然后密封起来。

  如果按照这种理解的话,封装就等于“隐藏”,但是这是不准确的,很片面。

  2、那么我们先来看下怎样隐藏属性吧!

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

  3、示例:

#!/usr/bin/env python3
#-*- coding:utf-8 -*-

class A:
    __x = '未知'   #  '_A__x': '未知'(__x是隐藏属性的一种格式,定义时便被改写成 '_A__x')
    def __init__(self,name):
        self.__name = name   # 改写成了self._A__name = 'cc'
    def __func(self):       # 改写成了 _A__func
        print('from __func')
    def foo(self):
        self.__func()  # 在类内部里可以直接使用的原因:被改写成了self._A__func()
        print('from foo')
print(A.__dict__)
a = A('cc')
print(a.__dict__)  # {'_A__name': 'cc'}
# print(a.__name) # 无法找到__name  在类外部无法直接使用:obj.__AttrName
a.foo()
'''输出:
from __func
from foo
'''

  4、加双下划线隐藏属性的特点:

   <1> 在类外部无法直接使用:obj.__AttrName -->无法获取

   <2> 在类内部可以直接使用:self.__AttrName -->直接使用

   <3> 子类无法覆盖父类双下划线开头的属性。如下所示: 

class Func:
    def __func2(self):  # 在内部被改写成 _Func__func2
        print('from func2')
class Bar(Func):
    def __func2(self):  # 在内部被改写成 _Bar__func2
        print('from Bar')
# foo = Bar()
# foo.func2() # from Bar

  5、需要注意的几点:

    <1> 这种机制并没有真正意义上限制我们从外部直接访问属性,当我们知道了类名和属性名就可以

    拼出名字了:_类名__属性,然后就可以访问了。

    <2> 变形的过程只在类的定义时发生一次,在定义后的赋值操作(添加或更改属性),不会变形。

    <3> 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有。

# 验证注意的问题1:
print(a._A__name) # cc

# 验证注意的问题2: A.__y = 666 print(A.__dict__) # '_A__x': '未知', '__y': 666 可见并未实现隐藏 a.__age = 18 print(a.__dict__) # {'_A__name': 'cc', '__age': 18} # 也未实现隐藏 print(a.__age) # 18 # 可直接访问 # print(a.__name) # 报错

# # 验证注意的问题3: class Func: def __func2(self): # _Func__func2 print('from Func.__func2') def bar(self): print('from Func.bar') self.__func2() # 定义时被改写成 self._Func__func2,所以此时只在自身里调用,避免被子类覆盖 class Foo(Func): def __func2(self): # _Foo__func2 print('from Foo.__func2') f = Foo() f.bar() # from Bar ''' 输出: from Func.bar from Func.__func2 '''

  

二、封装的意义

  1、封装数据属性:明确区分内外,控制外部对隐藏属性的操作行为。

  示例:

#!/usr/bin/env python3
#-*- coding:utf-8 -*-

class People:
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
    def tell_info(self):
        print('姓名:【%s】 年龄:【%s】'%(self.__name,self.__age))
    def set_info(self,name,age):
        if not isinstance(name,str):
            print('名字必须为字符串')
            return
        if not isinstance(age,int):
            print('年龄必须为整数')
            return
        self.__name = name
        self.__age = age

p1 = People('cc',21)
print(p1.__dict__) # {'_People__name': 'cc', '_People__age': 21}
p1.tell_info()    # 姓名:【cc】 年龄:【21】
p1.set_info(688,21)  # 名字必须为字符串
p1.set_info('sc','21')  # 年龄必须为整数

  2、封装方法:隔离复杂度

    示例1:

class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('输密码')
    def __input(self):
        print('输入金额')
    def __take_money(self):
        print('取出钱')
    def __print_bill(self):
        print('打印账单')
    def take_money(self): # 用户不用关心具体的流程,只需要调用取钱接口即可。
        self.__card()
        self.__auth()
        self.__input()
        self.__take_money()
        self.__print_bill()
p1 = ATM()
p1.take_money()
'''用户仅需一步就可以实现所有操作
插卡
输密码
输入金额
取出钱
打印账单

'''

    示例2:如傻瓜相机

class Autofocus_camera:
    def __get_sence(self):
        print('取景')
    def __aiming(self):
        print('调光')
    def __focus(self):
        print('聚焦')
    def __shutter(self):
        print('快门')
    def take_photo(self):
        self.__get_sence()
        self.__aiming()
        self.__focus()
        self.__shutter()
person = Autofocus_camera()
person.take_photo()
'''# 隔离复杂度,简化用户的操作
取景
调光
聚焦
快门

'''

  注意:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

三、封装与扩展性

  封装明确区分内外,使得类的设计者可以修改封装内的东西而不影响外部调用者的代码;

而外部使用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码则无需改变。

这极大的提升了程序的扩展性。

  示例:

# 类的设计者
class
House: def __init__(self,owner,position,length,width): self.owner = owner self.position = position self.price_m = price_m self.__length = length self.__width = width def tell_sum(self): # 对外提供的接口,隐藏了内部的实现细节,此时是计算面积 return self.__length*self.__width

# 使用者 p1
= House('cc','hgys',18,20) print(p1.tell_sum()) # 360 #使用者调用接口 tell_sum,获取面积结果

# 类的设计者,现在打算扩展功能,而要求使用者不需要改变操作方法
class House:
  def __init__(self,ower,position,length,width,price_m):
    self.owner = owner
    self.position = position
    self.length = length
    self.width = width
    self.price_m = price_m

  def tell_sum(self): # 对外提供接口,隐藏内容,此时求房价
    return self.length*self.width*self.price_m

# 扩展后使用者的使用方法
p2 = House('cc','hgnf',10,15,10000)
print(p2.tell_sum()) # 1500000 # 调用方法不变,同样调用tell_sum接口,降低使用者的使用难度

用户无需关心内部具体程序,只需调用teLl_sum()方法即可获取信息
当内部修改时,用户调用方式仍然不变,极大地提高了程序的可扩展性

四、property(特性)装饰器的使用

  1、定义: @property 属性装饰器,如果需要调用某一经过计算的属性(使用函数)时,但我们想以数据属性的方式调用,就要用到@property。

  2、示例:

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
'''
    BMI指数(即身体质量指数,简称体质指数又称体重,英文为Body Mass Index,简称BMI),
是用体重公斤数除以身高米数平方得出的数字,是目前国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。
它的定义如下:
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86

成人的BMI数值(参考):
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
'''

class People:
    def __init__(self,name,age,height,weight):
        self.name = name
        self.age = age
        self.height = height
        self.weight = weight
    @property   # 本意属性,特性
    def bmi(self):
        return self.weight/(self.height**2)

p1 = People('cc',21,1.70,63)
# print(p1.bmi()) # 21.79930795847751  # 调用方法不合适,容易引起误解,bmi更像是一个数据属性,而非函数
print(p1.bmi)    # 21.79930795847751   # 这样更符合使用习惯,虽然本质上仍然调用的是方法,但这个方法已经经过装饰,和调用数据属性方法别无二致
p1.weight = 60
print(p1.bmi) # 20.761245674740486

  3、property特性装饰器的扩展

class Per:
    def __init__(self,name):
        self.__name = name
    @property   # 查询
    def name(self):
        print('from property.name')
        return self.__name
    @name.setter  # 设置
    def name(self,s):
        if not isinstance(s,str):
            print('名字必须为字符串')
            return
        self.__name = s
    @name.deleter  # 删除
    def name(self):
        print('你无权删除')

p = Per('ht')
# print(p.name()) # ht
print(p.name)  # ht   查询属性
p.name = 'cc'    # 增加了@name.setter才能更改属性
p.age = 18
print(p.name,p.age) # cc  18
del p.age  # 同样增加了@name.deleter才能删除属性
print(p.__dict__) # {'_Per__name': 'cc'}
View Code

  

    

  

原文地址:https://www.cnblogs.com/schut/p/8636230.html