69119


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

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

[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)  
「起動スクリプト+モジュールフォルダの形で提供するのがベストプラクティス」ということで納得しました。


CC BY-SA 4.0
2020-07-30T02:57:05.857
69109
2020-07-31T06:23:26.570
2020-07-31T06:23:26.570
28998
28998
question
python import
カレントフォルダからimportされるのを防ぐ


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

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


CC BY-SA 4.0
2020-07-30T11:18:30.653
69118
2020-07-30T12:53:12.437
2020-07-30T12:53:12.437
40595
40595
69109
answer


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

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

[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]

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


CC BY-SA 4.0
2020-07-30T11:27:49.690
69119
2020-07-30T11:27:49.690
15473
69109
answer


思考実験として **敢えて**
要望通りにカレントフォルダ以外のパスから`import`するのと、カレントフォルダも含めて`import`するのを使い分けたい場合、こんな風にそれぞれのパスをセーブしておいて再設定すれば簡単に出来るのでは？  
C言語のような記号の違いでは無く、`import`を記述する行の位置で区別することになりますし、先に`import`したモジュールと同名の自前モジュールがあっても両方同時には`import`できません。更にメインとなるモジュール以外でもやろうとすると色々と煩雑なことになるでしょうが。

[code]

    import sys
    import os  ## 以下処理ではここでの import は不要だが追加のカスタマイズ等用
    
    pathdefault = sys.path      ## デフォルトをセーブ
    pathcustom1 = sys.path[1:]  ## カレントフォルダ抜きもセーブ @metropolis さんコメント応用
    ## 複数作って使い分けも可。その時は書き換えがおかしくならないように工夫が必要でしょうが。
    
    ## 以下 カレントフォルダ以外のパスからimport
    sys.path = pathcustom1
    import unittest
    
    ## 以下 デフォルトのカレントフォルダを優先したパスからimport
    sys.path = pathdefault
    import mylocalmodule
    
    
[/code]


CC BY-SA 4.0
2020-07-30T14:07:26.057
69127
2020-07-30T14:50:50.920
2020-07-30T14:50:50.920
26370
26370
69109
answer