73901


Pythonのimport動作が理解できていないようです。  
Main.pyから開始して、SubClass.pyをインポートし、そのSubClass内で、SubClass2.pyを呼び出します。  
SubClass.py内のグローバル変数OKは、その派生クラスがあるSubClass2.pyでも使用するため、インポートして変数OKに代入しています。

Main.pyおよびSubClass2.pyから開始すると、OKの代入部分で以下のエラーが発生します。  
これはなぜなのでしょうか？

**エラーメッセージ:**

[code]

    例外：AttributeErrorが発生します。
    module 'importTestSub' has no attribute 'OK'
    SubClass.pyから開始すると問題がありません。
    
[/code]

Main.py

[code]

    import importTestSub as TS
    
    class Main():
        def __init__(self):
            iSub = TS.SubClass()
            pass
    
    if __name__ == '__main__':
        Main()
    
    
[/code]

importTestSub .py

[code]

    import importTestSub2 as Tsub2
    
    OK = 'OK'
    
    class SubClass():
        def __init__(self):
            iTS2 = Tsub2.SubClass2()
            pass
    
    if __name__ == '__main__':
        SubClass()
    
    
[/code]

importTestSub2.py

[code]

    import importTestSub as Tsub
    
    OK = Tsub.OK
    
    class SubClass2(Tsub.SubClass):
        def __init__(self):
            SubsubClass()
            print(OK)
            pass
    
    class SubsubClass(SubClass2):
        def __init__(self):
            print(OK)
    
    if __name__ == '__main__':
        SubClass2()
    
    
[/code]


CC BY-SA 4.0
2021-02-09T04:56:03.153
73880
2021-02-10T01:08:58.300
2021-02-10T00:17:03.790
32891
32891
question
python python3 import
python import動作


発生したエラーの詳細は以下になります。  
問題となっているのは`circular import`ということでしょうね。

Main.pyの場合

[code]

    Traceback (most recent call last):
      File "C:\Develop\Python\qa0209\Main.py", line 1, in <module>
        import importTestSub as TS
      File "C:\Develop\Python\qa0209\importTestSub.py", line 1, in <module>
        import importTestSub2 as Tsub2
      File "C:\Develop\Python\qa0209\importTestSub2.py", line 3, in <module>
        OK = Tsub.OK
    AttributeError: partially initialized module 'importTestSub' has no attribute 'OK' (most likely due to a circular import)
    
[/code]

importTestSub2.pyの場合

[code]

    Traceback (most recent call last):
      File "C:\Develop\Python\qa0209\importTestSub2.py", line 1, in <module>
        import importTestSub as Tsub
      File "C:\Develop\Python\qa0209\importTestSub.py", line 1, in <module>
        import importTestSub2 as Tsub2
      File "C:\Develop\Python\qa0209\importTestSub2.py", line 3, in <module>
        OK = Tsub.OK
    AttributeError: partially initialized module 'importTestSub' has no attribute 'OK' (most likely due to a circular import)
    
[/code]

* * *

