人类文明开化以来,私有制似乎是人类历史的主流在西方国家,“私有财产神圣不可侵犯” 是很多资本主义国家的立国原则之一。在我国,“私有财产不可侵犯” 也是写在宪法中的。在人类社会中,私有制表面由由法律保证,实质上是有法律背后的国家强制力保证。
试图反映世界万物的编程语言,便也产生了私有的概念。在大部分面向对象的编程语言中,对象可以设置其变量和方法为私有。私有变量和方法只能自己使用,即使其子对象都不能访问。大部分编程语言中的私有制,和现实生活中的私有制一样,也是有强制力保证的。只是这部分强制力来自编程语言本身。比如下面的 Java 代码
- public class Person{
- private int money = 0
- }
Java 语言强制地让 money 只能内部访问。但并不是所有编程语言都是这样的,Python 就是其中一朵奇葩。Python 中的私有制是由默契保证的。
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 是可以破坏这个私有制的。所以这个私有制是建立在开发者之间的默契的基础上:“你既然这么设计了,说明这个变量你不希望我访问或者修改,那我就不访问或者修改了”,并不是建立在强制力的基础上。
访问权限控制
私有制的一个大用处就是控制访问,让一些变量可读不可写。比如写成下面那样,我们就可以通过 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)
RoomAI 的例子
最近在开发非***信息游戏 AI 环境:RoomAI (点击原文可以查看 RoomAI)。RoomAI 的目标是提供一些非***信息游戏环境和一些基线模型算法,方便 AI 开发人员快速地构建、测试和对比自己的非***游戏 AI 算法。目前 RoomAI 已经支持德州、梭哈和七鬼。RoomAI 的基本流程如下所示:玩家 AI 获得游戏环境给出的信息,当前玩家 AI 选择合适的动作,游戏环境根据该动作推进游戏逻辑;重复上述过程,直到分出胜负。给玩家 AI 的信息必须进行访问权限控制,那么玩家 AI 有可能通过信息获取游戏秘密或者操纵游戏。
为了实现这个目的,之前的做法是深度拷贝游戏环境给出的信息。了解了 Python 私有和访问权限控制,我们很容易想到用这套机制改造之。改造之后,我们通过 RoomAI 中 5 个 AI 对战 10000 局七鬼游戏实验,对比拷贝方法和访问控制方法的效率。
其中原始 copy 是直接使用 copy.deepcopy 进行拷贝,改进拷贝是自己实现了 __ deepcopy __ 函数,访问控制则是使用私有变量从而使得信息可读不可写。很明显地,访问控制方法效率比拷贝方法的高不少。
总结
试图反映世界万物的编程语言也有私有的概念。在大部分面向对象的编程语言中,对象可以设置其变量和方法为私有。私有变量和方法只能自己使用,即使其子对象都不能访问。大部分编程语言中的私有制,和现实生活中的私有制一样,也是有强制力保证的。只是这部分强制力来自编程语言本身。但并不是所有编程语言都是这样的,Python 就是其中一朵奇葩。Python 中的私有制是由默契保证的。私有制的作用之一就是访问控制,可以使得某些属性可读不可写。我们将这个做法用到非***信息游戏 AI 环境 RoomAI 中,提供了运行的效率。
【本文为51CTO专栏作者“李立”的原创稿件,转载请通过51CTO获取联系和授权】