カレントフォルダからimportされるのを防ぐ


標準ライブラリとカレントフォルダに同名のモジュールがある場合、デフォルトではカレントフォルダが優先されるかと思います。

例）以下のようなフォルダ構成のとき、

[code]

    some-dir/
      main.py
      unittest.py
    
[/code]

main.pyで `import unittest` すると、標準ライブラリの `unittest` ではなく、`some-dir` にある
`unittest.py` がインポートされる。

このケースで、標準ライブラリの `unittest` を明示的にインポートするにはどうしたらいいでしょうか。

補足）  
`sys.path`
からカレントフォルダを削除することで、とりあえずは目的は満たせたのですが、その場合はカレントフォルダから他のモジュールをインポートできなくなってしまいました。  
もっとスマートなやり方があれば何卒ご教示ください。

[code]

    import sys
    import os
    sys.path.remove(os.getcwd())
    import unittest # ← 標準ライブラリからインポートされる
    
[/code]

### 2020/07/31 追記

回答、及びコメントありがとうございました。  
自分なりに疑問点を整理した結果、「どうすればできるか」というよりは、「どういう思想でこういう仕様になっているのか」という部分が引っかかっていたのだと思います。  
(起動スクリプトのみ `from . import unittest`
の記法が使えないのはなぜか、カレントフォルダを明示的に指定する方法はあるのに、標準ライブラリパスを明示的に指定する方法がないのはなぜか, etc)  
「起動スクリプト+モジュールフォルダの形で提供するのがベストプラクティス」ということで納得しました。




ディレクトリ構成から見直すべきです。

起動スクリプト(最初に実行されるスクリプト)とそれ以外のスクリプトに分け、それ以外のスクリプトはサブディレクトリに置きます。

[code]

    run.py
    app/
      sub1.py
      sub2.py
    
[/code]

そして、

  * 起動スクリプト(上記の場合`run.py`)は、絶対importで他のスクリプトをimportする。
  * その他のスクリプトは、他のスクリプトを相対importでimportする。起動スクリプトはimportしない。(そのような処理は起動スクリプトには書かない)

ようにします。

[code]

    # sub1.py
    from . import sub2
    
[/code]

このようにすれば、

  * 自分のモジュール同士は相対importで呼び出すので、他人のモジュールとかち合うことはない。
  * グローバルなモジュール名をあまり汚さない(今回の場合、「app」のみで済む)
  * **`setup.py`さえ書けば、pipなどのパッケージ管理ができるようになる。**  
(故に、この構成がpythonにとって望ましい構成だと思ってます)

というメリットがあります。

* * *

完全に余談ですが、敢えてディレクトリ構成を変えずにimportする例です。  
その代わりと言ってはなんですが、自分のスクリプトをimportする場合には、頭に「my」を付ける必要があります。

`hoge.py`

[code]

    # coding: utf-8
    
    import sys, importlib, modulefinder, importlib.abc, importlib.util
    from pathlib import Path
    
    print('hoge.')
    
    if __name__ == '__main__':
        sys.path = sys.path[1:]
        class MyFinder(importlib.abc.MetaPathFinder):
            def find_spec(fullname, path, target):
                print('fullname = %s.' % repr(fullname))
                if fullname.startswith('my'):
                    return importlib.util.spec_from_file_location(fullname, str(Path(__file__).parent / (fullname[2:] + '.py')))
                return None
        sys.meta_path = [MyFinder] + sys.meta_path
    
        import mypiyo
    
[/code]

`piyo.py`

[code]

    # coding: utf-8
    
    print('piyo.')
    import myhoge
    
[/code]

これを「`python3 hoge.py`」と実行すると、

[code]

    hoge.
    fullname = 'mypiyo'.
    piyo.
    fullname = 'myhoge'.
    hoge.
    
[/code]

となります。  
まぁこんなやり方は **絶対におすすめしません** が、やっている事は面白いと思うので、見てやってください。




Python のつくりから言うと、同名のモジュールを通常の `import` で区別する手段はありません（コメントにあるように `importlib`
を使うことで無理やり何とかできますが）。

ですので、通常は標準パッケージとかぶらないように名前をつけます。他人に使われることを想定したライブラリの場合では、モジュール名のトップレベルに独自の名前を置く（`mylib.xxx`
のように）というのも一般的な慣習です。他人にコードを再利用されないようなアプリケーションでも同様にしているケースをよく見ます。

