天天看點

poky: COW.py 分析

# 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()