如何制作Python类的深层副本?

时间:2016-06-06 23:16:54

标签: python python-2.7 monkeypatching

我想制作一个类的副本,同时更新所有方法以引用一组新的__globals__

我在考虑如下所示,但与types.FunctionType不同,types.UnboundMethodType的构造函数不接受__globals__,是否有任何建议如何解决此问题?

def copy_class(old_class, new_module):
  """Copies a class, updating __globals__ of all methods to point to new_module"""

  new_dict = {}
  for name, entry in old_class.__dict__.items():
    if isinstance(entry, types.UnboundMethodType):
      entry = types.UnboundMethodType(name, None, old_class.__class__, globals=new_module.__dict__)
    new_dict[name] = entry
  return type(old_class.name, old_class.__bases__, new_dict)

2 个答案:

答案 0 :(得分:1)

__dict__值是函数,而不是未绑定的方法。仅在属性访问时创建未绑定的方法对象。如果 __dict__中看到未绑定的方法对象,那么在此函数到达之前,类对象会发生奇怪的事情。

答案 1 :(得分:1)

我不了解你,但我一般不喜欢使用types来进行类型检查以外的任何事情(我不经常这样做;-)。我更愿意inspect ...

我必须在这段代码前加上说我希望你有一个非常好的理由想要这样做;-) - 对我来说,似乎只是继承和覆盖类属性应该更优雅地完成工作...但是,如果你真的想要复制一个类 - 为什么不在新命名空间中再次执行它的源?

我将以下简单模块放在一起:

# test.py
# Just some test data
FOO = 1

class Bar(object):
  def subclass_method(self):
    print('Hello World!')

class Foo(Bar):
  def method(self):
    return FOO

然后做一些繁重的工作:

import sys
import inspect

def copy_class(cls, new_globals):
    source = inspect.getsource(cls)
    globs = {}
    globs.update(sys.modules[cls.__module__].__dict__)
    globs.update(new_globals)
    exec source in globs
    return globs[cls.__name__]


# Check that it works...
import test

NewFoo = copy_class(test.Foo, {'FOO': 2})

print NewFoo().method()
NewFoo().subclass_method()

print test.Foo().method()
test.Foo().subclass_method()

这有一些可能需要的属性和不受欢迎的......首先,它只适用于可检查的类。这几乎是用户定义的任何东西,所以可能不会限制......它也可能比其他不涉及重新解析源字符串的解决方案慢一点 - 但同样,它没有看起来这应该经常执行,所以这可能是好的。

现在的“优势”......

  1. 如果函数请求但未提供全局,则将使用旧命名空间中的全局。如果这种行为不合适(即你宁愿拥有NameError),你可以轻松修改该功能以将其删除。
  2. “副本”不会继承原件。在大多数情况下,这可能并不重要,但是从原始内容中继承某些东西的副本有点奇怪......
  3. 有些人可能会在这里看到exec并立刻想到“哦不!exec!?!?!世界即将结束!!!”。弗兰基,这是一个很好的默认回应。但是,我认为如果你要复制一个你打算稍后在代码中使用的函数,它就不比使用exec更安全了(毕竟,函数的代码已经被执行了)。