靠默契保證的私有制:Python 中的私有
人類文明開化以來,私有制似乎是人類歷史的主流在西方國家,“私有財產(chǎn)神圣不可侵犯” 是很多資本主義國家的立國原則之一。在我國,“私有財產(chǎn)不可侵犯” 也是寫在憲法中的。在人類社會中,私有制表面由由法律保證,實質(zhì)上是有法律背后的國家強制力保證。
試圖反映世界萬物的編程語言,便也產(chǎn)生了私有的概念。在大部分面向?qū)ο蟮木幊陶Z言中,對象可以設置其變量和方法為私有。私有變量和方法只能自己使用,即使其子對象都不能訪問。大部分編程語言中的私有制,和現(xiàn)實生活中的私有制一樣,也是有強制力保證的。只是這部分強制力來自編程語言本身。比如下面的 Java 代碼
- public class Person{
- private int money = 0
- }
Java 語言強制地讓 money 只能內(nèi)部訪問。但并不是所有編程語言都是這樣的,Python 就是其中一朵奇葩。Python 中的私有制是由默契保證的。
Python 中的私有制
我們知道 Python 是一門很隨意的編程語言,并沒有由語言提供的權限控制機制。雖然 Python 對象中的所有屬性都可以被外界訪問,但我們可以構造出一個偽私有來,如下面的代碼。
- class Person:
- def __init__(self):
- self.__age = 10
上面的代碼定義了一個 Person 類, 有 __age 表示的年齡屬性。神奇的事情發(fā)生了:
- >> p = Person()
- >> p.__age
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- AttributeError: Person instance has no attribute '__age'
Person 的對象中沒有年齡屬性。噢耶,私有制完成。這個現(xiàn)象的原理簡單明了:以 __ 打頭并且不以 __ 結尾的屬性變量,都會自動更名為 _類名__變量名。比如上面的 __age 實際名字是 _Person__age。如果外界不認這個私有,直接訪問 _Person_age 是可以破壞這個私有制的。所以這個私有制是建立在開發(fā)者之間的默契的基礎上:“你既然這么設計了,說明這個變量你不希望我訪問或者修改,那我就不訪問或者修改了”,并不是建立在強制力的基礎上。
訪問權限控制
私有制的一個大用處就是控制訪問,讓一些變量可讀不可寫。比如寫成下面那樣,我們就可以通過 age() 訪問年齡屬性了。
- class Person:
- def __init__(self):
- self.__age = 10
- def age(self):
- return self.__age
對于 Python 可讀不可寫變量,我們需要注意有兩點和其他語言不一樣。***點是 Python 提供了 property 修飾符,可以讓函數(shù)看起了像變量,但***不用。這是因為看起來像變量,不是很熟悉內(nèi)部機制的開發(fā)者很容易去改動它,比如下面的例子。
- 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 中的元素可變。如果一個對象的屬性變量是這些聚合類型,那么可以通過修改其元素的方式改變其內(nèi)容,如下所示。
- 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 的例子
最近在開發(fā)非***信息游戲 AI 環(huán)境:RoomAI (點擊原文可以查看 RoomAI)。RoomAI 的目標是提供一些非***信息游戲環(huán)境和一些基線模型算法,方便 AI 開發(fā)人員快速地構建、測試和對比自己的非***游戲 AI 算法。目前 RoomAI 已經(jīng)支持德州、梭哈和七鬼。RoomAI 的基本流程如下所示:玩家 AI 獲得游戲環(huán)境給出的信息,當前玩家 AI 選擇合適的動作,游戲環(huán)境根據(jù)該動作推進游戲邏輯;重復上述過程,直到分出勝負。給玩家 AI 的信息必須進行訪問權限控制,那么玩家 AI 有可能通過信息獲取游戲秘密或者操縱游戲。
為了實現(xiàn)這個目的,之前的做法是深度拷貝游戲環(huán)境給出的信息。了解了 Python 私有和訪問權限控制,我們很容易想到用這套機制改造之。改造之后,我們通過 RoomAI 中 5 個 AI 對戰(zhàn) 10000 局七鬼游戲?qū)嶒灒瑢Ρ瓤截惙椒ê驮L問控制方法的效率。
其中原始 copy 是直接使用 copy.deepcopy 進行拷貝,改進拷貝是自己實現(xiàn)了 __ deepcopy __ 函數(shù),訪問控制則是使用私有變量從而使得信息可讀不可寫。很明顯地,訪問控制方法效率比拷貝方法的高不少。
總結
試圖反映世界萬物的編程語言也有私有的概念。在大部分面向?qū)ο蟮木幊陶Z言中,對象可以設置其變量和方法為私有。私有變量和方法只能自己使用,即使其子對象都不能訪問。大部分編程語言中的私有制,和現(xiàn)實生活中的私有制一樣,也是有強制力保證的。只是這部分強制力來自編程語言本身。但并不是所有編程語言都是這樣的,Python 就是其中一朵奇葩。Python 中的私有制是由默契保證的。私有制的作用之一就是訪問控制,可以使得某些屬性可讀不可寫。我們將這個做法用到非***信息游戲 AI 環(huán)境 RoomAI 中,提供了運行的效率。
【本文為51CTO專欄作者“李立”的原創(chuàng)稿件,轉載請通過51CTO獲取聯(lián)系和授權】