Pythonネーミングスペースの理解


なんだか、ネーミングスペースという言葉を聞きました.Pythonといえばもうしばらく経ちましたが、ネーミングスペースという言葉は本当に久しぶりだと思います.ジャワやC#を作るとき、気になりました...Pythonをするときはよく考えていませんでした
そこで、Pythonの名前空間はどうなっているのでしょうか.資料を探していると、以前読んだ本を思い出し、ほこりだらけのページをめくって、「High Performance Python」という本を読みました.
だから私はこの本を参考にして、関連内容を書きたいです.

Pythonはどのようにオブジェクトを検索しますか?


オブジェクトをどこで検索するかは、ネーミングスペースに関する内容です.パイソンには、地域変数ディックシャーキャンプのローカル変数とグローバル変数ディックシャーキャンプglobalsがあります.
print(locals())
print(globals())

'''
Outputs:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7ff0f38676a0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'test.py', '__cached__': None}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7ff0f38676a0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'test.py', '__cached__': None}
'''
global contextで2つのディックシリーズを呼び出すのはほとんど違いません.
globalcontextとlocalcontextに差が生じると、結果は変化します.
def f():
    a = 1
    print(locals())
    print(globals())

f()

'''
Outputs:
{'a': 1}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fed02a0b6a0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'test.py', '__cached__': None, 'f': <function f at 0x7fed02a4e310>}
'''
あるオブジェクトを呼び出すと、Python interferはまずlocals()でオブジェクトを検索し、ない場合は次のglobals()でオブジェクトを検索します.ディック郡内部を探索したが,これは多くの場合O(1)O(1)O(1)の時間的複雑さを有する.(ディック・シャナリー探索についてはhash関数についても議論しますが、言いたいことがたくさんあるので、スキップしましょう...)
globalsでもない場合はbuildinsで検索します.前の2つのディックシリーズとは異なり、buildinsはモジュールです.
print(__builtins__)
print(dir(__builtins__))

'''
Outputs:
<module 'builtins' (built-in)>
['ArithmeticError', ..., 'ZeroDivisionError', '__build_class__', ..., '__spec__', 'abs', ..., 'zip']
'''
モジュールからオブジェクトをインポートすると、そのモジュールのコンテキストでローカルオブジェクトが呼び出され、ローカルディクバンナで必要なオブジェクトに移動します.
速度が重要なコードではこれらの部分に注意し、Pythonでは論理を最適化することでmaincontextのローカル人はディック検索を必要とせずにオブジェクトを見つけることができます.ただし,モジュールのlocalsからオブジェクトをインポートするとディクシャナ検索が行われるため,速度差が大きい.

Diss Assemblingによるネーミングスペース間のルックアップ速度の違いの測定


最近よく使われているdatetimeモジュールを用いて実験を行う.
import datetime
from datetime import timedelta
まず、calc_time関数をタイマに定義する.
def calc_time(func):
    st = time()
    func()
    et = time()
    return et - st
次に、timedeltaクラスをインポートする関数を異なる方法で記述する.
def test1():
    for _ in range(10**6):
        datetime.timedelta(1, seconds=1)

def test2():
    for _ in range(10**6):
        timedelta(1, seconds=1)

def test3(timedelta=datetime.timedelta):
    for _ in range(10**6):
        timedelta(1, seconds=1)
test1 Globalsでdatetimeを検索し、このモジュールのローカルでtimedeltaを検索します.test2はGlobalsからtimedeltaを直接持ってきた.test3は現地から直接timedeltaを持ってきた.
times = [
    calc_time(test1),
    calc_time(test2),
    calc_time(test3),
]

print(times)
'''
Outputs:
[0.29599452018737793, 0.2738306522369385, 0.2707936763763428]
'''
時間差は有意義に発生していることがわかる.
もっと詳しく検討してみましょう.
import dis

