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

如何制作Python类的深度副本?,python,python-2.7,monkeypatching,Python,Python 2.7,Monkeypatching,我想复制一个类,同时更新它的所有方法以引用一组新的\uuuuu globals\uuuu 我的想法如下,但与类型不同。FunctionType,类型的构造函数。UnboundMethodType不接受\uuuuuu globals\uuuu,有什么建议如何解决这个问题吗 def copy_class(old_class, new_module): """Copies a class, updating __globals__ of all methods to point to new_mo

我想复制一个类,同时更新它的所有方法以引用一组新的
\uuuuu globals\uuuu

我的想法如下,但与
类型不同。FunctionType
类型的构造函数。UnboundMethodType
不接受
\uuuuuu globals\uuuu
,有什么建议如何解决这个问题吗

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)

\u dict\u
值是函数,而不是未绑定的方法。未绑定的方法对象仅在属性访问时创建。如果您在
\uuu dict\uuuu
中看到未绑定的方法对象,那么在这个函数到达它之前,您的类对象发生了一些奇怪的事情。

我不知道您的情况,但是除了类型检查(我不经常这么做;-)之外,我一般不喜欢使用
类型。我宁愿
检查

在这段代码之前,我必须说,我希望你有一个很好的理由想这样做;-)-对我来说,似乎只是子类化和重写类属性就可以更优雅地完成这项工作。。。但是,如果您真的想复制一个类,为什么不在新的名称空间中再次执行它的源代码呢

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

# 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()
这可能有一些可取的性质和不可取的。。。首先,它只适用于可检查的类。这几乎是用户定义的任何内容,所以可能没有太多限制。。。它也可能比其他不涉及重新解析源字符串的解决方案慢一点——但是,这似乎不应该执行得太频繁,所以这可能没问题

现在的“优势”

  • 如果函数请求但未提供全局,则将使用旧命名空间中的全局。如果不希望出现这种行为(即,您希望出现
    namererror
    ),则可以轻松修改该函数以将其删除
  • “副本”不是从原件继承的。在大多数情况下,这可能并不重要,但让某个东西的副本从原始文件继承有点奇怪


  • 有些人可能会在这里看到
    exec
    ,立即想到“哦,不!
    exec
    !?!世界就要结束了!!!”。弗兰基,这是一个很好的默认响应。但是,我认为,如果您正在复制一个您计划稍后在代码中使用的函数,那么它并不比使用
    exec
    (毕竟,函数的代码已经执行)更安全

    我在dict中没有看到未绑定的方法……我是否应该像普通函数一样复制“dict”的FunctionType元素?@YaroslavBulatov:是的,因为它们是普通函数。