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

    接着上面的一篇继续往下:

    如何隐藏

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

    class A:
        __x = 1  # _A__x = 1
    
        def __init__(self, name):
            self.__name = name  # self._A__name = name
    
        def __fun1(self):
            print("run fun1.......")
    
        def fun2(self):
            self.__fun1()  # self._A__fun1()
            print("run fun2.......")
    
    a = A("Astro")
    # print(a.x)  # 报错 无法访问到 x   AttributeError: 'A' object has no attribute 'x'
    print(A.__dict__)
    # {'__module__': '__main__', '_A__x': 1, ........}
    # _A__x
    print(a._A__x)  # 1
    
    a.fun2()
    # run fun1.......
    # run fun2.......
    
    

    其实这仅仅这是一种变形操作
    类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
    A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

    这种变形的特点:

    • 在类外部无法直接obj.__AttrName
    • 在类内部是可以直接使用:obj.__AttrName
    • 在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

    这种变形需要注意的问题是:

    1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

    2、变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

    a.__g = "gd"
    print(a.__dict__)
    # {'_A__name': 'Astro', '__g': 'gd'}
    

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

    # 正常情况
    class A:
        def foo(self):
            print('A.foo')
    
        def bar(self):
            print('A.bar')
            self.foo() #b.foo()
    
    class B(A):
        def foo(self):
            print('B.foo')
    
    b=B()
    b.bar()
    # A.bar
    # B.foo
    
    

    # 私有化后
    class A:
        def __foo(self): # #在定义时就变形为 _A__foo
            print('A.foo')
    
        def bar(self):
            print('A.bar')
            self.__foo() #self._A__foo()   只会与自己所在的类为准,即调用_A__fa
    
    class B(A):
        def __foo(self): #_B__foo
            print('B.foo')
    
    b=B()
    b.bar()
    # A.bar
    # A.foo
    


    封装的意义

    1.封装数据

    # 封装数据属性:明确的区分内外
    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):  # 判断 传进来的 name 是不是 str 类型
                print("name must be str type")
                return
            if not isinstance(age, int):
                print("age must be int type")
                return
            self.__name = name
            self.__age = age
    
    
    
    p = People("Astro", 15)
    p.tell_info()  # name:Astro  age:15
    # 通过调用 tell_info 这个接口,来间接访问 name 和 age
    
    p.set_info("茶水博士", 60)
    p.tell_info()  # name:茶水博士  age:60
    
    p.set_info(1111,222)  # name must be str type
    

    2.封装方法, 隔离复杂度

    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()
    # 插卡
    # 用户认证
    # 输入取款金额
    # 打印账单
    # 取款
    
    #----------------------------
    #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    #隔离了复杂度,同时也提升了安全性
    
    • 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!
    • 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了


    3.特性(Property)

    什么是特性 property

    property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

    class People:
        def __init__(self, name, weight, height):
            self.name = name
            self.weight = weight
            self.height = height
        @property
        def bmi(self):
            return self.weight / (self.height ** 2)
    
    p = People("astro", 55, 1.73)
    # print(p.bmi())  # 18.376825152861773
    # 我们想得到 bmi 这个值,需要调用 p.bmi() ,如果在 def bmi(self) 上面加 @property ,即可直接调用了
    
    print(p.bmi)  # 18.376825152861773
    
    p.height = 1.8
    print(p.bmi)  # 16.975308641975307
    
    p.bmi = 20  # 报错 AttributeError: can't set attribute
    

    为什么要用property

    将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则


    ps:面向对象的封装有三种方式:
    【public】
    这种其实就是不封装,是对外公开的
    【protected】
    这种封装方式对外不公开,但对朋友(friend)或者子类公开
    【private】
    这种封装对谁都不公开

    python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现

    class People:
        def __init__(self, name):
            self.__name = name
    
        @property
        def name(self):
            return self.__name  # obj.name访问的是self.__NAME(这也是真实值的存放位置)
    
        @name.setter
        def name(self, val):
            if not isinstance(val, str):  # 在设定值之前进行类型检查
                print("姓名必须为字符串")
                return
            self.__name = val
        @name.deleter
        def name(self):
            print("deleter....")
            print("不允许删除....")
    
    p = People('astro')
    print(p.name)  # astro  此时可以直接访问 name 属性了
    
    p.name = 'Astro'
    print(p.name)  # Astro name 别改变了,执行了 @name.setter 下的方法
    
    p.name = 111  # 姓名必须为字符串
    
    del p.name  # deleter....   不允许删除....
    
  • 相关阅读:
    delegate和event的区别 (zz)
    delegate和event的区别 (zz)
    delegate和event的区别 (zz)
    delegate和event的区别 (zz)
    以太坊网络服务分析
    以太坊:P2P网络数据处理流程
    以太坊:P2P网络数据交互
    以太坊虚拟机的基本介绍
    Solidity概述及基本代码展示
    Solidity编译器和简单调试
  • 原文地址:https://www.cnblogs.com/friday69/p/9385352.html
Copyright © 2011-2022 走看看