Python數據模型
引言
像大多數人一樣,我在對一直傳統的面向過程語言C一知半解之後,走進了面向對象的世界,盡管對OOP一無所知,還好Python還保留有函數式編程,這使得我才不那麽抵觸,直到現在,習慣了面向對象之後,也習慣了接口這些叫法,而不是函數。
在看到len(collection)與collection.len(),也越來越習慣後者,他所代表的強大的思想,(其實是調用的collection對象的內部__len__方法),這種設計思想完全體現在 Python 的數據模型上,而數據模型所描述的 API,為使用最地道的語言特性來構建你自己的
對象提供了工具。數據模型其實是對 Python 框架的描述,它規範了這門語言自身構建模塊的接口,這些模塊包括但不限於序列、叠代器、函數、類等。
既然python的一致性是出了名的,來說一說命名風格。
一、命名風格
主要存在四種命名 核心:不要以下劃線開頭命名
1. object # 公用方法
2. __object__ # 內建方法,用戶不要這樣定義 例如__init__
3. __object # 全私有,全保護
4. _object # 半保護
"單下劃線" 開始的成員變量叫做保護變量,意思是只有類對象和子類對象自己能訪問到這些變量;
"雙下劃線" 開始的是私有成員,意思是只有類對象自己能訪問,連子類對象也不能訪問到這個數據。
一般來講,變量名_object被看作是“私有的”,在模塊或類外不可以使用,不能用‘from module import * ‘導入。當變量是私有的時候,用_object來表示變量是很好的習慣。單下劃線+類名,eg:_Class__object 機制就可以訪問__object__了。因為變量名__object__對Python 來說有特殊含義,對於普通的變量應當避免這種命名風格。
class Foo:
_foo = 3 def __init__(self): return Nonedef public_method(self): print(‘This is public method‘) def __fullprivate_method(self): print(‘This is fullprivate_method‘) def _halfprivate_method(self): print(‘This is halfprivate_method‘)
_foo # error
f._foo # 3 f= Foo(1) f.public_method() # OK f.__fullprivate_method() # Error occur f._halfprivate_method() # OK f._Foo__fullprivate_method() # OK
class A:
_a = 5 def __init__(self): self.__private() self.public() def __private(self): print(‘A.__private()‘) def public(self): print(‘A.public()‘) class B(A):
_b = 6 def __private(self): print(‘B.__private()‘) def public(self): print(‘B.public()‘) b = B() b._a
b._B__B 輸出 A.__private() B.public()
5
6
二、搭建一摞pythonic的紙牌
python的另一強大之處就是豐富的標準庫,還有許許多多的第三方庫,這使得不用重復造輪子
import collections
Card = collections.namedtuple(‘Card‘, [‘rank‘, ‘suit‘])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list(‘JQKA‘)
suits = ‘spades diamonds clubs hearts‘.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
deck = FrenchDeck()
for i in deck[:10]: # 其實這裏調用的是deck這個可叠代對象背後其實用的是 iter(x),而這個函數的背後則是 x.__iter__() 方法
print(i)
#打印十張紙牌
Card(rank=‘2‘, suit=‘spades‘) Card(rank=‘3‘, suit=‘spades‘) Card(rank=‘4‘, suit=‘spades‘) Card(rank=‘5‘, suit=‘spades‘) Card(rank=‘6‘, suit=‘spades‘) Card(rank=‘7‘, suit=‘spades‘) Card(rank=‘8‘, suit=‘spades‘) Card(rank=‘9‘, suit=‘spades‘) Card(rank=‘10‘, suit=‘spades‘) Card(rank=‘J‘, suit=‘spades‘)
# 對紙牌進行排序
suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0) def spades_high(card): rank_value = FrenchDeck.ranks.index(card.rank) return rank_value * len(suit_values) + suit_values[card.suit] for card in sorted(deck, key=spades_high): print(card) Card(rank=‘2‘, suit=‘clubs‘) Card(rank=‘2‘, suit=‘diamonds‘) Card(rank=‘2‘, suit=‘hearts‘) ... (46 cards ommitted) Card(rank=‘A‘, suit=‘diamonds‘) Card(rank=‘A‘, suit=‘hearts‘) Card(rank=‘A‘, suit=‘spades‘)
三、特殊方法
下面來看看特殊方法
beer_card = Card(‘7‘, ‘diamonds‘) >>> beer_card Card(rank=‘7‘, suit=‘diamonds‘)
len()方法與特殊方法__len__,
特殊方法的存在是為了被 Python 解釋器調用的,你自己並不需要調用它們。也就是說沒有 my_object.__len__() 這種寫法,而應該使用 len(my_object)。在執行 len(my_object) 的時候,如果my_object 是一個自定義類的對象,那麽 Python 會自己去調用其中由
你實現的 __len__ 方法。abs也是同理,
如果是 Python 內置的類型,比如列表(list)、字符串(str)、字節序列(bytearray)等,那麽 CPython 會抄個近路,__len__ 實際上會直接返回 PyVarObject 裏的 ob_size 屬性。PyVarObject 是表示內存中長度可變的內置對象的 C 語言結構體。直接讀取這
個值比調用一個方法要快很多。
>>> deck = FrenchDeck() >>> len(deck) 52
從一疊牌中抽取特定的一張紙牌,比如說第一張或最後一張,是很容易的:deck[0] 或 deck[-1]。這都是由 __getitem__ 方法提供的
字典中也有這種用法,類似dic[k], 其背後也是__getitem__在默默支持,不過這裏返回的值而是鍵k所對應的值value
>>> deck[0] Card(rank=‘2‘, suit=‘spades‘) >>> deck[-1] Card(rank=‘A‘, suit=‘hearts‘)
常見的特殊方法
當然也可以使用dir內置函數來查看常見並比較的數據結構的特殊方法,如list,dict等。
dir(list) [‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__iadd__‘, ‘__imul__‘, ‘__init__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__reversed__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘append‘, ‘clear‘, ‘copy‘, ‘count‘, ‘extend‘, ‘index‘, ‘insert‘, ‘pop‘, ‘remove‘, ‘reverse‘, ‘sort‘]View Code
dir(tuple) [‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__getnewargs__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘count‘, ‘index‘]View Code
Python數據模型