Circular imports

In the 1D tic-tac-toe game for your homework, you will probably write several modules. It will look like this: (Arrows between modules are showing the imports.)

┌──────────────────╮ ┌───────────────╮  ┌──────────────────╮ 
│      ai.py       │  │ tictactoe.py  │   │    game.py       │
├──────────────────┤  ├───────────────┤   ├──────────────────┤
│                  │◀-│ import ai     │◀-│import tictactoe  │
├──────────────────┤  ├───────────────┤   ├──────────────────┤
│ def ai_move      │  │ def evaluate  │   │                  │
│                  │  │ def move      │   │                  │
└──────────────────┘  │def player_move│   └──────────────────┘
                      │               │
                      └───────────────┘
                          ▲
                          │
                          │ ┌───────────────────╮
                          │ │ test_ticktactoe.py│
                          │ ├───────────────────┤
                          └─│ import tictactoe  │
                            ├───────────────────┤
                            │ def test_...      │
                            │                   │
                            └───────────────────┘

But function ai_move needs to call function move.
What can we do?
Could you import ai from tictactoe while you are importing tictactoe from ai?

┌──────────────────╮ ┌───────────────╮
│      ai.py       │  │ tictactoe.py  │
├──────────────────┤  ├───────────────┤
│                  │◀-│ import ai     │
│import ticktaktoe │-▶│               │
│                  │  │               │
│   def ai_move    │  │ def evaluate  │
│                  │  │ def move      │
└──────────────────┘  │def player_move│
                      │               │
                      └───────────────┘

We can look at it from the point of view of Python, which is executing the commands. When it has to import tictactoe.py, it process the file line by line. And it almost at the begging see command import ai. So it opens file ai.py and it start to process it line by line. And of course it will soon get to import tictactoe. What next?

To avoid an infinite loop - one file would import another one and this one would import the first one over and over again - Python will make some workaround when we run tictactoe: when it notices that tictactoe is already being imported in ai.py, it will import the part of tictactoe that it's just before import ai into ai module and this will replace line import tictactoe so it's no longer there. And then it can continue the import of ai in tictactoe.py. When it finishes this import it will continue in tictactoe and all its functions and commands.

This could be useful but in most of the times it behaves very unpredictable therefore it's dangerous.

In other words: when two modules are trying to import the other one the program doesn't have to work as expected.

We want to prevent this kind of situation.

How will we do it? We have two options.

Organise modules by dependency

First option is to move function move to module ai and we can use it from there. That's easy but that's not what we wont from ai module, because it should contain the logic how our "AI" is choosing where to move only. It definitely shouldn't contain other functions which might be useful somewhere else.

┌──────────────────╮ ┌───────────────╮
│      ai.py       │  │ tictactoe.py  │
├──────────────────┤  ├───────────────┤
│                  │◀-│ import ai     │
│                  │  │               │
│ def ai_move      │  │ def evaluate  │
│ def move         │  │def player_move│
│                  │  │               │
└──────────────────┘  └───────────────┘

Support module

Second option is to define new module which will be used in tictactoe.py and in ai.py.

This module is usually as util.py (=utility).

              ┌──────────────────╮
              │ util.py          │
              ├──────────────────┤
              │ def move         │
              └──────────────────┘
                      ▲  ▲
                      │  │
┌──────────────────╮ │  │  ┌───────────────╮
│      ai.py       │  │  │  │ tictactoe.py  │
├──────────────────┤  │  │  ├───────────────┤
│ import util      │──┘  └──│ import util   │
│                  │◀───────│ import ai     │
│                  │        │               │
│ def ai.move      │        │ def evaluate  │
│                  │        │def player_move│
│                  │        │               │
└──────────────────┘        └───────────────┘

Disadvantage of support module is that it can easily become non-maintained storage of your code, which you used on so many places that you have no idea where exactly you used it and whether you can modify or delete it.

