Python copyonwrite行为

2024-10-01 13:24:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在处理一个问题,我正在实例化一个对象的多个实例。大多数情况下,实例化的对象是相同的。为了减少内存开销,我希望所有相同的对象都指向同一个地址。但是,当我修改对象时,我希望创建一个新实例——本质上是写时复制行为。在Python中实现这一点的最佳方法是什么?在

Flyweight图案更接近。示例(来自http://codesnipers.com/?q=python-flyweights):

import weakref

class Card(object):
    _CardPool = weakref.WeakValueDictionary()
    def __new__(cls, value, suit):
        obj = Card._CardPool.get(value + suit, None)
        if not obj:
            obj = object.__new__(cls)
            Card._CardPool[value + suit] = obj
            obj.value, obj.suit = value, suit
        return obj

其行为如下:

^{pr2}$

期望的行为是:

>>> c1 = Card('10', 'd')
>>> c2 = Card('10', 'd')
>>> id(c1) == id(c2)
True
>>> c2.suit = 's'
>>> c1.suit
'd'
>>> id(c1) == id(c2)
False

更新:我偶然发现了Flyweight模式,它似乎几乎符合要求。不过,我对其他方法持开放态度。在


Tags: 对象实例方法idobjnewobjectvalue
3条回答

您是否需要id(c1)==id(c2)完全相同,或者这只是一个演示,真正的目标是避免创建重复的对象?在

一种方法是让每个对象都是不同的,但像上面所说的那样保存对“真实”对象的内部引用。然后,在任何__setattr__调用中,更改内部引用。在

我以前从未做过__setattr__的事情,但我想应该是这样的:

class MyObj:
    def __init__(self, value, suit):
        self._internal = Card(value, suit)

    def __setattr__(self, name, new_value):
        if name == 'suit':
            self._internal = Card(value, new_value)
        else:
            self._internal = Card(new_value, suit)

类似地,通过getattr公开属性。在

你仍然有很多重复的对象,但是只有一个“真实”的备份对象在它们后面。所以如果每个物体都是大的,这会有帮助,如果它们很轻的话也不会有帮助,但是你有数百万个这样的物体。在

不可能。在

id(c1) == id(c2)

表示c1和{}是对同一对象的引用。所以

c2.suit = 's'c1.suit = 's'完全相同。在

Python无法区分这两者(除非允许对以前的调用帧进行内省,这会导致一次肮脏的黑客攻击)

由于这两个赋值是相同的,Python无法知道c2.suit = 's'应该导致名称c2引用不同的对象。在


为了让你知道这个肮脏的黑客会是什么样子

^{pr2}$

这种回溯的使用只适用于那些使用框架的Python实现,例如CPython,而不是Jython或IronPython。在

另一个问题是

name = text[:text.find('.suit')]

是非常脆弱的,而且如果任务看起来像

if True: c2.suit = 's'

或者

c2.suit = (
    's')

或者

setattr(c2, 'suit', 's')

另一个问题是它假设名称c2是全局的。它可以很容易地成为一个局部变量(比如,在一个函数中),或者一个属性(obj.c2.suit = 's')。在

我不知道一个方法来解决所有可能的任务。在

在任何一种情况下,肮脏的黑客攻击都会失败。在

结论:不要用它。:)

以你目前的状态这是不可能的。名称(c1和{}在您的示例中)是一个引用,您不能简单地通过使用__setattr__来更改引用,更不用说对同一对象的所有其他引用。在

唯一可能的办法是:

c1 = c1.changesuit("s")

其中c1.changesuit返回对(新创建的)对象的引用。但只有当每个对象只被一个名称引用时,这才有效。或者你可以用locals()之类的东西来做一些魔术,但是请不要

相关问题 更多 >