dis.dis(test1)
print('-' * 100)
dis.dis(test2)
print('-' * 100)
dis.dis(test3)
disモジュールは、特定の関数をdissアセンブリすることによってバイトコードを表示するモジュールである.
 13           0 LOAD_GLOBAL              0 (range)
              2 LOAD_CONST               1 (1000000)
              4 CALL_FUNCTION            1
              6 GET_ITER
        >>    8 FOR_ITER                18 (to 28)
             10 STORE_FAST               0 (_)

 14          12 LOAD_GLOBAL              1 (datetime)
             14 LOAD_ATTR                2 (timedelta)
             16 LOAD_CONST               2 (1)
             18 LOAD_CONST               2 (1)
             20 LOAD_CONST               3 (('seconds',))
             22 CALL_FUNCTION_KW         2
             24 POP_TOP
             26 JUMP_ABSOLUTE            8
        >>   28 LOAD_CONST               0 (None)
             30 RETURN_VALUE
----------------------------------------------------------------------------------------------------
 17           0 LOAD_GLOBAL              0 (range)
              2 LOAD_CONST               1 (1000000)
              4 CALL_FUNCTION            1
              6 GET_ITER
        >>    8 FOR_ITER                16 (to 26)
             10 STORE_FAST               0 (_)

 18          12 LOAD_GLOBAL              1 (timedelta)
             14 LOAD_CONST               2 (1)
             16 LOAD_CONST               2 (1)
             18 LOAD_CONST               3 (('seconds',))
             20 CALL_FUNCTION_KW         2
             22 POP_TOP
             24 JUMP_ABSOLUTE            8
        >>   26 LOAD_CONST               0 (None)
             28 RETURN_VALUE
----------------------------------------------------------------------------------------------------
 21           0 LOAD_GLOBAL              0 (range)
              2 LOAD_CONST               1 (1000000)
              4 CALL_FUNCTION            1
              6 GET_ITER
        >>    8 FOR_ITER                16 (to 26)
             10 STORE_FAST               1 (_)

 22          12 LOAD_FAST                0 (timedelta)
             14 LOAD_CONST               2 (1)
             16 LOAD_CONST               2 (1)
             18 LOAD_CONST               3 (('seconds',))
             20 CALL_FUNCTION_KW         2
             22 POP_TOP
             24 JUMP_ABSOLUTE            8
        >>   26 LOAD_CONST               0 (None)
             28 RETURN_VALUE
test1は、LOAD GLOBALおよびLOAD ATTRによってtimedeltaをもたらす.test2はLOAD GLOBALとしてtimedeltaをもたらす.test3はLOAD FASTとしてtimedeltaをもたらす.
実際のバイトコードの演算量にも差があります.
これが定量的にどれほどの差があるのか知りたかったのですが、正確に実験するのは難しいので断念しました.たとえば、LOAD CONSTコマンドの違いを理解するために、このような関数を作成すると、CALL FUNCTIOnKWで渡されるパラメータの数に差が生じるなど、干渉が多すぎます.
方法をご存知の方がいらっしゃいましたら、教えてください・・・ううう
test 1の時間からtest 2の時間を減算すると、LOAD ATTRに要する時間は約0.02秒であることがわかる.10610^6106回繰り返してもこの程度の時間差が出てくるので、LOAD ATTRコマンドが非常に遅いことを示しています.
これが,このモジュールのcontext dictionaryからディック探索アルゴリズムによりオブジェクトを取得するのに要する時間差である.
ざっと実験してみると、LOAD GLOBALとLOAD FASTは2倍程度の差がありますが、時間自体が少なすぎます(100万回繰り返しても、それ相応のコマンドの0.001単位の時間が必要です)、無視できます.
要するに,Pythonのネーミング空間構造と,それによって生じる可能性のある時間差を理解した.Pythonは非常に遅い言語で、主に速度を気にしない人がそれを使っていると思っているかもしれませんが、実際には科学関連の分野に入ると、誰よりも重い演算があります.
今私も、データETLの过程の中で、时间は本当にとても长くて、この点を理解して、スピードの大幅な向上を実现することができますと思っています.
もちろん、多くの場合、これらに比べて他の面でより大きな改善があります.