親クラスで定義した関数内において、子クラスで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 完全に理解した!!(嘘