zoukankan      html  css  js  c++  java
  • 面向对象之封装

    一、封装--隐藏属性

      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

      

        

      

  • 相关阅读:
    十三、Sklearn核密度估计
    十二、Sklearn高斯混合模型
    十一、Sklearn K-means聚类
    十、Sklearn流形学习
    九、Sklearn主成分分析
    八、Sklearn决策树与随机森林
    七、Sklearn支持向量机
    六、Sklearn线性回归
    五、Sklearn朴素贝叶斯分类
    $.each()和$().each()
  • 原文地址:https://www.cnblogs.com/schut/p/8636230.html
Copyright © 2011-2022 走看看