What you should choose always depends on the current situation.

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2018/pyladies-en-prague:git:0",
      "title": "Circular imports",
      "html": "\n          \n    \n\n    <h2>Circular imports</h2>\n<p>In the 1D tic-tac-toe game for your homework, you will probably write several modules.\nIt will look like this:\n(Arrows between modules are showing the imports.)</p>\n<div class=\"highlight\"><pre><code>&#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E; &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E;  &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E; \n&#x2502;      ai.py       &#x2502;  &#x2502; tictactoe.py  &#x2502;   &#x2502;    game.py       &#x2502;\n&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;  &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;   &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n&#x2502;                  &#x2502;&#x25C0;-&#x2502; import ai     &#x2502;&#x25C0;-&#x2502;import tictactoe  &#x2502;\n&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;  &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;   &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n&#x2502; def ai_move      &#x2502;  &#x2502; def evaluate  &#x2502;   &#x2502;                  &#x2502;\n&#x2502;                  &#x2502;  &#x2502; def move      &#x2502;   &#x2502;                  &#x2502;\n&#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;  &#x2502;def player_move&#x2502;   &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;\n                      &#x2502;               &#x2502;\n                      &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;\n                          &#x25B2;\n                          &#x2502;\n                          &#x2502; &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E;\n                          &#x2502; &#x2502; test_ticktactoe.py&#x2502;\n                          &#x2502; &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n                          &#x2514;&#x2500;&#x2502; import tictactoe  &#x2502;\n                            &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n                            &#x2502; def test_...      &#x2502;\n                            &#x2502;                   &#x2502;\n                            &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;</code></pre></div><p>But function <code>ai_move</code> needs to call function <code>move</code>.<br>\nWhat can we do?<br>\nCould you import <code>ai</code> from <code>tictactoe</code> while you are importing <code>tictactoe</code> from <code>ai</code>?</p>\n<div class=\"highlight\"><pre><code>&#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E; &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E;\n&#x2502;      ai.py       &#x2502;  &#x2502; tictactoe.py  &#x2502;\n&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;  &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n&#x2502;                  &#x2502;&#x25C0;-&#x2502; import ai     &#x2502;\n&#x2502;import ticktaktoe &#x2502;-&#x25B6;&#x2502;               &#x2502;\n&#x2502;                  &#x2502;  &#x2502;               &#x2502;\n&#x2502;   def ai_move    &#x2502;  &#x2502; def evaluate  &#x2502;\n&#x2502;                  &#x2502;  &#x2502; def move      &#x2502;\n&#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;  &#x2502;def player_move&#x2502;\n                      &#x2502;               &#x2502;\n                      &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;</code></pre></div><p>We can look at it from the point of view of Python,\nwhich is executing the commands.\nWhen it has to import <code>tictactoe.py</code>, it process the file line by line.\nAnd it almost at the begging see command <code>import ai</code>.\nSo it opens file <code>ai.py</code> and it start to process it line by line.\nAnd of course it will soon get to <code>import tictactoe</code>. What next?</p>\n<p>To avoid an infinite loop - one file would import another one and this one would import\nthe first one over and over again - \nPython will make some workaround when we run <code>tictactoe</code>:\nwhen it notices that <code>tictactoe</code> is already being imported in <code>ai.py</code>,\nit will import the part of <code>tictactoe</code> that it&apos;s just before <code>import ai</code> into <code>ai</code> module\nand this will replace line <code>import tictactoe</code> so it&apos;s no longer there. And then it can continue the\nimport of <code>ai</code> in <code>tictactoe.py</code>.\nWhen it finishes this import it will continue in <code>tictactoe</code> and all its functions and commands.</p>\n<p>This could be useful but in most of the times it behaves very unpredictable therefore it&apos;s dangerous.</p>\n<p>In other words: when two modules are trying to import the other one\nthe program doesn&apos;t have to work as expected.</p>\n<p>We want to prevent this kind of situation.</p>\n<p>How will we do it? We have two options.</p>\n<h2>Organise modules by dependency</h2>\n<p>First option is to move function <code>move</code> to module <code>ai</code> and we can use it from there.\nThat&apos;s easy but that&apos;s not what we wont from <code>ai</code> module, because it should contain\nthe logic how our &quot;AI&quot; is choosing where to move only.\nIt definitely shouldn&apos;t contain other functions which might be useful somewhere else.</p>\n<div class=\"highlight\"><pre><code>&#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E; &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E;\n&#x2502;      ai.py       &#x2502;  &#x2502; tictactoe.py  &#x2502;\n&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;  &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n&#x2502;                  &#x2502;&#x25C0;-&#x2502; import ai     &#x2502;\n&#x2502;                  &#x2502;  &#x2502;               &#x2502;\n&#x2502; def ai_move      &#x2502;  &#x2502; def evaluate  &#x2502;\n&#x2502; def move         &#x2502;  &#x2502;def player_move&#x2502;\n&#x2502;                  &#x2502;  &#x2502;               &#x2502;\n&#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;  &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;</code></pre></div><h2>Support module</h2>\n<p>Second option is to define new module which will be used in\n<code>tictactoe.py</code> and in <code>ai.py</code>.</p>\n<p>This module is usually as <code>util.py</code> (=utility).</p>\n<div class=\"highlight\"><pre><code>              &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E;\n              &#x2502; util.py          &#x2502;\n              &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n              &#x2502; def move         &#x2502;\n              &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;\n                      &#x25B2;  &#x25B2;\n                      &#x2502;  &#x2502;\n&#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E; &#x2502;  &#x2502;  &#x250C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x256E;\n&#x2502;      ai.py       &#x2502;  &#x2502;  &#x2502;  &#x2502; tictactoe.py  &#x2502;\n&#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;  &#x2502;  &#x2502;  &#x251C;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2524;\n&#x2502; import util      &#x2502;&#x2500;&#x2500;&#x2518;  &#x2514;&#x2500;&#x2500;&#x2502; import util   &#x2502;\n&#x2502;                  &#x2502;&#x25C0;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2502; import ai     &#x2502;\n&#x2502;                  &#x2502;        &#x2502;               &#x2502;\n&#x2502; def ai.move      &#x2502;        &#x2502; def evaluate  &#x2502;\n&#x2502;                  &#x2502;        &#x2502;def player_move&#x2502;\n&#x2502;                  &#x2502;        &#x2502;               &#x2502;\n&#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;        &#x2514;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2500;&#x2518;</code></pre></div><p>Disadvantage of support module is that it can easily \nbecome non-maintained storage of your code, which you used on so\nmany places that you have no idea where exactly you used it and whether \nyou can modify or delete it.</p>\n<p>What you should choose always depends on the current situation.</p>\n\n\n        "
    }
  }
}