親クラスで定義した関数内において、子クラスでimportしたmoduleを使いたい!!!私はPythonを完全に理解していなかった!!!
結論
子クラスの適切なタイミング(__init__()
時など)で、インスタンス変数にモジュールを入れてあげる。
とある日
下記のようなコードを打っていた。
親クラス(Parent
)が持っている関数内(parent_method()
)で、子クラス(ChildFirst
or ChildSecond
)でimport
したmodule
(child_first_module
or child_second_module
)の関数を使いたい。
目的は子クラスで import
した module
によって親クラスのparent_method()
の 処理を変えたかった。
parent_method()
は子クラスにおいて共通の処理があり、クラスによる差分はモジュール側で吸収するので親クラスで定義したい。
Pythonのバージョンは3.7.2です。
$ python --version Python 3.7.2
ディレクトリ構成
┣child_first.py ┣child_first_module.py ┣child_second.py ┣child_second_module.py ┣main.py ┣parent.py
ソースコードの中身
parent.py
ChildFirst
及び、ChildSecond
が継承する親元のクラス。
parent_method()
を実行すると、child_module
として import
しているモジュールの関数を実行できるようにしている。
class Parent(): def __init__(self): # 親クラスは抽象的なクラスのため直接__init__()しない pass def parent_method(self): child_module().say()
child_first.py
子クラスその1。
子クラスモジュールを import
している。
from parent import Parent from child_first_module import ChildModule1 as child_module class ChildFirst(Parent): def __init__(self): print("I am Child First Class")
child_first_module.py
子クラス1 で import
されるモジュールその1。
say()
関数を持っている。Parentクラス
内のparent_method()
内で呼ばれることを想定。
class ChildModule1(): def __init__(self): print('child module 1') def say(self): print('I am child module 1')
child_second.py
子クラスその2。以下同文。
from parent import Parent from child_second_module import ChildModule2 as child_module class ChildSecond(Parent): def __init__(self): print("I am Child Second Class")
child_second_module.py
子クラス2 で import
されるモジュールその2。以下同文。
class ChildModule2(): def __init__(self): print('child module 2') def say(self): print('I am child module 2')
main.py
単純に子クラスその1、その2を呼び出すだけのファイル。
from child_first import ChildFirst from child_second import ChildSecond child_first = ChildFirst() child_first.parent_method() child_second = ChildSecond() child_second.parent_method()
実行結果
子クラスで import
しているのだから、親クラスの持っている関数内でモジュールの関数を使えるだろうと main.py
を実行したところ、エラー。
main.py
の 子クラスが呼び出した親元クラスの parent_method()
実行時にエラーが起きている。
$ python main.py child module 1 I am Child First Class Traceback (most recent call last): File "main.py", line 5, in <module> child_first.parent_method() File "/Users/xxxxx/python/Python_Practice/module_import/parent.py", line 6, in parent_method child_module.say() NameError: name 'child_module' is not defined
てっきり小クラスで import
した module
を 親クラスで定義した関数から呼べると思っていました。
親クラスを継承した関数って、子クラスで定義したと同義のイメージだったので特に違和感なく、コードをリファクタリングしていました。
一緒に働いている同僚と一緒に雰囲気でいけると思い込んでおり、あれれ〜??となっていました。
私はPythonを完全に理解していなかった・・・・
試行錯誤した結果: 子クラスの適切なタイミングでインスタンス変数に入れる
子クラスの適切なタイミングで、子クラスのインスタンス変数にモジュールを代入してあげる。
親クラスからはself
をつけて呼び出してあげる。
今回は__init__()
時にインスタンス変数に入れてあげるようにしました。
parent.py
ChildFirst
及び、ChildSecond
が継承する親元のクラス。
parent_method()
を実行すると、child_module
として import
しているモジュールの関数を実行できるようにしている。
インスタンス変数に入っているのでself
を追加。
class Parent(): def __init__(self): # 親クラスは抽象的なクラスのため直接__init__()しない pass def parent_method(self): self.child_module().say() # ★変更部分!!★
child_first.py
子クラスその1。
子クラスモジュールを import
している。__init__()
時にインスタンス変数を追加。
from parent import Parent from child_first_module import ChildModule1 as child_module class ChildFirst(Parent): def __init__(self): self.child_module = child_module() # ★追加部分!!★ print("I am Child First Class")
child_second.py
子クラスその2。以下同文。
from parent import Parent from child_second_module import ChildModule2 as child_module class ChildSecond(Parent): def __init__(self): self.child_module = child_module() # ★追加部分!!★ print("I am Child Second Class")
実行結果
$ python main.py child module 1 I am Child First Class I am child module 1 child module 2 I am Child Second Class I am child module 2
そうそう。やりたかったのはこの挙動。 本当にこれでいいのか?という説はあるが・・・ インスタンス変数として定義してあげれば、インスタンスからアクセスはできるので、これでいい気がする。
子クラスでimport
したmodule
はインスタンス変数に入れずに呼び出すことができるので、親クラスから呼び出す時も同じ感覚でいたのが勘違いの原因かなぁ。
何か挙動で変なところが出たり、今後のクラス設計で足を引っ張らなければいいのだが・・・
まとめ
なんかPythonの基本的なところでつまづいてしまっている気がする。 Pythonの基礎本(錦蛇のオライリー本)、カッコつけてカナダに行くインターン生にあげちゃったんだよな・・・苦笑
しかし、これで・・・ Python 完全に理解した!!(嘘