pythonのOOP インスタンス変数の継承

大きな勘違いでした。追記までは、間違いです。追記部分参照

はまった。(cygwin python2.5.1)

いろいろ試してた現時点での結論は「pythonでは、コンストラクタを省略して継承したら、インスタンス変数は継承されなくなる」なんだけど、本当にそうゆうものなんだろうか?

#! /usr/bin/python
# -*- coding: cp932 -*-
class Tag(object):
def __init__(self, children = [], attrs = {}):
self.children = children
def addChild(self, child):
self.children.append(child)
class Html(Tag):
pass
html1 = Html()
html2 = Html()
html1.addChild("child of html1")    # Htmlのクラス変数へappendされてしまう。
html2.addChild("child of html2")    # Htmlのクラス変数へappendされてしまう。
print html1.children
# => ['child of html1', 'child of html2'] ..........  orz.
print html2.children
# => ['child of html1', 'child of html2'] ..........  orz.

思ったことなど

  • pythonのクラスのインスタンス変数はコンストラクタなどでアクセスした時に作られるらしい。
  • しかも、Html()が呼ばれたときに実行される Tag.__init__ も Htmlクラスのメソッドとしては実行されないみたい。
  • だから、コンストラクタを省略して継承してもインスタンス変数は、引き継がれない。
  • ↓のように、明示的にコンストラクタを作って、インスタンス変数にしたい変数を作って初期化してあげないとインスタンス変数ができない。インスタンス変数ができないとクラス変数になってしまう。(あまり良くわかってないかも)
class Html(Tag):
def __init__(self, children = [], attrs = {}):
self.children = children
  • これじゃ、全然嬉しくない。というか、これが本当ならpythonで真面目にOOPするのは辛い気がするんですが、何か大きな勘違いをしてるような気もしてます。

python の oop のいい入門ページって無いですかね。。。

追記 大きな勘違いでした。

速攻で、コメントを貰いました。ありがとうございます。

重要な警告: デフォルト値は 1 度だけしか評価されません。デフォルト値がリストや辞書のような変更可能なオブジェクトの時にはその影響がでます。例えば以下の関数は、後に続く関数呼び出しで関数に渡されている引数を累積します:

def f(a, L=[]):
L.append(a)
return L
print f(1)
print f(2)
print f(3)
このコードは、
[1]
[1, 2]
[1, 2, 3]
を出力します。
後続の関数呼び出しでデフォルト値を共有したくなければ、代わりに以下のように関数を書くことができます:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.7.1 デフォルトの引数値

まさに、これでした。

何気なしに使ってみたデフォルト引数の機能で、重要な警告がありました。デフォルト引数の値が、変更可能なオブジェクトの場合、そのまま使っちゃだめということを教えていただきました。

修正版

#! /usr/bin/python
# -*- coding: cp932 -*-
class Tag(object):
def __init__(self, children = None, attrs = None):
if children is None:
children = []
self.children = children
def addChild(self, child):
self.children.append(child)
class Html(Tag):
pass
html1 = Html()
html2 = Html()
html1.addChild("child of html1")    # ちゃんとhtml1のインスタンスに追加されるようになった。
html2.addChild("child of html2")    # ちゃんとhtml2のインスタンスに追加されるようになった。
print html1.children
# => ['child of html1']
print html2.children
# => ['child of html2']

2件のコメント

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


The reCAPTCHA verification period has expired. Please reload the page.

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください