15

Python では、親クラスのインスタンスから子クラスのインスタンスを直接構築したいと考えています。例えば:

A = Parent(x, y, z)
B = Child(A)

これは私がうまくいくかもしれないと思ったハックです:

class Parent(object):

    def __init__(self, x, y, z):
        print "INITILIZING PARENT"
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):

    def __new__(cls, *args, **kwds):
        print "NEW'ING CHILD"
        if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>":
            new_args = []
            new_args.extend([args[0].x, args[0].y, args[0].z])
            print "HIJACKING"
            return Child(*new_args)
        print "RETURNING FROM NEW IN CHILD"
        return object.__new__(cls, *args, **kwds)

しかし、私が走るとき

B = Child(A) 

私は得る:

NEW'ING CHILD  
HIJACKING  
NEW'ING CHILD  
RETURNING FROM NEW IN CHILD  
INITILIZING PARENT  
Traceback (most recent call last):  
  File "classes.py", line 52, in <module>  
    B = Child(A)  
TypeError: __init__() takes exactly 4 arguments (2 given) 

ハックは期待どおりに機能しているようですが、コンパイラは最後に TypeError をスローします。TypeError をオーバーロードして B = Child(A) イディオムを無視できるかどうか疑問に思っていましたが、その方法がわかりませんでした。いずれにせよ、インスタンスから継承するための解決策を教えてください。

ありがとう!

4

8 に答える 8

8

わかりました。それで、私が解決策を半分終えるまで、引数の静的コピーに満足していることに気づきませんでした。しかし、私はそれを無駄にしないことに決めたので、とにかくここにあります。他のソリューションとの違いは、更新された場合でも、実際には親から属性を取得することです。

_marker = object()

class Parent(object):

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

class Child(Parent):

    _inherited = ['x', 'y', 'z']

    def __init__(self, parent):
        self._parent = parent
        self.a = "not got from dad"

    def __getattr__(self, name, default=_marker):
        if name in self._inherited:
            # Get it from papa:
            try:
                return getattr(self._parent, name)
            except AttributeError:
                if default is _marker:
                    raise
                return default

        if name not in self.__dict__:
            raise AttributeError(name)
        return self.__dict__[name]

これを行うと、次のようになります。

>>> A = Parent('gotten', 'from', 'dad')
>>> B = Child(A)
>>> print "a, b and c is", B.x, B.y, B.z
a, b and c is gotten from dad

>>> print "But x is", B.a
But x is not got from dad

>>> A.x = "updated!"
>>> print "And the child also gets", B.x
And the child also gets updated!

>>> print B.doesnotexist
Traceback (most recent call last):
  File "acq.py", line 44, in <module>
    print B.doesnotexist
  File "acq.py", line 32, in __getattr__
    raise AttributeError(name)
AttributeError: doesnotexist

これのより一般的なバージョンについては、http://pypi.python.org/pypi/Acquisitionパッケージを参照してください。実際、それは場合によっては血なまぐさい解決策です。

于 2009-07-04T10:12:09.457 に答える
8

__new__クラスでChildが のインスタンスを返すと、そのインスタンスで(Child同じChild.__init__引数__new__が指定されて) 呼び出されます。明らかに、 を継承するだけであり、1 つの引数 (もう 1 つの, )Parent.__init__だけで呼び出されるのはうまくいきません。ParentA

a を作成する方法が他にない場合は、引数を 1 つ (無視する) または 3 つ (この場合は を呼び出す) 受け入れるChilda を定義できます。しかし、適切に を呼び出すだけで、 にすべてのロジックを含めることをやめて、 に含める方が簡単です!Child.__init__Parent.__init____new__Child.__init__Parent.__init__

コード例でこれを具体的にするには:

class Parent(object):

    def __init__(self, x, y, z):
        print "INITIALIZING PARENT"
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return "%s(%r, %r, %r)" % (self.__class__.__name__,
            self.x, self.y, self.z)


class Child(Parent):

    _sentinel = object()

    def __init__(self, x, y=_sentinel, z=_sentinel):
        print "INITIALIZING CHILD"
        if y is self._sentinel and z is self._sentinel:
            print "HIJACKING"
            z = x.z; y = x.y; x = x.x
        Parent.__init__(self, x, y, z)
        print "CHILD IS DONE!"

p0 = Parent(1, 2, 3)
print p0
c1 = Child(p0)
print c1
c2 = Child(4, 5, 6)
print c2
于 2009-07-04T01:11:01.553 に答える
8

これ(カプセル化)が最もクリーンな方法だと思います:

class Child(object):
    def __init__(self):
        self.obj = Parent()

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

このようにして、継承の問題に遭遇することなく、すべての親のメソッドと独自のメソッドを使用できます。

于 2013-01-06T13:22:56.430 に答える
4

Child のコンストラクター (init) を定義しないため、Parent コンストラクターが呼び出され、(new から) 渡されるのは 2 つだけですが、4 つの引数が必要です。あなたが望むものを達成するための1つの方法は次のとおりです。

class Child(Parent):
    def __init__(self, *args, **kwargs):
        if len(args) == 1 and isinstance(args[0], Parent):
            Parent.__init__(self, args[0].x, args[0].y, args[0].z)

        else:
            # do something else
于 2009-07-04T01:18:16.950 に答える
2

これが非常に古いスレッドであることは承知していますが、最近アレクサンドラと同じ課題に遭遇しました。これは私が見つけた中で最も関連性の高いトピックでした。非常に多くの属性を持つ親クラスがあり、メソッドと属性をすべて保持し、いくつかを追加し、他のものを変更/上書きすることにより、そのインスタンスに本質的に「パッチ」を適用したいと考えていました。属性は実行時にユーザーによって入力されるため、単純なサブクラスは機能しません。また、親クラスからデフォルト値を継承することもできませんでした。いろいろいじくり回した後、 を使用してそれを行うための非常にきれいな (かなりハックな) 方法を見つけました__new__。次に例を示します。

class Parent(object):
    def __init__(self):
        # whatever you want here
        self.x = 42
        self.y = 5
    def f(self):
        print "Parent class, x,y =", self.x, self.y

class Child(Parent):
    def __new__(cls, parentInst):
        parentInst.__class__ = Child
        return parentInst
    def __init__(self, parentInst):
        # You don't call the Parent's init method here
        self.y = 10
    def f(self):
        print "Child class, x,y =", self.x, self.y

c = Parent()
c.f()  # Parent class, x,y = 42 5
c.x = 13
c.f()  # Parent class, x,y = 13 5
c = Child(c)
c.f()  # Child class, x,y = 13 10

唯一の特別な部分は__class__、Child のコンストラクターで Parent クラスの属性を変更することです。このため、Child の__init__メソッドは通常どおり呼び出され、私の知る限り、Child クラスは他の継承されたクラスとまったく同じように機能するはずです。

于 2012-08-15T18:09:21.210 に答える
1

ありがとう、みんな、それは速かったです!私は最初にアレックスのコメントを読み、子供のコメントを次の__init__ように書き直しました

def __init__(self, *args, **kwds):
    if len(args) == 1 and str(type(args[0])) == "<class '__main__.Parent'>":
        new_args = [args[0].x, args[0].y, args[0].z]
        super(Child, self).__init__(*new_args, **kwds)
    else:
        super(Child, self).__init__(*args, **kwds)

これは abhinavg が提案したものと非常によく似ています (私が見つけたように)。そして、それは機能します。彼とアルスのラインだけ

if len(args) == 1 and isinstance(args[0], Parent):

私よりきれいです。

再度、感謝します!!

于 2009-07-04T01:37:25.977 に答える