zoukankan      html  css  js  c++  java
  • 面向对象编程(封装、封装的意义、封装与扩展性、@property)

    1.封装之如何实现属性的隐藏

    封装: __x=1 # 把数据属性隐藏 (如何实现隐藏) 类定义阶段 __开头发生了变形 __x --> _A__x
    特点:
      1.在类外部无法直接:obj.__AttrName
      2.在类内部是可以直接使用:obj.__AttrName # 为什么会这样?python 如何实现的 !类定义阶段已经变形 #__x --> _A__x #self._A_foo()
      3.子类无法覆盖父类__开头的属性 它两根本不是一个名字 #_Foo__func #_Bar__func
    总结:
    这种变形需要注意的问题:
      1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N eg:print(A._A__x)
      2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形 eg: b.__age=18 {'_B__name': 'alice', '__age': 18}
      3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 eg: def __foo(self): #_A__foo

    # class A:
    #     __x=1  # 把数据属性隐藏    _A__x
    #
    #     def __init__(self,name):
    #         self.__name=name    # _A__name
    #
    #     def __foo(self):  # 把函数属性隐藏   def _A__foo(self)
    #         print('run foo')
    #
    #     def bar(self):
    #         self.__foo()  #self._A_foo()  定义阶段已经变了
    #         print('from bar')
    
    
    # print(A.__dict__)
    # {'__module__': '__main__', '_A__x': 1, '__init__': <function A.__init__ at 0x0000017AC92B0B70>, '_A__foo': <function A.__foo at 0x0000017AC92B0BF8>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
    # 类定义阶段  __开头发生了变形  __x --> _A__x
    
    # print(A.__x)
    # print(A._A__x)
    # print(A.__foo)
    # print(A._A__foo)
    # a=A('alice')
    # print(a.__dict__)
    # {'_A__name': 'alice'}
    # print(a.__name)
    # print(a._A__name)
    # a.bar()
    
    # ---------------------------------------------
    # class Foo:
    #     def __func(self):  #_Foo__func
    #         print('from foo')
    #
    # class Bar(Foo):
    #     def __func(self):  #_Bar__func
    #         print('from bar')
    
    # b=Bar()
    # b.func()  # 子类把父类 重名的给覆盖掉了
    
    # ---------------------------------------------
    # class B:
    #     __x=1
    #
    #     def __init__(self,name):
    #         self.__name=name
    
    
    # # print(B._B__x)   # 一般不要这么做!py不让你这么干了
    # B.__y=2   # 类定义完之后不会发生变形
    # # print(B.__dict__)
    # # {'__module__': '__main__', '_B__x': 1, '__init__': <function B.__init__ at 0x00000180405A0B70>, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None, '__y': 2}
    #
    # b=B('alice')
    # print(b.__dict__)  # {'_B__name': 'alice'}
    #
    # b.__age=18
    # print(b.__dict__) #{'_B__name': 'alice', '__age': 18}
    
    # ---------------------------------------------
    # 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()
    
    # ---------------------------------------------
    class A:
        def __foo(self):  #_A__foo
            print('A.foo')
    
        def bar(self):
            print('A.bar')
            self.__foo() #self._A__foo()  # 只调自己类的方法 定义时就已经确定好的!
    
    class B(A):
        def __foo(self): #_B__foo
            print('B.foo')
    
    b=B()
    b.bar()

    2.封装的意义


    封装数据属性目的: (封装不是单纯意义上的隐藏)
    明确的区分内外,控制外部对隐藏的属性的操作行为

    封装方法属性目的:
    隔离复杂度 # a=ATM() a.withdraw(

    在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:

    # 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):
    #             print('名字必须是字符串类型')
    #             return
    #         if not isinstance(age,int):
    #             print('年龄必须是数字类型')
    #             return
    #         self.__name=name
    #         self.__age=age
    #
    # p=People('alice','18')
    # p.tell_info()
    
    # p.set_info('alex',38)
    # p.tell_info()
    
    # p.set_info('alex','38')
    # p.tell_info()
    
    # -------------------------------------------------------
    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.封装与扩展性

    面向对象:可扩展性高面向对象三大特性:继承 多态 封装封装的扩展性: def tell_area(self): # 对使用者来说 不用改变 方式 开发者在类里面修改

    class Room:
        def __init__(self,name,owner,weight,length,height):
            self.name=name
            self.owner=owner
    
            self.__weight=weight
            self.__length=length
            self.__height=height
    
        def tell_area(self):  # 对使用者来说 不用改变 方式
            return self.__weight * self.__length * self.__height
    
    
    r=Room('卫生间','alex',10,10,10)
    print(r.tell_area())

    4.property的使用

    Python内置的@property装饰器就是负责把一个方法变成属性调用的:

    @property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

    注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

    还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:

    小结

    @property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

    property :
    @property
    def bmi(self): 必须有个返回值
    print(p.bmi) 可以使 函数属性 伪装成 数据属性 bmi 是名词

    p.bmi=23 # 不能赋值 can't set attribute bmi 实质是个方法

    总结:通过计算得来的方法 可以通过@property 伪装成数据属性
    @property 查看 必须有返回值
    @name.setter 修改
    @name.deleter 删除

    练习

    请利用@property给一个Screen对象加上widthheight属性,以及一个只读属性resolution

    class Screen(object):
        @property
        def resolution(self):
            return self.width * self.height
    
    # 测试:
    s = Screen()
    s.width = 1024
    s.height = 768
    print('resolution =', s.resolution)
    if s.resolution == 786432:
        print('测试通过!')
    else:
        print('测试失败!')
    
    #输出
    resolution = 786432
    测试通过!
  • 相关阅读:
    mysql的undo log和redo log
    MySQL表的定期分析检查优化
    MySQL 数据库设计总结
    Innodb引擎下mysql自身配置优化
    linux的top命令参数详解
    InnoDB的关键特性-插入缓存,两次写,自适应hash索引
    第一次接私活亲身经历
    码农与技术控
    软件公司与非软件公司区别(纯个人看法)
    SQL Server表 & 存储过程 创建日期查询
  • 原文地址:https://www.cnblogs.com/mjiu/p/8969115.html
Copyright © 2011-2022 走看看