Pythonネーミングスペースの理解
なんだか、ネーミングスペースという言葉を聞きました.Pythonといえばもうしばらく経ちましたが、ネーミングスペースという言葉は本当に久しぶりだと思います.ジャワやC#を作るとき、気になりました...Pythonをするときはよく考えていませんでした
そこで、Pythonの名前空間はどうなっているのでしょうか.資料を探していると、以前読んだ本を思い出し、ほこりだらけのページをめくって、「High Performance Python」という本を読みました.
だから私はこの本を参考にして、関連内容を書きたいです.
オブジェクトをどこで検索するかは、ネーミングスペースに関する内容です.パイソンには、地域変数ディックシャーキャンプのローカル変数とグローバル変数ディックシャーキャンプglobalsがあります.
globalcontextとlocalcontextに差が生じると、結果は変化します.
globalsでもない場合はbuildinsで検索します.前の2つのディックシリーズとは異なり、buildinsはモジュールです.
速度が重要なコードではこれらの部分に注意し、Pythonでは論理を最適化することでmaincontextのローカル人はディック検索を必要とせずにオブジェクトを見つけることができます.ただし,モジュールのlocalsからオブジェクトをインポートするとディクシャナ検索が行われるため,速度差が大きい.
最近よく使われている
もっと詳しく検討してみましょう.
実際のバイトコードの演算量にも差があります.
これが定量的にどれほどの差があるのか知りたかったのですが、正確に実験するのは難しいので断念しました.たとえば、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の过程の中で、时间は本当にとても长くて、この点を理解して、スピードの大幅な向上を実现することができますと思っています.
もちろん、多くの場合、これらに比べて他の面でより大きな改善があります.
そこで、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の过程の中で、时间は本当にとても长くて、この点を理解して、スピードの大幅な向上を実现することができますと思っています.
もちろん、多くの場合、これらに比べて他の面でより大きな改善があります.
Reference
この問題について(Pythonネーミングスペースの理解), 我々は、より多くの情報をここで見つけました https://velog.io/@ysn003/Python-네임스페이스와-모듈-import-대한-이해テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol