类复制方法返回不正确的引用

时间:2019-09-07 16:47:08

标签: python

我正在创建一个小型缓存类,并且复制方法似乎有问题。在缓存类中,有一个名为copy witch的方法返回使用当前类数据初始化的缓存类的新实例。问题是,当我从源类中删除某些内容时,它也会从复制版本中删除它。我已经包含了一些工作代码来演示这一点。有人知道为什么吗?

cfg = {
    'level_1': {
        'level_2': {
            'item1': 10,
            'item2': 20,
        }
    }
}
more_cfg = {
    'level_3': {
        'level_4': {
            'item3': 10,
            'item4': 20,
        }
    }
}


class Cache:
    def __init__(self, *args, **kwargs):
        self.nodes = args[0] if args else kwargs

    def get(self, uri=None):
        if not uri:
            return self.nodes

        def walk(_uri, nodes):
            parts = _uri.split('/', 1)
            key = parts.pop(0)

            if key in nodes:
                node = nodes[key]

                if not parts:
                    return node
                else:
                    return walk(parts[0], node)

        return walk(uri, self.nodes)

    def set(self, uri, *args, **kwargs):

        def walk(_uri, nodes):
            parts = _uri.split('/', 1)
            key = parts.pop(0)

            if key in nodes and parts:
                return walk(parts[0], nodes[key])
            elif len(_uri.split('/')) == 1:
                value = args[0] if args else kwargs
                nodes[key] = value

        return walk(uri, self.nodes)

    def dump(self, indent=None):
        """ Dumps the contents of the cache to the screen.
        The output from dump goes stdout and is used to view the cache contents.
        Default indentation is a dot for each level.
        :param indent:
            indent (str): String to be use for indenting levels.
        :return:
            Nothing.
        """
        indent = indent if indent else '.'

        print('-------------------------------------------------------------------------------------------------------')

        if self.nodes:
            def walk(_cfg, count):
                count += 1
                for key, value in _cfg.items():
                    if isinstance(value, dict):
                        print(indent * count, key)
                        walk(value, count)
                    else:
                        if isinstance(value, str):
                            value = f'"{value}"'
                        print(indent * count, key, value)
            walk(self.nodes, 0)
        else:
            print(' (No Data)')

        print('-------------------------------------------------------------------------------------------------------')

    def copy(self):
        return Cache(self.nodes.copy())

    def remove(self, uri):
        """ Remove entree from cache.
        Removes an entree from the cache if it exists.
        :param uri:
            uri (str): URI that points to the entree to remove.
        :return:
            Nothing.
        """

        uri = uri.lstrip('/')
        if self.exists(uri):
            node = self.get('/'.join(uri.split('/')[:-1]))
            del node[uri.split('/')[-1]]

    def exists(self, uri):
        """ Test if URI exists in the cache.

        :param uri:
        :return:
        """
        return True if self.get(uri) else False

    def destroy(self):
        """ Destroy cache.
        Deletes all entries in the cache.
        :return:
            Nothing.
        """
        del self.nodes
        self.nodes = {}


c = Cache(cfg)

print(c.get('level_1/level_2/item2'))

c.set('level_1/level_2/more_cfg', more_cfg)

if c.exists('level_1/level_2/more_cfg'):
    print(c.get('level_1/level_2/more_cfg'))

cache_copy = c.copy()
cache_copy.dump()

c.remove('level_1/level_2/item1')
c.remove('level_1/level_2/item2')
c.dump()

cache_copy.dump()

2 个答案:

答案 0 :(得分:2)

尝试使用deepcopy代替副本

如果您阅读文档:

  

浅表副本会构造一个新的复合对象,然后(在可能的范围内)将引用插入到原始对象中。

     

深层副本会构造一个新的复合对象,然后递归地将原始对象中发现的对象的副本插入其中

强调我的。

答案 1 :(得分:1)

这是因为.copy()方法复制了现有引用。如上所述,您应该改用deepcopy。