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 "" % (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 "" % (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()

你可能感兴趣的:(Poky)