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

    封装

    层次一:类就是麻袋,这本身就是一种封装

               从字面意义上去理解封装,装就是搬家的时候,把书、电脑、杯子什么的都往袋子里装,封就是把这个袋子封起来,封起来之后,从外面就什么都看不到了,就是所谓的‘隐藏’。在面向对象里面,这个袋子就是类或者对象,下面举个例子:

    class People:
        star = 'earth'
    
        def __init__(self,id,name,age):
            self.id = id
            self.name = name
            self.age = age
    
        def get_id(self):
            print('我是私有方法,我的id是%s'%self.id)
    

          定义好这个类之后,其他人要调用你的类,就是这样的:

    from (文件名) import People
    
    p1 = People('2131415151','pengfy',18)
    p1.get_id()
    
    >>> 我是私有方法,我的id是2131415151

            我可以使用你这个类,但是并不知道你具体是怎么实现的,这就属于一种封装,但封装并不只是隐藏。

    层次二:类中定义私有的,只有在类的内部才能使用,外部无法使用

            首先,什么是内部,什么是外部?在类里面用就是内部,调用这个类再使用,就是外部喽。那怎么定义私有呢?说这个之前,就要说要Python的两个约定:

    1.单下划线开头

            类里面单下划线开头的属性,一般都是定义的私有,继续看上面的例子:

    # -*- coding: utf-8 -*-
    class People:
        star = 'earth'
        _star = 'earth1'
    
        def __init__(self,id,name,age):
            self.id = id
            self.name = name
            self.age = age
    
        def get_id(self):
            print('我是私有方法,我的id是%s'%self.id)
    
    p1 = People('2131415151','pengfy',18)
    print(p1.star)
    print(p1._star)

    运行一下,结果是:

    >>>earth
    >>>earth1

           说好的私有呢?怎么都能打印,但是,请记住,这只是一个约定,你非要调用,还是可以的,哈哈。

    2.双下划线开头

    # -*- coding: utf-8 -*-
    class People:
        star = 'earth'
        _star = 'earth1'
        __star = 'earth2'
    
        def __init__(self,id,name,age):
            self.id = id
            self.name = name
            self.age = age
    
        def get_id(self):
            print('我是私有方法,我的id是%s'%self.id)
    
    p1 = People('2131415151','pengfy',18)
    print(p1.star)
    print(p1._star)
    print(p1.__star)

           看一下运行结果:

    >>> earth
    >>> earth1
    AttributeError: 'People' object has no attribute '__star'

          报错了,好像可以啊,双下划线外部不能访问嘛,你可以试试内部能不能访问,肯定是可以的,这就完了私有属性啦?有点早,不信你试试把p1.__star替换成p1._People__star,运行结果如下:

    class People:
        star = 'earth'
        _star = 'earth1'
        __star = 'earth2'
    
        def __init__(self,id,name,age):
            self.id = id
            self.name = name
            self.age = age
    
        def get_id(self):
            print('我是私有方法,我的id是%s'%self.id)
    
    p1 = People('2131415151','pengfy',18)
    print(p1.star)
    print(p1._star)
    print(p1._People__star)
    
    >>>earth
    >>>earth1
    >>>earth2

           写成这样就出来啦?为什么要这么写?自己找答案!打开类的属性字典找找就好了:

    print(People.__dict__)
    
    >>>{'__module__': '__main__', 'star': 'earth', '_star': 'earth1', '_People__star': 'earth2', '__init__': <function People.__init__ at 0x00000206A3BF26A8>, 'get_id': <function People.get_id at 0x00000206A3BF2730>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

           找到没有,Python中类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__star,会变形为_People__star,这种变形需要注意的是:

    1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._C__D,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。

    2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形,看下边例子就知道了

    class People:
        star = 'earth'
        _star = 'earth1'
        __star = 'earth2'
    
        def __init__(self,id,name,age):
            self.id = id
            self.name = name
            self.age = age
    
        def get_id(self):
            print('我是私有方法,我的id是%s'%self.id)
    
    p1 = People('2131415151','pengfy',18)
    print(p1.__dict__)
    p1.__star = 'earth666'
    print(p1.__dict__)
    
    >>>{'id': '2131415151', 'name': 'pengfy', 'age': 18}
    >>>{'id': '2131415151', 'name': 'pengfy', 'age': 18, '__star': 'earth666'}

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

    #正常情况
    >>> class A:
    ...     def fa(self):
    ...         print('from A')
    ...     def test(self):
    ...         self.fa()
    ... 
    >>> class B(A):
    ...     def fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from B
     
    
    #把fa定义成私有的,即__fa
    >>> class A:
    ...     def __fa(self): #在定义时就变形为_A__fa
    ...         print('from A')
    ...     def test(self):
    ...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
    ... 
    >>> class B(A):
    ...     def __fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from A

    层次三:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个借口供外部使用(这才是真正意义的封装,不要为了封装而封装)

            上面已经知道了,双下划线开头,外部无法直接访问,但是内部可以,这样的话,你可以定义一个函数直接return出去,就OK了:

    class People:
        star = 'earth'
        _star = 'earth1'
        __star = 'earth2'
    
        def __init__(self,id,name,age):
            self.id = id
            self.name = name
            self.age = age
    
        def get_id(self):
            print('我是私有方法,我的id是%s'%self.id)
    
        def tell_star(self):
            return self.__star
    
    p1 = People('2131415151','pengfy',18)
    print(p1.tell_star())
    
    >>> earth2

            这样做外部要拿这个属性时,也可以拿到,tell_star这个函数就是这个类的接口函数,当然上面的例子讲的都是数据属性,函数属性呢?肯定也是一样可以:

    #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    #隔离了复杂度,同时也提升了安全性
    
    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()

           打印一下结果:

    插卡
    用户认证
    输入取款金额
    打印账单
    取款

         了解:  

    python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的

    其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,作为外部的你,一意孤行也是可以用的,只不过显得稍微傻逼一点点

    python要想与其他编程语言一样,严格控制属性的访问权限,只能借助内置方法如__getattr__,这里就不做讲解

        小结:

         封装看起来简单,但不要去为了封装而封装,要提前识别哪些数据要使用,那些数据需要隐藏,不能滥用双下划线,否则的话,项目做到后期,你的类里面会出现很多接口函数,这也就是麻袋上面的洞,那么你的类看起来也是烂七八糟,后面会持续补充其他封装的内容,封装不只是这么简单。

  • 相关阅读:
    关于多工序生产中损耗的计算
    ERP相关术语
    linux下如何挂载U盘
    linux boot下的文件
    MPLS简述
    BGP
    linux添加新磁盘
    列表的方法
    python之列表
    python之模块初识-os模块
  • 原文地址:https://www.cnblogs.com/pengfy/p/10610193.html
Copyright © 2011-2022 走看看