web-dev-qa-db-ja.com

派生クラスは自動的に基本クラスのすべての属性を持っていますか?

これに関する優れたオンラインドキュメントはないようです。派生クラスを作成すると、基本クラスのすべての属性が自動的に得られますか?しかし、BaseClass.__init()は何のためのものですか、他の基本クラスのメソッドに対しても行う必要がありますか? BaseClass.__init__()には引数が必要ですか?基本クラス__init__()の引数がある場合、それらも派生クラスで使用されますか?派生クラスの__init__()に引数を明示的に設定する必要がありますか、それともBaseClass.__init__()

25
liamel1i

BaseClassから派生したクラスに__init__を実装すると、継承された__init__メソッドが上書きされるため、BaseClass.__init__が呼び出されることはありません。 BaseClassの__init__メソッドを呼び出す必要がある場合(通常そうです)、それを行うのはあなた次第であり、通常は新しく実装されたBaseClass.__init__メソッド内から__init__を呼び出すことによって明示的に行われます。

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

    def do_something(self):
        print self.a

class Bar(Foo):
    def __init__(self):
        self.b = 20

bar = Bar()
bar.do_something()

これにより、次のエラーが発生します。

AttributeError: 'Bar' object has no attribute 'a'

したがって、do_somethingメソッドは期待どおりに継承されましたが、このメソッドではa属性を設定する必要があります。これは、__init__も上書きされたためではありません。これを回避するには、Foo.__init__内から明示的にBar.__init__を呼び出します。

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

    def do_something(self):
        print self.a

class Bar(Foo):
    def __init__(self):
        Foo.__init__(self)
        self.b = 20

bar = Bar()
bar.do_something()

期待どおりに10を出力します。この場合のFoo.__init__は、Foo(慣例によりselfと呼ばれます)のインスタンスである単一の引数を予期します。

通常、クラスのインスタンスでメソッドを呼び出すと、クラスインスタンスが最初の引数として自動的に渡されます。クラスのインスタンスのメソッドは、bound methodと呼ばれます。 bar.do_somethingは、バインドされたメソッドの例です(引数なしで呼び出されることに注意してください)。 Foo.__init__は、Fooの特定のインスタンスにアタッチされていないため、非バインドメソッドであり、最初の引数、Fooのインスタンスを明示的に渡す必要があります。

この例では、selfFoo.__init__に渡します。これは、Bar__init__メソッドに渡されたBarのインスタンスです。 BarFooを継承するため、BarのインスタンスはFooのインスタンスでもあるため、selfFoo.__init__に渡すことができます。

継承元のクラスでは、クラスのインスタンスだけではなく、より多くの引数が必要または受け入れられる場合が考えられます。これらは、__init__内から呼び出すメソッドと同様に処理されます。

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

    def do_something(self):
        print self.a

class Bar(Foo):
    def __init__(self):
        Foo.__init__(self, 20)

bar = Bar()
bar.do_something()

これは20を出力します。

継承クラスを通じて基本クラスのすべての初期化引数を完全に公開するインターフェイスを実装しようとしている場合は、明示的にそれを行う必要があります。これは通常、* argsおよび** kwargs引数(名前は慣例による)で行われます。これらは明示的に名前が付けられていない残りのすべての引数のプレースホルダーです。次の例では、これまでに説明したすべてを利用しています。

class Foo(object):
    def __init__(self, a, b=10):
        self.num = a * b

    def do_something(self):
        print self.num

class Bar(Foo):
    def __init__(self, c=20, *args, **kwargs):
        Foo.__init__(self, *args, **kwargs)
        self.c = c

    def do_something(self):
        Foo.do_something(self)
        print self.c


bar = Bar(40, a=15)
bar.do_something()

この場合、引数cは、Bar.__init__の最初の引数であるため、40に設定されます。次に、2番目の引数が変数argsおよびkwargsに組み込まれ(*および**は、関数/メソッドに渡すときにリスト/タプルまたはディクショナリを個別の引数に展開することを示す特定の構文です)、Foo.__init__に渡されます。

この例は、any上書きされたメソッドが必要な場合は明示的に呼び出す必要があることも示しています(この場合、do_somethingがあるため)。

最後に、ChildClassメソッドを明示的に呼び出す代わりに、super(ChildClass, self).method()BaseClassは任意の子クラス)を使用することがよくあります。 superについての議論は、まったく別の質問ですが、これらのケースでは、通常、BaseClass.method(self)を呼び出すことによって行われていることを正確に行うために使用されています。簡単に言うと、superは、メソッド呼び出しをメソッド解決順序の次のクラスであるMRO(単一継承では親クラス)に委譲します。詳細は superのドキュメント を参照してください。

60
Henry Gomersall

派生クラスを作成すると、基本クラスのすべての属性が自動的に含まれますか?

クラス属性、はい。派生クラスに__init__がない場合を除いて、インスタンス属性はありません(単にクラスが作成されたときに存在しないためです)。その場合、基本クラスが代わりに呼び出され、インスタンス属性が設定されます。

BaseClass .init()には引数が必要ですか?

クラスとその__init__署名によって異なります。派生クラスで明示的にBase.__init__を呼び出す場合は、少なくとも最初の引数としてselfを渡す必要があります。あなたが持っている場合

class Base(object):
    def __init__(self):
        # something

次に、__init__が他の引数を受け入れないことは明らかです。あなたが持っているなら

class Base(object):
    def __init__(self, argument):
        # something

次に、base __init__を呼び出すときにargumentを渡す必要があります。ここにはロケット科学はありません。

基本クラスの引数がある場合init()、それらは派生クラスでも使用されますか?引数を派生クラスのinit()に明示的に設定する必要がありますかまたは、それらをBaseClassに設定します。init()代わりに?

この場合も、派生クラスに__init__がない場合は、代わりに基本クラスが使用されます。

class Base(object):
    def __init__(self, foo):
        print 'Base'

class Derived(Base):
    pass

Derived()   # TypeError
Derived(42) # prints Base

それ以外の場合は、どうにかして対処する必要があります。 *args, **kwargsを使用して、引数を変更せずに基本クラスに渡すか、基本クラスの署名をコピーするか、他の場所から引数を提供するかは、達成しようとしていることに依存します。

11
Cat Plus Plus