【问题标题】:how to make a copy of a class in python?如何在python中复制一个类?
【发布时间】:2018-07-19 18:30:03
【问题描述】:

我有一堂课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,反之亦然。

【问题讨论】:

  • 注意:您使用的是 Python 2 这一事实在这里很重要;在 Python 3 上,您不会遇到未绑定方法的问题,因为没有这样的事情(在大多数情况下,您可以将一个函数从一个类复制到另一个类而不会抱怨,当您将它与新类一起使用时,例外情况是其中使用了 no-arg super(),这要归功于使 no-arg super() 起作用所涉及的一些魔法)。
  • 不要使用dir,只需复制整个vars(A)
  • @juanpa.arrivillaga:这也可能绕过导致未绑定方法问题的描述符协议问题。不过您需要使用vars(A).copy(),因此您可以在生成的dict 中编辑'a' 的值,而无需更改A(您实际上无法在不复制的情况下更改结果,因为它是@ 987654340@/mappingproxy 禁止重新分配)。
  • @ShadowRanger 我在使用A_attrs = {attr:getattr(A,attr) for attr in vars(A)}时遇到了同样的错误
  • @springcc 不要使用getattr,使用B = type("B", (object,), dict(vars(A)))。请注意,请注意每个班级都有一个__dict__,他们可能有__slots__,在这种情况下,您需要手动复制它。

标签: python metaprogramming python-2.x metaclass


【解决方案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

# 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

正如预期的那样。

【讨论】:

    【解决方案2】:

    出于某种原因,您可能正在尝试 (1) 为某些实际应用创建类的副本: 在这种情况下,请尝试使用copy.deepcopy - 它包含复制类的机制。如果需要,只需在之后更改 copy __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 后代的行为可能会有所不同。

    【讨论】:

      猜你喜欢
      • 2021-01-06
      • 2012-09-29
      • 2018-04-04
      • 2020-06-28
      • 1970-01-01
      • 2018-07-04
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      相关资源
      最近更新 更多