通过共享对象的属性创建对象视图

2024-05-19 07:21:49 发布

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

我目前正在研究一种可维护且易于使用的方法来创建python对象的“视图”。具体来说,我有一个非常大的类集合,这些类共享两个公共方法,我想包装这些类的实例,以便修改这些方法的行为。你知道吗

当然,我可以为每个类创建一个新类,遵循包装器模式,重新定义完整接口,并将每个方法重定向到原始对象,但我希望重写的方法除外。这是不实际的,因为在任何类发生变化时都需要大量的代码和维护。你知道吗

一些实验表明,我可以通过大量使用元类和内省来生成包装器,以便在包装器对象中“重新创建”接口,但这被证明是相当可怕的使用和调试,特别是如果a具有属性(不包括代码)

第二次尝试表明,通过共享__dict__属性并重写__class__,可以用相当少的代码完成。这将导致以下代码(https://repl.it/repls/InfantileAshamedProjector):

##############################
# Existing code
##############################

class A1:
  def __init__(self, eggs):
    self.eggs = eggs

  # Lots of complicated functions and members

  def hello(self):
    print ("hello, you have %d eggs" % self.eggs)

  def meeting(self):
    self.hello()
    print ("goodbye")

  # Lots of complicated functions calling hello.

# Lots of A2, A3, A4 with the same pattern

##############################
# "Magic" code for view generation
##############################

class FutureView:
  pass

def create_view(obj, name):
  class View(obj.__class__):
    def hello(self):
      print ("hello %s, you have %d eggs" % (name, self.eggs))

  view = FutureView()
  view.__dict__ = obj.__dict__
  view.__class__ = View

  return view

##############################
# Sample of use
##############################

a = A1(3)
a.hello() # Prints hello, you have 3 eggs

v = create_view(a, "Bob")
v.hello() # Prints hello Bob, you have 3 eggs

a.eggs = 5
a.hello() # Prints hello, you have 5 eggs
v.hello() # Prints hello Bob, you have 5 eggs

a.meeting() # Prints hello, you have 5 eggs. Goodbye
v.meeting() # Prints hello Bob, you have 5 eggs. Goodbye

这使得代码相当短,修改A1、A2等。。。类不需要对补丁进行任何更改,这非常好。然而,我显然担心在多个类之间共享__dict__的影响。我的问题是:

  • 你有没有看到其他的方法来达到我的目标,或者稍微改进一下上面的方法,或者完全不同的方法?(请注意,允许等级修改/添加而无需对修补机械进行任何更改是一项硬要求)
  • 当我有多个对象共享同一个__dict__时,我应该注意哪些陷阱?你知道吗
  • 什么是最好的(不那么坏?)通过明确提供__dict____class__来创建对象的方法?你知道吗
  • 奖励:如上面的例子所示,我需要在包装器上附加一条额外的信息。因为我不能将它添加到对象__dict__,我被迫将它添加到类本身,或者作为类成员,或者作为“捕获的”变量。我还能把它放在别的位置吗?理想情况下,我希望避免为name的每个实例创建一个新类(我只想为每个原始类动态创建一个新类)

其他考虑过的解决方案:

代理对象(请参见胡安帕.阿里维拉加答案)是一个近乎完美的解决方案,但当修补的函数被另一个函数内部调用时,它就不够了。具体来说,在上面发布的代码中,对meeting函数的最终调用将使用原始实现,而不是修补的实现。参见https://repl.it/repls/OrneryLongField以获取示例。你知道吗


Tags: of对象方法代码selfyouviewhello
1条回答
网友
1楼 · 发布于 2024-05-19 07:21:49

在我看来,您似乎想要一个代理对象,这通常就是视图。简而言之,模式可以很简单(对于只读代理),如下所示:

class View:
    def __init__(self, obj):
        self._obj = obj
    def __getattr__(self, attr):
        return getattr(self._obj, attr)

__getattr__的好处是,只有在找不到属性时才调用它。如果您想要写访问,那么您需要更加小心,实现__setattribute__,这是总是调用的,并且很容易在不经意间触发无限递归。你知道吗

注意,因为我们在被代理的对象上使用getattr,所以我们不必管理重新创建接口!方法解析、描述符协议(soproperty)、继承等都由常规机制处理:

In [1]: class View:
   ...:     def __init__(self, obj):
   ...:         self._obj = obj
   ...:     def __getattr__(self, attr):
   ...:         return getattr(self._obj, attr)
   ...:

In [2]: class UrFoo:
   ...:     def __init__(self, value):
   ...:         self.value = value
   ...:     def foo(self):
   ...:         return self.value
   ...:

In [3]: class Foo(UrFoo):
   ...:     def frognicate(self):
   ...:         return self.value * 42
   ...:     @property
   ...:     def baz(self):
   ...:         return 0
   ...:

In [4]: foo = Foo(8)

In [5]: view = View(foo)

In [6]: view.foo()
Out[6]: 8

In [7]: view.frognicate()
Out[7]: 336

In [8]: view.baz
Out[8]: 0

相关问题 更多 >

    热门问题