如何在python中创建一个类的副本?

时间:2018-02-09 03:09:37

标签: python metaprogramming python-2.x metaclass

我有一个班级A

class A(object):
    a = 1
    def __init__(self):
      self.b = 10 
    def foo(self):
      print type(self).a
      print self.b

然后我想创建一个类B,其等同于A,但具有不同的类成员a的名称和值:

这就是我的尝试:

  class A(object):
    a = 1 
    def __init__(self):
      self.b = 10
    def foo(self):
      print type(self).a
      print self.b

  A_dummy = type('A_dummy',(object,),{})
  A_attrs = {attr:getattr(A,attr)  for attr in dir(A) if (not attr in dir(A_dummy))}

  B = type('B',(object,),A_attrs)
  B.a = 2 

  a = A() 
  a.foo()

  b = B() 
  b.foo()

但是我得到了一个错误:

  File "test.py", line 31, in main
    b.foo()
TypeError: unbound method foo() must be called with A instance as first argument (got nothing instead)

那么我如何应对这类工作(创建一个存在类的副本)?也许需要一个元类?但我更喜欢的只是一个函数FooCopyClass,这样:

B = FooCopyClass('B',A)
A.a = 10
B.a = 100

print A.a # get 10  as output
print B.a # get 100 as output

在这种情况下,修改B的班级成员不会影响A,反之亦然。

2 个答案:

答案 0 :(得分:1)

你遇到的问题是在Python 2类上查找方法属性会创建一个未绑定的方法,它不返回底层的原始函数(在Python 3上,取消了未绑定的方法,以及你是什么尝试会工作得很好)。您需要绕过从函数转换为未绑定方法的描述符协议机制。最简单的方法是使用vars直接获取类的属性字典:

# Make copy of A's attributes
Bvars = vars(A).copy()
# Modify the desired attribute
Bvars['a'] = 2
# Construct the new class from it
B = type('B', (object,), Bvars)

同样,您可以一步复制并初始化B,然后在B.a之后重新分配:{/ p>

# Still need to copy; can't initialize from the proxy type vars(SOMECLASS)
# returns to protect the class internals
B = type('B', (object,), vars(A).copy())
B.a = 2

或略微非惯用的单行乐趣:

B = type('B', (object,), dict(vars(A), a=2))

无论哪种方式,当你完成时:

B().foo()

将输出:

2
10

正如所料。

答案 1 :(得分:1)

你可能会尝试(1)为某些真正的应用程序创建类的副本: 在这种情况下,尝试使用copy.deepcopy - 它包括复制类的机制。如果需要,请稍后更改副本__name__属性。适用于Python 2或Python 3。

(2)尝试学习和理解Python内部类组织:在这种情况下,没有理由与Python 2作斗争,因为Python 3修复了一些问题。

在任何情况下,如果您尝试使用dir来获取类属性,那么最终会得到比您想要的更多 - 因为dir还会检索所有超类的方法和属性。因此,即使您的方法有效(在Python 2中,这意味着获取检索到的未绑定方法的.im_func属性,要在创建新类时用作原始函数),您的类将拥有比原始方法更多的方法之一。

实际上,在Python 2和Python 3中,复制类__dict__就足够了。如果您希望不共享类属性的可变对象,则应再次使用deepcopy。在Python 3中:

class A(object):
    b = []
    def foo(self):
      print(self.b)

from copy import deepcopy

def copy_class(cls, new_name):
   new_cls = type(new_name, cls.__bases__, deepcopy(A.__dict__))
   new_cls.__name__ = new_name
   return new_cls

在Python 2中,它的工作方式几乎相同,但没有方便的方法来获得现有类的显式基础(即未设置__bases__)。您可以使用__mro__获得相同的效果。唯一的事情是所有祖先类都以硬编码顺序作为新类的基础传递,并且在复杂的层次结构中,如果多个 - B后代和A后代的行为之间可能存在差异。使用继承。