靠默契保证的私有制:Python 中的私有

人类文明开化以来,私有制似乎是人类历史的主流在西方国家,“私有财产神圣不可侵犯” 是很多资本主义国家的立国原则之一。在我国,“私有财产不可侵犯” 也是写在宪法中的。在人类社会中,私有制表面由由法律保证,实质上是有法律背后的国家强制力保证。

试图反映世界万物的编程语言,便也产生了私有的概念。在大部分面向对象的编程语言中,对象可以设置其变量和方法为私有。私有变量和方法只能自己使用,即使其子对象都不能访问。大部分编程语言中的私有制,和现实生活中的私有制一样,也是有强制力保证的。只是这部分强制力来自编程语言本身。比如下面的 Java 代码

public class Person{
   private int money = 0
}

Java 语言强制地让 money 只能内部访问。但并不是所有编程语言都是这样的,Python 就是其中一朵奇葩。Python 中的私有制是由默契保证的。

1. Python 中的私有制

我们知道 Python 是一门很随意的编程语言,并没有由语言提供的权限控制机制。虽然 Python 对象中的所有属性都可以被外界访问,但我们可以构造出一个伪私有来,如下面的代码。

class Person:
    def __init__(self): 
       self.__age = 10

上面的代码定义了一个 Person 类, 有 __age 表示的年龄属性。神奇的事情发生了:

>> p = Person()
>> p.__age
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
AttributeError: Person instance has no attribute '__age'

Person 的对象中没有年龄属性。噢耶,私有制完成。这个现象的原理简单明了:以 __ 打头并且不以 __ 结尾的属性变量,都会自动更名为 _类名__变量名。比如上面的 __age 实际名字是 _Person__age。如果外界不认这个私有,直接访问 _Person_age 是可以破坏这个私有制的。所以这个私有制是建立在开发者之间的默契的基础上:“你既然这么设计了,说明这个变量你不希望我访问或者修改,那我就不访问或者修改了”,并不是建立在强制力的基础上。

2. 访问权限控制

私有制的一个大用处就是控制访问,让一些变量可读不可写。比如写成下面那样,我们就可以通过 age() 访问年龄属性了。

class Person:
    def __init__(self): 
       self.__age = 10
    def age(self):
       return self.__age

对于 Python 可读不可写变量,我们需要注意有两点和其他语言不一样。第一点是 Python 提供了 property 修饰符,可以让函数看起了像变量,但最好不用。这是因为看起来像变量,不是很熟悉内部机制的开发者很容易去改动它,比如下面的例子。

class Person:
    def __init__(self): 
       self.__age = 10
    @property
    def age(self):
       return self.__age
>> p = Person()
>> p.age
10
>> p.age = 25
>> p._Person__age
10
>> p.age
25

p.age = 25 是构建了一个新变量 age = 25, 原来的 .age 就不见了。这时候虽然实际的年龄属性 _Person__age 并没有被改变,但程序通过 .age 访问不到它了。

第二点是需要注意聚合类型的属性变量。聚合类型变量中的 list, set 和 dict 中的元素可变。如果一个对象的属性变量是这些聚合类型,那么可以通过修改其元素的方式改变其内容,如下所示。

class Person:
    def __init__(self): 
       self.__lessons = ["Chinese","English","Math"]
   
    def lessons(self):
       return self.__lessons
>> p = Person()
>> p.lessons
["Chinese","English","Math"]
>> p.lessons[0] = "CS"
>> p.lessons
["CS","English","Math"]

解决这个问题的办法就是用 tuple, fronzenset 和 fronzendict。

class Person:
    def __init__(self): 
       self.__lessons = ["Chinese","English","Math"]
   
    def lessons(self):
       return tuple(self.__lessons)

3.RoomAI 的例子

最近在开发非完美信息游戏 AI 环境:RoomAI。RoomAI 的目标是提供一些非完美信息游戏环境和一些基线模型算法,方便 AI 开发人员快速地构建、测试和对比自己的非完美游戏 AI 算法。目前 RoomAI 已经支持德州、梭哈和七鬼。RoomAI 的基本流程如下所示:玩家 AI 获得游戏环境给出的信息,当前玩家 AI 选择合适的动作,游戏环境根据该动作推进游戏逻辑;重复上述过程,直到分出胜负。给玩家 AI 的信息必须进行访问权限控制,那么玩家 AI 有可能通过信息获取游戏秘密或者操纵游戏。

