Python类:overwrite`self`

2024-10-02 22:34:52 发布

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

在我的python脚本中,我有一个存储Processo对象的全局存储(一个简单的全局dict)。它在我的程序执行期间被填充。它的存在是为了避免由于性能原因而创建重复的Processo对象

因此,对于class Processo,我希望在创建过程中验证它是否已经在全局存储

在这种情况下,我只想把它复制到self。我用getfromStorage()来做这个

class Processo:
    def __init__(self, name, ...): # ... for simplicity
       self.processoname = name
       self = getfromStorage(self)

不知道它是否有用但是

def getfromStorage(processo):
    if processo.processoname in process_storage:
        return process_storage[processo.processoname]
    return processo

我如何做到这一点?是我遗漏了什么还是我的设计错了


Tags: 对象nameself脚本returndefstorage全局
2条回答

我要问的第一个问题是,为什么方法getfromStorage不向用户公开实现:

def getfromStorage(name):
    if name in process_storage:
        return process_storage[name]
    return Processo(name)

Processo定义为:

class Processo:
    def __init__(self, name, ...): # ... for simplicity
       self.processoname = name

这似乎可以解决你的问题。但如果您不想或无法公开基础缓存,则:

另一种类似于您正在尝试的方式

我相信适合你需要的模式是所谓的桥模式,也被称为把手/身体模式。引用Gamma等人的设计模式,该模式用于多种情况,其中包括“您希望在多个对象之间共享一个实现,这一事实应该对客户端隐藏,或者您总是希望客户端访问实际的“主体”由于某些其他原因,通过“handle”对象实现。“handle”对象保留对“body”实现的引用,并将所有调用委托给该实现。当然,多个句柄可以引用同一个主体。在下面的示例中,我使用了一个抽象基类,这在Python的duck类型中是不必要的

from abc import ABC, abstractmethod

class Processo(ABC):

    @abstractmethod
    def __init__(self, name): raise NotImplementedError

    @abstractmethod
    def get_name(self): raise NotImplementedError

    @abstractmethod
    def foo(self): raise NotImplementedError


class Processo_Handle(Processo): # the handle class
    def __init__(self, name):
        self.processo = getfromStorage(name)

    def get_name(self):
        return self.processo.get_name() # delegate to "body" object

    def foo(self):
        self.processo.foo() # delegate to "body" object
        return self # don't return result of the call to foo because it's the wrong instance (i.e. the body)


class Processo_Body(Processo): # the bodyclass
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name

    def foo(self):
        """ do whatever it takes """
        return self

process_storage = {} # dummy implementation    
def getfromStorage(processoname):
    if processoname in process_storage:
        return process_storage[processoname]
    return Processo_Body(processoname)

使用__init__无法合理地完成此模式,因为__init__初始化一个已经存在的对象,并且您无法更改调用者将获得的内容(您可以重新绑定self,但这只会将您与正在创建的对象断开,调用者有自己的独立别名,不受影响)

正确的方法是重写实际的构造函数__new__,它允许您返回新实例,您可以创建也可以不创建新实例:

class Processo:
    def __new__(cls, name, ...): # ... for simplicity
       try:
           # Try to return existing instance from storage
           return getfromStorage(name)
       except KeyError:
           pass

       # No instance existed, so create new object
       self = super().__new__(cls)  # Calls parent __new__ to make empty object

       # Assign attributes as normal
       self.processoname = name

       # Optionally insert into storage here, e.g. with:
       self = process_storage.setdefault(name, self)
       # which will (at least for name of built-in type) atomically get either then newly
       # constructed self, or an instance that was inserted by another thread
       # between your original test and now
       # If you're not on CPython, or name is a user-defined type where __hash__
       # is implemented in Python and could allow the GIL to swap, then use a lock
       # around this line, e.g. with process_storage_lock: to guarantee no races

       # Return newly constructed object
       return self

为了减少开销,我温和地重写了getfromStorage,因此它只取名称并执行查找,如果失败,允许异常冒泡:

def getfromStorage(processoname):
    return process_storage[processoname]

这意味着,当可以使用缓存实例时,不需要重新构造不必要的self对象

注意:如果这样做,通常最好不要定义__init__;对象的构造是通过调用类的__new__,然后对结果隐式调用__init__来完成的。对于缓存实例,您不希望重新初始化它们,因此需要一个空的__init__(这样缓存实例不会因为从缓存中检索而被修改)。将所有类似__init__的行为放在__new__中构造并返回新对象的代码中,并且只对新对象执行它,以避免此问题

相关问题 更多 >