21.Python编程:访问权限

前面一节重点学习了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,专门帮我们解决上面的问题,我们会在后面详细学习这些知识。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,218评论 4 16
  • 〇、前言 本文共108张图,流量党请慎重! 历时1个半月,我把自己学习Python基础知识的框架详细梳理了一遍。 ...
    Raxxie阅读 18,954评论 17 410
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • “谢谢亲爱的,你其实很体贴。感觉教练技术让你受益匪浅,为你的觉察力喝彩。” “如果生活中,我们有时是教练,有时是闺...
    羊群_拓思PCP教练阅读 337评论 1 9
  • 我们班有个孩子叫杨家乐,是一个活泼好动的帅气男孩儿。杨家乐学习基础差,纪律习惯差,总受到老师的批评,可他又偏爱...
    安翾阅读 387评论 0 0