为了实现这个目的,之前的做法是深度拷贝游戏环境给出的信息。了解了 Python 私有和访问权限控制,我们很容易想到用这套机制改造之。改造之后,我们通过 RoomAI 中 5 个 AI 对战 10000 局七鬼游戏实验,对比拷贝方法和访问控制方法的效率。

原始copy方案

改进copy方案

访问控制方案

371.73s

59.21s

26.56s

其中原始 copy 是直接使用 copy.deepcopy 进行拷贝,改进拷贝是自己实现了 __ deepcopy __ 函数(为什么要自己实现 __ deepcopy __ 的资料),访问控制则是使用私有变量从而使得信息可读不可写。很明显地,访问控制方法效率比拷贝方法高不少。

4.总结

试图反映世界万物的编程语言也有私有的概念。在大部分面向对象的编程语言中,对象可以设置其变量和方法为私有。私有变量和方法只能自己使用,即使其子对象都不能访问。大部分编程语言中的私有制,和现实生活中的私有制一样,也是有强制力保证的。只是这部分强制力来自编程语言本身。但并不是所有编程语言都是这样的,Python 就是其中一朵奇葩。Python 中的私有制是由默契保证的。私有制的作用之一就是访问控制,可以使得某些属性可读不可写。我们将这个做法用到非完美信息游戏 AI 环境 RoomAI 中,提高了运行效率。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木制robot技术杂谈

谈一谈Python中str()和repr()的区别

前言 在学习BeautifulSoup文档的时候发现了一个以前不常见的Python内建函数repr(),带着好奇对这个内建函数进行了一番搜索和学习。 总结 s...

37640
来自专栏程序员互动联盟

【编程基础】main函数,你知道多少?

近期学习时对这个问题产生了迷惑,看到了这篇文章,感觉挺好。 在C/C++的学习过程中,一个很常见的问题就是void main和int main有什么区别呢?本文...

37380
来自专栏Golang语言社区

第九节 Go语言循环语句

干货来了!!!为了让更多的小伙伴喜欢Golang、加入Golang之中来,Golang语言社区发起人彬哥联合业界大牛共同推出了Go语言基础、进阶、提高课程,目前...

9120
来自专栏magicsoar

Effective Modern C++翻译(6)-条款5:auto比显示的类型声明要更好

    在概念上说,auto关键字和它看起来一样简单,但是事实上,它要更微妙一些的。使用auto会让你在声明变量时省略掉类型,同时也会防止了手动类型声明带来的正...

207100
来自专栏软件开发 -- 分享 互助 成长

简单工厂模式

一、简单工厂模式的相关概念: 1、定义:简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式。 其核心思想就是有一...

19090
来自专栏C语言及其他语言

[每日一题]平移运动

估计大家今天忙开学迎新什么的都忙不过来了吧,今天介绍的这题呢,跟之前的题很像,也是数组的题 题目描述 有n个整数,使前面各数顺序向后移m个位置,最后m个数变成...

28450
来自专栏向治洪

Scala入门笔记

Scala入门 Scala简介 ps:在最新的薪资调查中,Scala程序员的工资是平均最高的Scala工资。 Scala是一门多范式的编程语言,一种类似ja...

22570
来自专栏IMWeb前端团队

神奇运算符

每一门编程语言的设计都离不开运算符,我们写的每一行代码基本也少不了它们,这篇文章就让我们一起来了解一下这个无处不在的小伙伴的应用和小技巧吧~~ ~ 按位取反 字...

22390
来自专栏书山有路勤为径

寻找中位数

LeetCode 295. Find Median from Data Stream 设计一个数据结构,该数据结构动态维护一组数据,且支持如下操作: 1.添...

13630
来自专栏Java帮帮-微信公众号-技术文章全总结

第八天 自定义类型方法集合混合使用【悟空教程】

21280

扫码关注云+社区

领取腾讯云代金券