【问题标题】:How to make deep copy of Python class?如何制作 Python 类的深拷贝?
【发布时间】:2016-06-06 23:16:53
【问题描述】:

我想复制一个类,同时更新它的所有方法以引用一组新的__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)

【问题讨论】:

    标签: python python-2.7 monkeypatching


    【解决方案1】:

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

    【讨论】:

    • 我没有在我的 dict 中看到未绑定的方法......我应该像常规函数一样复制“dict”的 FunctionType 元素吗?
    • @YaroslavBulatov:是的,因为它们是常规函数。
    【解决方案2】:

    我不了解你,但我通常不喜欢将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. “副本”不继承自原始文件。对于大多数目的,这可能无关紧要,但是让某些东西的副本继承自原始文件有点奇怪......

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

    【讨论】:

      猜你喜欢
      • 2012-06-01
      • 2015-01-13
      • 2017-05-13
      • 2011-04-29
      • 1970-01-01
      • 1970-01-01
      • 2013-09-07
      • 2017-09-11
      • 1970-01-01
      相关资源
      最近更新 更多