スポンサーサイト

    上記の広告は1ヶ月以上更新のないブログに表示されています。
    新しい記事を書く事で広告が消せます。

    スーパーを利用しよう!!

     もちろん生鮮食品とか売ってるスーパーの話じゃないですよ☆(ゝω・)vキャピ
     でもそっちのスーパーも利用した方が経済的だから、やっぱりそっちのスーパーも利用しましょ~☆


     今回は継承・特に多重継承のお話です。
     
     Pythonではクラスの多重継承をサポートしています。あんまりやり過ぎると訳ワカメになるんで乱用は禁物ですが、効果的に使えばかなりコードを簡略化できるスグレモノですね!

     今まではなんとな~くとしか使ってなかったんですが、PySideの多重継承で躓いたので一度初心に返ってちゃんと検証してみようかと。
     まずは基礎から。
    class A( object ):
        def __init__( self ):
            super( A, self ).__init__()
            print( 'Called : A.__init__' )
    
    
    class B( object ):
        def __init__( self ):
            super( B, self ).__init__()
            print( 'Called : B.__init__' )
    
    class C( A, B ):
        def __init__( self ):
            super( C, self ).__init__()
            print( 'Called : C.__init__' )
    
    c = C()
    
    # Called : B.__init__
    # Called : A.__init__
    # Called : C.__init__
    
     各クラスの__init__部分でそれぞれ何を__init__したのかをプリントしただけのシンプルなものです。
     __init__はコンストラクタではないらしいので、あえて__init__と呼ばせてもらおう!!
    Python の __new__ や __init__ はコンストラクタではない
    気になる方は↑を見てネ♥ とってもわかりやすく解説されております。

     さて話がそれましたが、上のコードを見るとどんな順番で__init__が処理されているのかがわかりますね。
     基本的にPythonでは継承クラスを指定している部分の右から処理していっているようです非常にシンプルで分かりやすい。


     右から処理していっているとどうなるかと言うのを次の例で見てみましょう。
    class A( object ):
        def __init__( self ):
            super( A, self ).__init__()
            self.test = "A"
            print( 'Called : A.__init__' )
    
    
    class B( object ):
        def __init__( self ):
            super( B, self ).__init__()
            self.test = 'B'
            print( 'Called : B.__init__' )
    
    class C( A, B ):
        def __init__( self ):
            super( C, self ).__init__()
            print( 'Called : C.__init__' )
    
    c = C()
    print( c.test )
    
    # Called : B.__init__
    # Called : A.__init__
    # Called : C.__init__
    # A
    
     クラスAとクラスBでそれぞれクラス内変数testを定義しています。
     そしてA、Bから継承したクラスCのインスタンスからtest変数を呼ぶと
    A
    が返ってきました。

     つまりPythonにおける多重継承では、同じクラス内変数が定義されている場合左にあるクラスが優先されるようです。
     これはメソッドでも同様です。(まぁPythonでは基本的にみんなオブジェクトなんで、変数だろうが関数だろうが関係のない、当たり前の話ですが・・・)
    class A( object ):
        def __init__( self ):
            super( A, self ).__init__()
            print( 'Called : A.__init__' )
        def testFunc( self ):
            print( 'The function of A' )
    
    class B( object ):
        def __init__( self ):
            super( B, self ).__init__()
            print( 'Called : B.__init__' )
        def testFunc( self ):
            print( 'The function of B' )
            
    
    class C( A, B ):
        def __init__( self ):
            super( C, self ).__init__()
            print( 'Called : C.__init__' )
    
    c = C()
    c.testFunc()
    
    
    # Called : B.__init__
    # Called : A.__init__
    # Called : C.__init__
    # The function of A
    
     こんな感じ。

     因みに__init__はコンストラクタではないと言う話を書きましたが、こんなソースコードにするとここでもその理由がわかっちゃいますね。
    class A( object ):
        def __init__( self ):
            super( A, self ).__init__()
            print( 'Called : A.__init__' )
            self.testFunc()
        def testFunc( self ):
            print( 'The function of A' )
    
    class B( object ):
        def __init__( self ):
            super( B, self ).__init__()
            print( 'Called : B.__init__' )
            self.testFunc()
        def testFunc( self ):
            print( 'The function of B' )
            
    
    class C( A, B ):
        def __init__( self ):
            super( C, self ).__init__()
            print( 'Called : C.__init__' )
    
    c = C()
    
    
    # Called : B.__init__
    # The function of A
    # >
    # Called : A.__init__
    # The function of A
    # >
    # Called : C.__init__
    # >
    
     全部クラスAのtestFuncメソッドの内容で呼ばれてます。また、関数のアドレスもみんな同一のものをさしていて__main__Cのオブジェクトだと言っているので、継承先の__init__を呼んでいる時にはすでに継承が終了している状態(つまりコンストラクト終了済み!)ですので注意が必要。
    以下のような例だとエラーが起こります。
    class A( object ):
        def __init__( self ):
            super( A, self ).__init__()
            self.setup()
        def setup( self ):
            self.testA = 'A'
    
    class B( object ):
        def __init__( self ):
            super( B, self ).__init__()
            self.setup()
        def setup( self ):
            self.testB = 'B'
            
    
    class C( A, B ):
        def __init__( self ):
            super( C, self ).__init__()
    
    c = C()
    c.testB
    
    # Error: AttributeError: file  line 1: 'C' object has no attribute 'testB' #
    
     ↑のようにクラスメソッドでクラス変数を定義するような場合、右側の同名メソッドは無視されます。


     う~ん、深い! 改めて検証してみるとPythonの多重継承は非常にシンプルなルールに則っていますな。ちゃんとルールがわかればそんなにややこしくはないんですが、あやふやのままだと思わぬエラーを巻き起こすので注意が必要ですね~。
     最後に、同名メソッドで定義すると右側が無視されますが、無視されないためにはどうすればよいか?
    一応の解決策を・・・(たぶんあんまり推奨された書き方ではないと思うので、そもそもこんな構造にならないように注意しないといけないっすね)
    class A( object ):
        def __init__( self ):
            super( A, self ).__init__()
            self.setup()
        def setup( self ):
            self.testA = 'A'
    
    class B( object ):
        def __init__( self ):
            super( B, self ).__init__()
            B.setup( self )
        def setup( self ):
            self.testB = 'B'
            
    
    class C( A, B ):
        def __init__( self ):
            super( C, self ).__init__()
    
    c = C()
    c.testB
    
     こんな感じでクラスBの__init__内で
    B.setup( self )
    
    みない事をしちゃいます。
    う~ん、全然エレガントじゃない・・・ でも、改めて考えてみると、こうしないといけなくなりそうなクラスを過去の自分は作ってしまってる気がする・・・(((((((( ;゚Д゚))))))))ガクガクブルブルガタガタブルブル





    気をつけよう・・・
    スポンサーサイト

    コメントの投稿

    非公開コメント

    プロフィール

    Eske

    Author:Eske
    萌えイラストレーターを目指す3DCGイラストレーター。
    現在ポケモンカードゲーム、ガンダムトライエイジ、ガンダムコンクエスト、妖怪ウォッチとりつきカードゲームなどで3DCGを使用したイラストレーターとして参加中。

    主にここでは日々気づいたメモなんかを残してます。
    イラストのお仕事も受け付けております。ココのメールアドレスからご連絡できますので、お気軽にご相談下さい。

    最新記事
    最新コメント
    カテゴリ
    最新トラックバック
    月別アーカイブ
    検索フォーム
    リンク
    QRコード
    QR
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。