Python Class: 覆盖`self`

Python Class: overwrite `self`

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

我该如何实现?是我遗漏了什么还是我的设计有误?

我要问的第一个问题是为什么方法 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

这似乎可以解决您的问题。但是如果你不想或不能公开底层缓存,那么:

与您尝试做的类似的另一种方式

我相信适合您需要的模式就是所谓的桥接模式,也称为 Handle/Body 模式。引用 Gamma 等人 Design Patterns,该模式用于多种情况,其中包括 "you want to share an implementation among multiple objects and this fact should be hidden from the client or you always want the client to access the actual "body" 实现由于某些其他原因通过 "handle" 对象。"handle" 对象保留对 "body" 实现的引用,并将所有调用委托给该实现。当然,多个句柄可以引用同一个主体. 在下面的示例中,我使用了抽象基础 class,这对于 Python 的鸭子类型来说确实不是必需的。

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,但这只是将 you 从正在创建的对象中删除,调用者有自己独立的别名,该别名不受影响)。

正确的做法是覆盖实际的构造函数,__new__,它允许您 return 新实例,您可以或者可能不会创建:

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__;对象的构造是通过调用 class 的 __new__,然后在结果上隐式调用 __init__ 来完成的。对于缓存的实例,您不希望它们重新初始化,因此您需要一个空的 __init__ (这样缓存的实例不会因为从缓存中检索而被修改)。将所有类似 __init__ 的行为放在 __new__ 中构造和 return 新对象的代码中,并且只对新对象执行它,以避免这个问题。