上記を基に、`python circular import`とか`python import
behavior`で検索すると、自分自身に同じ名前を付けていたとかありますが、今回の場合は以下のような記事が関連の説明になっていると思われます。  
[Python Circular Imports](https://stackabuse.com/python-circular-imports/)

[Buggy Python Code: The 10 Most Common Mistakes That Python Developers
Make](https://www.toptal.com/python/top-10-mistakes-that-python-programmers-
make)  
の`Common Mistake #7: Creating circular module dependencies`

[Python import: Advanced Techniques and Tips](https://realpython.com/python-
import/) ←■これが一番広範囲で詳しそうです。  
の[Handle Cyclical Imports](https://realpython.com/python-import/#handle-
cyclical-imports)

[Behavior of import in python
[duplicate]](https://stackoverflow.com/q/56842659/9014308)

> If you're worried about this sort of thing causing errors, the best thing to
> do is to not use global variables - instead, use classes and assign them
> default values when constructed.  
>
> この種のエラーの原因が心配な場合は、最善の方法はグローバル変数を使用せず、代わりにクラスを使用し、それに対して構築時にデフォルト値を割り当てることです。

[Understanding behavior of Python imports and circular
dependencies](https://stackoverflow.com/q/51639547/9014308)

* * *

例えばそれぞれのソースの随所に`print()`を挿入し、何が如何実行されているかを調べてみると、状況が見えてくるのでは？  
Main.py

[code]

    import importTestSub as TS
    print('Ma-1')
    class Main():
        print('Mc-1')
        def __init__(self):
            print('Md-1')
            iSub = TS.SubClass()
            print('Md-2')
            pass
    print('Ma-2')
    if __name__ == '__main__':
        print('Mb-1')
        Main()
        print('Mb-1')
    
[/code]

importTestSub.py

[code]

    import importTestSub2 as Tsub2
    print('S1a-1')
    OK = 'OK'
    print('S1a-2')
    class SubClass():
        print('S1c-1')
        def __init__(self):
            print('S1d-1')
            iTS2 = Tsub2.SubClass2()
            print('S1d-2')
            pass
    print('S1a-3')
    if __name__ == '__main__':
        print('S1b-1')
        SubClass()
        print('S1b-2')
    
[/code]

importTestSub2.py

[code]

    import importTestSub as Tsub
    print('S2a-1')
    OK = Tsub.OK
    print('S2a-2')
    class SubClass2(Tsub.SubClass):
        print('S2c-1')
        def __init__(self):
            print('S2d-1')
            SubsubClass()
            print('S2d-2')
            print(OK)
            print('S2d-3')
            pass
    print('S2a-3')
    class SubsubClass(SubClass2):
        print('S2e-1')
        def __init__(self):
            print('S2f-1')
            print(OK)
            print('S2f-2')
    print('S2a-4')
    if __name__ == '__main__':
        print('S2b-1')
        SubClass2()
        print('S2b-2')
    
[/code]

* * *

実行結果は以下のようになります。  
エラー時には`importTestSub2.py`の`OK =
Tsub.OK`の前の`print('S2a-1')`しか実行されていませんが、正常時には`importTestSub.py`の`print('S1x-y')`の方が先に実行されており、`OK
= 'OK'`の初期化が先に済んでいます。

[code]

    C:\Develop\Python\qa0209>py Main.py
    S2a-1
    Traceback (most recent call last):
      File "C:\Develop\Python\qa0209\Main.py", line 1, in <module>
        import importTestSub as TS
      File "C:\Develop\Python\qa0209\importTestSub.py", line 1, in <module>
        import importTestSub2 as Tsub2
      File "C:\Develop\Python\qa0209\importTestSub2.py", line 3, in <module>
        OK = Tsub.OK
    AttributeError: partially initialized module 'importTestSub' has no attribute 'OK' (most likely due to a circular import)
    
    C:\Develop\Python\qa0209>py importTestSub.py
    S1a-1
    S1a-2
    S1c-1
    S1a-3
    S2a-1
    S2a-2
    S2c-1
    S2a-3
    S2e-1
    S2a-4
    S1a-1
    S1a-2
    S1c-1
    S1a-3
    S1b-1
    S1d-1
    S2d-1
    S2f-1
    OK
    S2f-2
    S2d-2
    OK
    S2d-3
    S1d-2
    S1b-2
    
    C:\Develop\Python\qa0209>py importTestSub2.py
    S2a-1
    Traceback (most recent call last):
      File "C:\Develop\Python\qa0209\importTestSub2.py", line 1, in <module>
        import importTestSub as Tsub
      File "C:\Develop\Python\qa0209\importTestSub.py", line 1, in <module>
        import importTestSub2 as Tsub2
      File "C:\Develop\Python\qa0209\importTestSub2.py", line 3, in <module>
        OK = Tsub.OK
    AttributeError: partially initialized module 'importTestSub' has no attribute 'OK' (most likely due to a circular import)
    
[/code]

* * *

**対策としては：**

[Python Circular Imports](https://stackabuse.com/python-circular-
imports/)の「How to Fix Circular Dependencies」とか、

> In general, circular imports are the result of bad designs. A deeper
> analysis of the program could have concluded that the dependency isn't
> actually required, or that the depended functionality can be moved to
> different modules that wouldn't contain the circular reference.  
>
> 一般的に、循環輸入は悪いデザインの結果です。プログラムをより深く分析すると、依存関係は実際には必要ない、または依存する機能を循環参照を含まない別のモジュールに移動できると結論付けることができます。

[Python import: Advanced Techniques and Tips](https://realpython.com/python-
import/)の[Handle Cyclical Imports](https://realpython.com/python-
import/#handle-cyclical-imports)の終わりの方などが参考になると思われます。

> So how can you avoid being bogged down and confused by cyclical imports?
> Having two or more modules importing each other is often a sign that you can
> improve the design of your modules.
>
> Often, the easiest time to fix cyclical imports is before you implement
> them. If you see cycles in your architecture sketches, have a closer look
> and **try to break the cycles**.
>
> Still, there are times when it’s reasonable to introduce an import cycle. As
> you saw above, this isn’t a problem so long as your modules define only
> attributes, functions, classes, and so on. The second tip—which is also good
> design practice—is to **keep your modules free of side effects at import
> time**.
>
> If you really need modules with import cycles and side effects, there’s
> still another way out: **do your imports locally inside functions**.

**要は設計を見直しましょうということですね。**


CC BY-SA 4.0
2021-02-10T01:02:14.823
73901
2021-02-10T01:08:58.300
2021-02-10T01:08:58.300
26370
26370
73880
answer