21.Python编程:访问权限

作者: TensorFlow开发者 | 来源:发表于2018-04-22 00:06 被阅读24次

    前面一节重点学习了Python3中的面向对象最基础的类和对象的知识:类的定义、对象、构造方法、属性和方法等,在类中定义好的属性,就可以赋值、取值等一系列操作了。

    例如:定义一个SmartPhone类,源码如下:

    # 定义一个SmartPhone类
    class SmartPhone(object):
        os = ""  # 系统
        type = ""  # 品牌
        memory = "4GB"  # 内存大小 默认单位GB
    
        # 构造方法
        def __init__(self, os, type, memory):
            self.os = os
            self.type = type
            self.memory = memory
    
        # 定义一个打印智能手机属性的方法
        def print_phone_info(self):
            print('os:', self.os, '\n品牌:', self.type, '\n内存大小:', self.memory)
    

    创建一个SmartPhone对象,打印调用打印信息方法print_phone_info()后,再修改属性的值。但是,从前面SmartPhone类的定义来看,外部代码还是可以自由地修改一个实例的type 、memory 、os等属性的。如下:

    # 创建一个SmartPhone类型的对象
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    print('---------------------')
    
    # 修改phone1指向的对象的属性:品牌、内存
    phone1.type = "MeiZu 7Pro"
    phone1.memory = '8GB'
    phone1.print_phone_info()
    

    运行结果如下:

    os: Android 
    品牌: Galaxy 
    内存大小: 6GB
    ---------------------
    os: Android 
    品牌: MeiZu 7Pro 
    内存大小: 8GB
    

    Java中是通过关键字:私有的private、公开的public、默认受保护的protected等来限制属性、方法的访问权限的。如果在Python中,想要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,即:__

    私有属性

    在Python中规定:实例的变量名如果以__开头,就变成了一个私有变量(private),只有类内部可以访问,类外部不能访问。

    所以,我们把SmartPhone类的定义做一下修改:

    # 定义一个SmartPhone类
    class SmartPhone(object):
        __os = ""  # 私有属性:系统
        __type = ""  # 私有属性:品牌
        __memory = "4GB"  # 私有属性:内存大小 默认单位GB
    
        # 构造方法
        def __init__(self, os, type, memory):
            self.__os = os
            self.__type = type
            self.__memory = memory
    
        def print_phone_info(self):
            print('os:', self.__os, '\n品牌:', self.__type, '\n内存大小:', self.__memory)
    
    

    创建一个SmartPhone对象,打印调用打印信息方法print_phone_info()后,再访问私有属性,会报错:AttributeError,如下:

    # 创建一个SmartPhone类型的对象
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    # 在类的外部访问私有属性__type,会报错:AttributeError
    phone1.__type
    

    运行结果:

    Traceback (most recent call last):
    os: Android 
      File "F:/python_projects/oop/23SmartPhone.py", line 21, in <module>
    品牌: Galaxy 
        phone1.__type
    内存大小: 6GB
    AttributeError: 'SmartPhone' object has no attribute '__type'
    

    可以看到运行结果报错了,AttributeError: 'SmartPhone' object has no attribute '__type'意思是:属性错误:SmartPhone的对象没有__type属性。

    这是因为我们在type属性前加了__,使__type变成了私有属性,只有类内部可以访问,类外部不能访问。

    假如,我们在类的外部还想访问、修改类里面的私有属性怎么办?可能你已经想到了:给类添加访问、修改私有属性的方法!没错,修改如下:

    # 定义一个SmartPhone类
    class SmartPhone(object):
        __os = ""  # 私有属性:系统
        __type = ""  # 私有属性:品牌
        __memory = "4GB"  # 私有属性:内存大小 默认单位GB
    
        # 构造方法
        def __init__(self, os, type, memory):
            self.__os = os
            self.__type = type
            self.__memory = memory
    
        def print_phone_info(self):
            print('os:', self.__os, '\n品牌:', self.__type, '\n内存大小:', self.__memory)
    
        #  对外界提供获取__os的值的方法
        def get_os(self):
            return self.__os
    
        #  对外界提供设置__os的值的方法
        def set_os(self, os):
            self.__os = os
    
        #  对外界提供获取__type的值的方法
        def get_type(self):
            return self.__type
    
        #  对外界提供设置__type的值的方法
        def set_type(self, types):
            self.__type = types
    
        #  对外界提供获取__memory的值的方法
        def get_memory(self):
            return self.__memory
    
        #  对外界提供设置__memory的值的方法
        def set_memory(self, memory):
            self.__memory = memory
    

    创建对象,在外界获取和设置属性的值如下:

    # 创建一个SmartPhone类型的对象
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    print('-------------------')
    
    # 通过提供的set方法在类的外部访问私有属性
    phone1.set_type("MeiZu 7Pro")
    phone1.set_memory("8GB")
    
    # 通过提供的get方法在类的外部访问私有属性
    print('外部访问:\n', phone1.get_os(), '\n', phone1.get_type(), '\n', phone1.get_memory())
    

    运行结果:

    os: Android 
    品牌: Galaxy 
    内存大小: 6GB
    -------------------
    外部访问:
     Android 
     MeiZu 7Pro 
     8GB
    

    可以看到运行结果,在类的外部成功地获取、修改私有属性的值了。这样做,不是自找麻烦吗?那么,为什么要这样多此一举呢?这么做的意义非常大:在修改类中的属性前,可以对外界传入的数据做检查。例如:外界传入的品牌不是字符串,而是随意传入了一个整数:

    # 创建一个SmartPhone类型的对象
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    print('-------------------')
    
    phone1.set_type(2)
    print(phone1.get_type())
    

    运行结果:

    os: Android 
    品牌: Galaxy 
    内存大小: 6GB
    -------------------
    2
    

    这是未做检查的运行结果。在set_type()方法中做了数据检查,其他代码不变,更新的部分如下:

    #  对外界提供设置__type的值的方法
        def set_type(self, types):
            if isinstance(types, str):
                self.__type = types
            else:
                raise ValueError(" SmartPhone type should be str!")
    

    重新运行,运行结果:

    os: Android 
    Traceback (most recent call last):
    品牌: Galaxy 
      File "F:/python_projects/oop/23SmartPhone.py", line 51, in <module>
    内存大小: 6GB
        phone1.set_type(2)
    -------------------
      File "F:/python_projects/oop/23SmartPhone.py", line 33, in set_type
        raise ValueError(" SmartPhone type should be str!")
    

    起到了对传入的无效数据检查的作用。关于Python中异常的处理后面会学习,此处先拿来用下。raise:表示抛出异常,抛出一个ValueError的异常,并提示用户:SmartPhone type should be str!

    私有方法

    Python中规定,私有方法同样以两个下划线开头,声明该方法为私有方法,只能在类的内部调用,不能在类地外部调用,形如:self.__method_name

    在上面的SmartPhone类中添加一个私有方法__test(),代码如下:

    # 上接SmartPhone类,添加一个私有方法
    
        # 添加一个私有方法
        def __test(self):
            print("__test()是私有方法")
    

    创建一个SmartPhone类型的对象,

    
    # 创建一个SmartPhone类型的对象,并在外部调用该私有方法,
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    print('-------------------')
    # 在外部调用该私有方法,
    phone1.__test()
    

    运行结果:

    os: Android 
    Traceback (most recent call last):
    品牌: Galaxy 
      File "F:/python_projects/oop/23SmartPhone.py", line 55, in <module>
    内存大小: 6GB
        phone1.__test()
    -------------------
    AttributeError: 'SmartPhone' object has no attribute '__test'
    

    报错AttributeError: 'SmartPhone' object has no attribute '__test',意思是:属性错误:SmartPhone的对象没有__test属性

    提示

    需要特别注意的是,在Python中,变量名类似__xxxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量。而特殊变量是可以直接访问的,并不是私有的变量,所以我们自己在定义类中的私有属性时,千万不能用__xxxx__这样的变量名,以免混淆。

    有些时候,你会看到以一个下划线开头的实例变量名,比如_xxxx,这样的实例变量外部也是可以访问的。但是,按照Python约定俗成的规定,好的习惯是,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

    注意:
    双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。

    上面例子中,不能直接访问__type是因为Python解释器对外把__type变量改成了_SmartPhone__type,所以,仍然可以通过_SmartPhone__type来访问__type变量。例如:

    # 创建一个SmartPhone类型的对象
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    print('-------------------')
    
    print(phone1._SmartPhone__os)
    print(phone1._SmartPhone__type)
    print(phone1._SmartPhone__memory)
    

    运行结果:

    os: Android 
    品牌: Galaxy 
    内存大小: 6GB
    -------------------
    Android
    Galaxy
    6GB
    

    但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__xxxx改成不同的变量名,不一定总是_类名__xxxx

    易错点举例

    # 上接SmartPhone类定义
    
    # 创建一个SmartPhone类型的对象
    phone1 = SmartPhone('Android', 'Galaxy', '6GB')
    phone1.print_phone_info()
    
    print('-------------------')
    
    # 修改phone1指向的对象的内存为"8GB"
    phone1.__memory = "8GB"
    print('__memory :', phone1.__memory)
    
    print('get_memory() :', phone1.get_memory())
    print('_SmartPhone__memory :', phone1._SmartPhone__memory)
    

    从表面上看,我们在外部代码中“成功”地把"8GB"设置了__memory 变量,但实际上这个__memory变量和class内部的__memory变量不是一个变量!内部的__memory变量已经被Python解释器自动改成了_SmartPhone__memory,而外部代码给phone1新增了一个__memory变量。

    运行结果:

    os: Android 
    品牌: Galaxy 
    内存大小: 6GB
    -------------------
    __memory : 8GB
    get_memory() : 6GB
    _SmartPhone__memory : 6GB
    

    运行结果印证了上面的话,这是一个易忽略点,要留意。

    说明

    类中还预定义了一些方法,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类,我们会在以后一一详细学习。

    类的专有方法:
    __init__ : 构造函数,在生成对象时调用
    __del__ : 析构函数,释放对象时使用
    __repr__ : 打印,转换
    __setitem__ : 按照索引赋值
    __getitem__: 按照索引获取值
    __len__: 获得长度
    __cmp__: 比较运算
    __call__: 函数调用
    __add__: 加运算
    __sub__: 减运算
    __mul__: 乘运算
    __div__: 除运算
    __mod__: 求余运算
    __pow__: 乘方
    

    小结

    本文主要通过几个简单的例子来学习类中私有属性和方法,以及它们之间的区别,要熟练掌握。

    如果你观察的足够仔细,你会发现,例子中我们属性只有3个,增加了3对get\set方法,也就是6个。那么,如果属性非常多时,我们上面的做法又会显得非常麻烦,这有没有好的解决方案呢?答案是有的。python中给我们提供了一些关键词@property,专门帮我们解决上面的问题,我们会在后面详细学习这些知识。


    更多了解,可关注公众号:人人懂编程


    微信公众号:人人懂编程

    相关文章

      网友评论

      本文标题:21.Python编程:访问权限

      本文链接:https://www.haomeiwen.com/subject/sonokftx.html