靠默契保证的私有制: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.73s59.21s26.56s

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

4.总结

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

       欢迎关注 AlgorithmDog 公众号,每两周的更新会有推送哦。

weixin_saomiao

      

此条目发表在编程开发分类目录,贴了, 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。