【问题标题】:Convert superclass instance to subclass instance将超类实例转换为子类实例
【发布时间】:2015-05-29 21:11:51
【问题描述】:
  • 我有一个无法触摸的外部库。这个库有一个函数 genA(),它返回类 A 的实例。
  • 在我这边,我将 B 类定义为 A 类的子类。
  • 我想在我的项目中使用B类的实例,但是该实例应该由genA()生成。

是否有任何标准且简单的方法可以做到这一点?


# I cannnot tweak these code

def genA():
    a = A
    return(a)

class A:
    def __init__():
        self.a = 1

# ---

# code in my side

class B(A):
    def __init__():
        self.b = 2


a = genA()
# like a copy-constructor, doesn't work
# b = B(a)

# I want to get this
b.a # => 1
b.b # => 2

这是一个等效的 c++ 代码:

#include <iostream>

// library side code
class A {
public:
  int a;
  // ... many members

  A() { a = 1; }
};

void fa(A a) {
  std::cout << a.a << std::endl;
}

A genA() { A a; return a; }

// ///
// my code

class B : public A {
public:
  int b;
  B() : A() { init(); }
  B(A& a) : A(a) { init(); }
  void init() { b = 2; }
};

void fb(B b) {
  std::cout << b.b << std::endl;
}


int main(void) {
  A a = genA();
  B b(a);

  fa(b); // => 1
  fb(b); // => 2
}

【问题讨论】:

  • 通过极其笨拙的方法可以做到这一点,但我不推荐它。您不能将A 对象粘贴在某种包装器中而不是尝试更改其类吗?
  • 谢谢,实际上我需要将对象传递给库端函数和我的函数,因此该对象在某种意义上应该是 A 的实例。但现在我明白了困难(令我惊讶!),我将采取另一种方法。也许定义具有类 A 实例作为其成员的类 B。当我需要将它传递给库端函数时,调用a = genA(); B b; b.a_instance =a; lib_fun(b.a_instance)。你觉得有道理吗?
  • 我不确定它的效果如何,但似乎可行。

标签: python class python-2.7 inheritance function


【解决方案1】:

不应该使用__new__,但它只适用于新式类:

class A(object):
    def __init__(self):
        self.a = 10

class B(A):
    def __new__(cls, a):
        a.__class__ = cls
        return a

    def __init__(self, a):
        self.b = 20

a = A()
b = B(a)

print type(b), b.a, b.b   # <class '__main__.B'> 10 20

但正如我所说,不要那样做,在这种情况下,您可能应该使用聚合,而不是子类化。如果你想让BA有相同的接口,你可以用__getattr__写透明代理:

class B(object):
    def __init__(self, a):
        self.__a = a
        self.b = 20

    def __getattr__(self, attr):
        return getattr(self.__a, attr)

    def __setattr__(self, attr, val):
        if attr == '_B__a':
            object.__setattr__(self, attr, val)

        return setattr(self.__a, attr, val)

【讨论】:

  • 谢谢,如果A类有很多成员,我是否需要为所有成员编写一个setter/getter...?
  • @kohske,不,你没有。每次你尝试获取b.a,Python 找不到a 属性,所以它请求你的__getattr__ 帮助,并将"a" 作为attr 参数传递,所以__getattr__ 处理@ 的所有属性987654333@类。
  • 谢谢,很有用。请问为什么我不应该使用new
  • @kohske:重新定义__class__ 是有效的,但非常不寻常的技术。它会混淆其他开发人员,并可能导致无法预料的后果(即在多重继承中,您可能还需要覆盖 MRO)。此外,它与我所知道的任何设计模式都不匹配。
  • @myaut,不应该是def __setattr__(self, attr, val): if attr == '_B__a': object.__setattr__(self, attr, val); else: return setattr(self.__a, attr, val);否则 Python 不会在 self.__a 中查找名为 _B__a 的变量吗?
【解决方案2】:

我不明白你的代码。 IMO 不正确。首先,在 A 和 B 中的 __init__ 中都没有 self。其次,在您的 B 类中,您没有调用 A 的构造函数。第三, genA 不返回任何对象,只是引用 A 类。请检查更改的代码:

def genA():
    a = A() #<-- need ()
    return(a)

class A:
    def __init__(self):  # <-- self missing
        self.a = 1

# ---

# code in my side

class B(A):
    def __init__(self, a=None):
        super().__init__()  #<-- initialize base class

        if isinstance(a, A): #<-- if a is instance of base class, do copying
            self.a = a.a

        self.b = 2


a = genA()
a.a = 5
b = B(a)

# this works
print(b.a) # => 5
print(b.b) # => 2   

【讨论】:

  • 谢谢,如果A班有很多成员,我需要为所有成员写self.a = a.a吗?
  • 简短回答是。长答案,你可以iterate through the variables in a loop。但我认为这超出了这个问题的范围。
  • 非常感谢。它很容易使用迭代,但我只是想知道是否有一种简单而标准的方法。非常感谢。
【解决方案3】:

似乎没有标准的方法,但有几种方法。如果您不想照顾每个单独的属性,我建议您使用以下属性:

class A(object):
    # Whatever

class B(A):
    def __init__(self, a):
        super(B, self).__init__()
        for attr in dir(a):
            setattr(self, attr, getattr(a, attr))
        # Any other specific attributes of class B can be set here

# Now, B can be instantiated this way:
a = A()
b = B(a)

如果您不想访问父级的“私有”属性,您可以添加

if not attr.startswith('__'):

for 循环中。

【讨论】:

    猜你喜欢
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-23
    • 1970-01-01
    相关资源
    最近更新 更多