zoukankan      html  css  js  c++  java
  • Python面向对象三要素-封装(Encapsulation)

                Python面向对象三要素-封装(Encapsulation)

                                          作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.封装概述

      将数据和操作组织到类中,即属性和方法 
      
      将数据隐藏起来,给使用者提供操作(方法)。使用者通过操作就可以获取或者修改数据。getter和setter。 
      
      通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来,例如保护成员或私有成员。

    二.类属性的访问控制 

    1>.抛出问题 

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Person:
     8     def __init__(self,name,age=18):
     9         self.name = name
    10         self.age = age
    11 
    12     def growup(self,i=1):
    13          if i > 0 and i < 150:  #控制逻辑
    14              self.age += i
    15 
    16 
    17 p1 = Person("jason")
    18 print(p1.age)
    19 
    20 p1.growup(20)           #在我们控制逻辑的范围内
    21 print(p1.age)
    22 
    23 p1.age = 9999           #直接修改对象的属性,超过了范围,并绕过了咱们控制逻辑,是不是很操蛋?Python提供了私有属性可以解决这个问题。
    24 print(p1.age)
    25 
    26 
    27 
    28 #以上代码输出结果如下:
    29 18
    30 38
    31 9999

    2>.私有(Private)属性

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Person:
     8     def __init__(self,name,age=18):
     9         self.name = name
    10         """
    11             私有变量的本质: 类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为"_类名__变量名"的名称,所以用原来的名字访问不到了。
    12         """
    13         self.__age = age            #使用双下划线开头的属性名,就是私有属性
    14 
    15     def growup(self,i=1):
    16          if i > 0 and i < 150:      #控制逻辑
    17              self.__age += i
    18 
    19     def getage(self):               #我们只对外提供访问"__age"的方法
    20         return self.__age
    21 
    22 p1 = Person("jason")
    23 print(p1.getage())
    24 print(p1.__dict__)
    25 
    26 p1.growup(120)
    27 print(p1.getage())
    28 
    29 print(Person.__dict__)
    30 print(p1.__dict__)
    31 
    32 #p1.__age == 9999                   #我们发现现在无法访问到"__age"这个属性啦,会抛出"AttributeError"异常
    33 p1._Person__age = 9999              #既然我们知道了私有变量的新名称,就可以直接从外部访问到,并修改它。因此尽管是私有属性我们依旧是可以对其进行更改,但建议大家不要去修改,因为这样就违背了私有变量但属性啦,但你如果一定要改的话你得知道去哪改哟。
    34 print(p1.getage())
    35 print(p1.__dict__)
    36 
    37 
    38 
    39 #以上代码输出结果如下:
    40 18
    41 {'name': 'jason', '_Person__age': 18}
    42 138
    43 {'__module__': '__main__', '__init__': <function Person.__init__ at 0x10075f950>, 'growup': <function Person.growup at 0x10075fae8>, 'getage': <function Person.getage at 0x10075fb70>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
    44 {'name': 'jason', '_Person__age': 138}
    45 9999
    46 {'name': 'jason', '_Person__age': 9999}

    3>.保护(protected)属性

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 class Person:
     8     def __init__(self,name,age=18):
     9         self.name = name
    10         """
    11             在变量名前使用一个下划线,称为保护变量。
    12             可以看出,这个_age属性根本就没有改变名称,和普通的属性一样,解释器不做任何特殊处理。 
    13             这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用。
    14         """
    15         self._age = age
    16 
    17 
    18 p1 = Person("jason")
    19 
    20 print(p1._age)
    21 print(p1.__dict__)              #
    22 
    23 
    24 
    25 #以上代码输出结果如下:
    26 18
    27 {'name': 'jason', '_age': 18}

    4>.私有方法

     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 
     7 """
     8 私有方法的本质
     9     单下划线的方法只是开发者之间的约定,解释器不做任何改变。
    10     双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同, 即"_类名__方法名" 。
    11     方法变量都在类的 __dict__ 中可以找到。
    12 """
    13 class Person:
    14     def __init__(self, name, age=18):
    15         self.name = name
    16         self._age = age
    17 
    18     """
    19         参照保护变量、私有变量,使用单下划线、双下划线命名方法。
    20     """
    21     def _getname(self):
    22         return self.name
    23 
    24     def __getage(self):
    25         return self._age
    26 
    27 
    28 jason = Person('Jason')
    29 print(jason._getname())  # 没改名
    30 #print(jason.__getage()) # 无此属性
    31 print(jason.__dict__)
    32 print(jason.__class__.__dict__)
    33 print(jason._Person__getage()) # 改名了
    34 
    35 
    36 
    37 #以上代码执行结果如下:
    38 Jason
    39 {'name': 'Jason', '_age': 18}
    40 {'__module__': '__main__', '__init__': <function Person.__init__ at 0x10215f950>, '_getname': <function Person._getname at 0x10215fae8>, '_Person__getage': <function Person.__getage at 0x10215fb70>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
    41 18

    5>.私有成员的总结

      在Python中使用 _单下划线 或者 __ 双下划线来标识一个成员被保护或者被私有化隐藏起来。 
     
      但是,不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员。Python中没有绝对的安全的保护成员或者私有成员。 
    
      因此,前导的下划线只是一种警告或者提醒,请遵守这个约定。除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们。

    三.补丁

      可以通过修改或者替换类的成员。使用者调用的方式没有改变,但是,类提供的功能可能已经改变了。
    
      猴子补丁(Monkey Patch):
        在运行时,对属性、方法、函数等进行动态替换。
        其目的往往是为了通过替换、修改来增强、扩展原有代码的能力。
        黑魔法,慎用。
     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 class Person:
     7     def get_score(self):
     8         #模拟下面的字典是从数据库拿的某个学生成绩(基本是及格的不多呀)。
     9         ret = {"English":37,"Chinese":66,"History":52}
    10         return ret
    test2.py
    1 #!/usr/bin/env python
    2 #_*_conding:utf-8_*_
    3 #@author :yinzhengjie
    4 #blog:http://www.cnblogs.com/yinzhengjie
    5 
    6 
    7 def get_score(self):
    8     return dict(name=self.__class__.__name__,English=98, Chinese=96, History=95)
    test3.py
     1 #!/usr/bin/env python
     2 #_*_conding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:http://www.cnblogs.com/yinzhengjie
     5 
     6 from test2 import Person
     7 from test3 import get_score
     8 
     9 def monkey_patch_for_Person():
    10     Person.get_score = get_score
    11 
    12 monkey_patch_for_Person()       #打补丁操作
    13 
    14 if __name__ == '__main__':
    15     print(Person().get_score())
    16 
    17 
    18 
    19 #以上代码输出结果如下:
    20 {'name': 'Person', 'English': 98, 'Chinese': 96, 'History': 95}

    四.属性装饰器

    一般好的设计是:
      把实例的某些属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性。

    1>.自定义getter和setter方法

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:https://www.cnblogs.com/yinzhengjie
     5 #EMAIL:y1053419035@qq.com
     6 
     7 class Person:
     8     def __init__(self,name,age=18):
     9         self.name = name
    10         self.__age = age
    11     
    12     """
    13         我们自定义age和set_age方法操作属性,的确可以实现私有变量的管理。那有没有简单的方式呢?
    14     """
    15     def age(self):
    16         return self.__age
    17 
    18     def set_age(self,age):
    19         self.__age = age
    20 
    21 
    22 jason = Person("Jason")
    23 print(jason.age())
    24 
    25 jason.set_age(20)
    26 print(jason.age())
    27 
    28 
    29 
    30 #以上代码输出结果如下:
    31 18
    32 20

    2>.property装饰器

     1 # !/usr/bin/env python
     2 # _*_coding:utf-8_*_
     3 # @author :yinzhengjie
     4 # blog:https://www.cnblogs.com/yinzhengjie
     5 # EMAIL:y1053419035@qq.com
     6 
     7 class Person:
     8     def __init__(self, name, age=18):
     9         self.name = name
    10         self.__age = age
    11 
    12     """
    13         特别注意:
    14             使用property装饰器的时候这三个方法同名。
    15             property装饰器必须在前,setter,deleter装饰器在后。
    16             property装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏效果。
    17 
    18         property装饰器:
    19             后面跟的函数名是以后的属性名。它就是getter。这个必须有,有了它至少是只读属性。
    20 
    21         setter装饰器:
    22             与属性名同名,且接收2个参数,第一个self,第二个是将要赋值的值。有了它,属性可写。
    23 
    24         deleter装饰器:
    25             可以控制是否删除属性,很少用,咱们了解即可。
    26     """
    27 
    28     @property
    29     def age(self):
    30         return self.__age
    31 
    32     @age.setter
    33     def age(self, age):
    34         self.__age = age
    35 
    36     @age.deleter
    37     def age(self):
    38         del self.__age
    39         print("del")
    40 
    41 
    42 jason = Person("Jason")
    43 print(jason.age)
    44 
    45 jason.age += 10
    46 print(jason.age)
    47 
    48 del jason.age
    49 
    50 # 以上代码输出结果如下:
    51 18
    52 28
    53 del
     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:https://www.cnblogs.com/yinzhengjie
     5 #EMAIL:y1053419035@qq.com
     6 
     7 class Person:
     8     def __init__(self,name,age=18):
     9         self.name = name
    10         self.__age = age
    11 
    12     def getage(self):
    13         return self.__age
    14 
    15     def setage(self,age):
    16         self.__age =age
    17 
    18     def delage(self):
    19         del self.__age
    20         print("del")
    21 
    22     age = property(getage,setage,delage,"age property")
    23 
    24 jason = Person("Jason")
    25 print(jason.age)
    26 
    27 jason.age = 20
    28 print(jason.age)
    29 
    30 del jason.age
    31 
    32 #以上代码输出结果如下:
    33 18
    34 20
    35 del
    property的另外一种写法(第二种写法)
     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:https://www.cnblogs.com/yinzhengjie
     5 #EMAIL:y1053419035@qq.com
     6 
     7 class Person:
     8     def __init__(self,name,age=18):
     9         self.name = name
    10         self.__age = age
    11 
    12     age = property(lambda self:self.__age)          #这种写法只能设置只读属性,无法让属性可写。
    13 
    14 jason = Person("Jason")
    15 print(jason.age)
    16 
    17 
    18 #以上代码输出结果如下:
    19 18
    仅设置只读的装饰器

    五.对象的销毁

     1 #!/usr/bin/env python
     2 #_*_coding:utf-8_*_
     3 #@author :yinzhengjie
     4 #blog:https://www.cnblogs.com/yinzhengjie
     5 #EMAIL:y1053419035@qq.com
     6 
     7 import time
     8 
     9 class Person:
    10     def __init__(self,name,age=18):
    11         self.name = name
    12         self.__age = age
    13 
    14     """
    15         类中可以定义"__del__"方法,称为析构函数(方法)。
    16         作用:销毁类的实例的时候调用,以释放占用资源。其中就放些清理资源的代码,比如释放连接。
    17         注意这个方法不能引起真正销毁,只是对象销毁的时候会自动调用它。
    18         
    19         由于Python实现了引用计数的垃圾回收机制,不能确定对象何时执行垃圾回收。
    20         我们可以使用del语句删除实例,引用计数减1。当引用计数为0时,会自动调用"__del__"方法。
    21         由于垃圾回收对象销毁时,才会真正清理对象,还会在回收对象之前自动调用"__del__"方法,除非你明确自己的目的,建议不要手动调用这个方法。
    22     """
    23     def __del__(self):
    24         print("delete {}".format(self.name))
    25 
    26 
    27 if __name__ == '__main__':
    28     jason = Person("Jason")
    29     jason.__del__()
    30     jason.__del__()
    31     jason.__del__()
    32     jason.__del__()
    33     print("=========start=========")
    34     jason2  = jason
    35     jason3 = jason2
    36     print(1,"del")
    37     del  jason
    38     time.sleep(2)
    39 
    40     print(2,"del")
    41     del jason2
    42     time.sleep(2)
    43     print("*" * 50)
    44 
    45     del jason3      #注释一下看看效果
    46     time.sleep(3)
    47     print("=========end========")
    48     
    49     
    50     
    51 #以上代码执行结果如下:
    52 delete Jason
    53 delete Jason
    54 delete Jason
    55 delete Jason
    56 =========start=========
    57 1 del
    58 2 del
    59 **************************************************
    60 delete Jason
    61 =========end========

    六.方法重载(overload)  

      其他面向对象的高级语言中,会有重载的概念。
    
      所谓重载,就是同一个方法名,但是形式参数个数、类型不一样,就是同一个方法的重载。
      Python没有重载!
      Python不需要重载! Python中,方法(函数)定义中,形参非常灵活,不需要指定类型(就算指定了也只是一个说明而非约束),参数个数也不固定(可变参数)。一个函数的定义可以实现很多种不同形式实参的调用。所以Python不需要方法的重载。
      或者说Python语法本身就实现了其它语言的重载。
  • 相关阅读:
    LeetCode_1.Two Sum
    F#周报2018年第48期
    使用Kdenlive为视频加入马赛克特效
    网络安全从入门到精通(第一章)前言篇
    hdu 5023 线段树染色问题
    poj 2528 线段树离散化+染色
    字符Hash初步
    经典二分:秦腾与教学评估
    国王游戏
    Trie:hdu 4825、1251、1247、Poj 3764
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/11161519.html
Copyright © 2011-2022 走看看