# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# This is a copy on write dictionary and set which abuses classes to try and be nice and fast.
#
# Copyright (C) 2006 Tim Amsell
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#Please Note:
# Be careful when using mutable types (ie Dict and Lists) - operations involving these are SLOW.
# Assign a file to __warn__ to get warnings about slow operations.
#
from __future__ import print_function
import copy
import types
ImmutableTypes = (
types.NoneType,
bool,
complex,
float,
int,
long,
tuple,
frozenset,
basestring
)
#可變類型添加字尾的标志,如字典。
MUTABLE = "__mutable__"
#COWMeta is a metaclass (元類)。
class COWMeta(type):
pass
#為什麼這個地方要定義一個元類呢。元類的使用:
#隻有當想在派生類上運作代碼。有不想讓使用者注意到。才應該使用元類。
#在這裡。元類替換了常用的内建方法。至于為什麼替換内建的方法。是因為要對自己的字典進行操作。這個操作需要自己的一些行為。
#因為,内建的方法都是隐式調用的。是以,必須用自己的方法,讓使用者在使用的時候隐性的調用對自己字典的方法。
#前面的__是為了私有。不讓使用者去調用。
class COWDictMeta(COWMeta):
__warn__ = False
__hasmutable__ = False
__marker__ = tuple()
#print 函數調用這個方法。
#為什麼這裡用cls,這個可以了解為 class,說明這是類的行為。當然這隻是形式而已。
def __str__(cls):
print ("str")
# FIXME: I have magic numbers!
return "<COWDict Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) - 3)
#如果__str__不存在,那麼, python 将轉向__repr__. 反之,就不會。__repr__用于互動模式,比如>>>x。而__str__用于在程式中需要終端顯示。比如:print x
__repr__ = __str__
#通過一個函數來創作一個類。
def cow(cls):
print ("cow")
class C(cls):
__count__ = cls.__count__ + 1
return C
#copy的方法,是當這個類被執行個體化以後就調用這個函數。比如:a = COWDictMeta()
copy = cow
#當function()的時候調用這個。
__call__ = cow
#相當于。cls.key = vaule. 也就是将這個指派放到一個字典裡
def __setitem__(cls, key, value):
#利用isinstance 判斷一個類型是否屬于一緻的類型。關鍵的地方是ImmutableTypes必須是個元組。
if not isinstance(value, ImmutableTypes):
if not isinstance(value, COWMeta): #說明是一個可變的類型。比如:字典類型。__dict____mutable__
cls.__hasmutable__ = True
key += MUTABLE
setattr(cls, key, value)
#毫無疑問,得到一個可變類型的值,
def __getmutable__(cls, key, readonly=False):
nkey = key + MUTABLE
try:
#這裡傳回鍵值nkey在cls裡面的值。這裡誰都知道的。那麼問題就來了
# return.cls.__dict__[nkey]和getattr(cls,nkey)有什麼差別嗎?
# 在這裡是有的,而且很大。因為cls是一個執行個體傳進來的,cls.__dict__隻是目前執行個體的字典的空間。而getattr(cls,nkey)是表示目前執行個體沒有的nkey的話那麼,會通過類的機制搜尋到,繼承的類字典空間。或者本例中的b = a.copy(),就會搜尋到a 的字典空間。
return cls.__dict__[nkey]
except KeyError:
pass
value = getattr(cls, nkey)
if readonly:
return value
#如果上述方法都沒有找到可變類型key的話,就将這個類型存在這個類裡,也就是這個類的字典裡(cls.__dict__)。并傳回
if not cls.__warn__ is False and not isinstance(value, COWMeta):
print("Warning: Doing a copy because %s is a mutable type." % key, file=cls.__warn__)
try:
value = value.copy() #??.
except AttributeError as e:
value = copy.copy(value)
setattr(cls, nkey, value)
return value
__getmarker__ = []
def __getreadonly__(cls, key, default=__getmarker__):
print ("getreadonly")
"""\
Get a value (even if mutable) which you promise not to change.
"""
return cls.__getitem__(key, default, True)
def __getitem__(cls, key, default=__getmarker__, readonly=False):
print ("getitem")
try:
try:
#取得不可變的參數
value = getattr(cls, key)
except AttributeError:
#這裡如果是可變參數
value = cls.__getmutable__(key, readonly)
# This is for values which have been deleted
if value is cls.__marker__:
raise AttributeError("key %s does not exist." % key)
return value
except AttributeError as e:
if not default is cls.__getmarker__:
return default
raise KeyError(str(e))
#事實上,key并沒有從字典裡删除,隻不過做了個标記而已
def __delitem__(cls, key):
print ("delitem")
cls.__setitem__(key, cls.__marker__)
#删除可變的類型
def __revertitem__(cls, key):
print ("revertitem")
if not cls.__dict__.has_key(key):
key += MUTABLE
delattr(cls, key)
def __contains__(cls, key):
print ("contains")
return cls.has_key(key)
def has_key(cls, key):
#因為要得到一個隻讀的類型,是以傳進的是元組。
value = cls.__getreadonly__(key, cls.__marker__)
if value is cls.__marker__:
return False
return True
def iter(cls, type, readonly=False):
print ("iter")
for key in dir(cls):
if key.startswith("__"):
continue
if key.endswith(MUTABLE):
key = key[:-len(MUTABLE)] #如果是一個可變的類型,那麼去掉可變的标志。
if type == "keys":
yield key
try:
if readonly:
value = cls.__getreadonly__(key)
else:
value = cls[key]
except KeyError:
continue
if type == "values":
yield value
if type == "items":
yield (key, value)
raise StopIteration()# 當字典裡沒有值的時候,停止疊代
# 為什麼這裡面用了return cls.iter("keys")
#因為可疊代的函數必須要求每次能夠傳回一個值,
#如果沒有的話就不是疊代的函數,而且這個函數要滿足兩個條件
#1 有next 的方法:
#2 或者有yield生成器
def iterkeys(cls):
print ("iterkeys")
return cls.iter("keys")
def itervalues(cls, readonly=False):
print ("itervalues")
if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
print("Warning: If you arn't going to change any of the values call with True.", file=cls.__warn__)
return cls.iter("values", readonly)
def iteritems(cls, readonly=False):
print ("iteritems")
if not cls.__warn__ is False and cls.__hasmutable__ and readonly is False:
print("Warning: If you arn't going to change any of the values call with True.", file=cls.__warn__)
return cls.iter("items", readonly)
class COWSetMeta(COWDictMeta):
def __str__(cls):
# FIXME: I have magic numbers!
return "<COWSet Level: %i Current Keys: %i>" % (cls.__count__, len(cls.__dict__) -3)
__repr__ = __str__
def cow(cls):
class C(cls):
__count__ = cls.__count__ + 1
return C
def add(cls, value):
COWDictMeta.__setitem__(cls, repr(hash(value)), value)
def remove(cls, value):
COWDictMeta.__delitem__(cls, repr(hash(value)))
def __in__(cls, value):
return COWDictMeta.has_key(repr(hash(value)))
def iterkeys(cls):
raise TypeError("sets don't have keys")
def iteritems(cls):
raise TypeError("sets don't have 'items'")
# These are the actual classes you use!
class COWDictBase(object):
__metaclass__ = COWDictMeta
__count__ = 1
class COWSetBase(object):
__metaclass__ = COWSetMeta
__count__ = 0
if __name__ == "__main__":
import sys
COWDictBase.__warn__ = sys.stderr
a = COWDictBase()
b = COWDictBase()
# print("a", a)
sys.exit()
a['a'] = 'a'
a['b'] = 'b'
a['dict'] = {}
b = a.copy()
print("b", b)
b['c'] = 'b'
print()
print("a", a)
for x in a.iteritems():
print(x)
print("--")
print("b", b)
for x in b.iteritems():
print(x)
print()
b['dict']['a'] = 'b'
b['a'] = 'c'
print("a", a)
for x in a.iteritems():
print(x)
print("--")
print("b", b)
for x in b.iteritems():
print(x)
print()
try:
b['dict2']
except KeyError as e:
print("Okay!")
a['set'] = COWSetBase()
a['set'].add("o1")
a['set'].add("o1")
a['set'].add("o2")
print("a", a)
for x in a['set'].itervalues():
print(x)
print("--")
print("b", b)
for x in b['set'].itervalues():
print(x)
print()
b['set'].add('o3')
print("a", a)
for x in a['set'].itervalues():
print(x)
print("--")
print("b", b)
for x in b['set'].itervalues():
print(x)
print()
a['set2'] = set()
a['set2'].add("o1")
a['set2'].add("o1")
a['set2'].add("o2")
print("a", a)
for x in a.iteritems():
print(x)
print("--")
print("b", b)
for x in b.iteritems(readonly=True):
print(x)
print()
del b['b']
try:
print(b['b'])
except KeyError:
print("Yay! deleted key raises error")
if b.has_key('b'):
print("Boo!")
else:
print("Yay - has_key with delete works!")
print("a", a)
for x in a.iteritems():
print(x)
print("--")
print("b", b)
for x in b.iteritems(readonly=True):
print(x)
print()
b.__revertitem__('b')
print("a", a)
for x in a.iteritems():
print(x)
print("--")
print("b", b)
for x in b.iteritems(readonly=True):
print(x)
print()
b.__revertitem__('dict')
print("a", a)
for x in a.iteritems():
print(x)
print("--")
print("b", b)
for x in b.iteritems(readonly=True):
print(x)
print()