Skip to content

2023 06 25

【Python】Listで削除実行時の実行時間

https://wiki.python.org/moin/TimeComplexity 参考リンク先ではDel sliceは計算量\(O(n)\) これは最悪の場合で実際は削除する量による。 PythonではListの要素を削除する際に、各要素のインデックスの再設定が行われる様子。 したがって削除された要素以降の要素が多いほど計算量が嵩むことになる。

from time import perf_counter

C = [3, 5, 7, 8]

for c in C:
    l = [0 for _ in range(10**c)]
    s = perf_counter()
    del l[10:]
    e = perf_counter()
    print(f"del item [c={c}, del list[10:]]: {e - s}")

print()

for c in C:
    l = [0 for _ in range(10**c)]
    s = perf_counter()
    del l[-10:]
    e = perf_counter()
    print(f"del item [c={c}, del list[-10:]: {e - s}")
del item [c=3, del list[10:]]: 4.600000465870835e-06
del item [c=5, del list[10:]]: 0.0003085000007558847
del item [c=7, del list[10:]]: 0.03811470000073314
del item [c=8, del list[10:]]: 0.36907919999976

del item [c=3, del list[-10:]: 3.4000004234258085e-06
del item [c=5, del list[-10:]: 1.2000000424450263e-06
del item [c=7, del list[-10:]: 3.600000127335079e-06
del item [c=8, del list[-10:]: 1.1600001016631722e-05
Del Itemの時でも同様。
from time import perf_counter

C = [3, 5, 7, 8]

for c in C:
    l = [0 for _ in range(10**c)]
    s = perf_counter()
    del l[10]
    e = perf_counter()
    print(f"del item [c={c}, del list[10]: {e - s}")

print()

for c in C:
    l = [0 for _ in range(10**c)]
    s = perf_counter()
    del l[-10]
    e = perf_counter()
    print(f"del item [c={c}, del list[-10]: {e - s}")
del item [c=3, del list[10]: 6.999998731771484e-07
del item [c=5, del list[10]: 5.290000081004109e-05
del item [c=7, del list[10]: 0.009698500000013155
del item [c=8, del list[10]: 0.11039209999944433

del item [c=3, del list[-10]: 0.0001799000001483364
del item [c=5, del list[-10]: 8.999995770864189e-07
del item [c=7, del list[-10]: 2.3000011424301192e-06
del item [c=8, del list[-10]: 2.6999987312592566e-06

【Python】Metaclass

https://astropengu.in/posts/32/ - __new__は初期化されていないクラスのインスタンスを生成するときに使われる - __init__インスタンス生成後に初期化のために呼び出される つまり__init__ではインスタンスは生成されない。=>コンストラクタではない

# 下記の2つの生成方法は等価

# 通常のインスタンス生成
obj == Class(arg)

# __new__, __init__を明示的に呼び出す場合
# Classに__new__が定義されていない場合はobject.__new__が呼び出される
obj = Class.__new__(Class, arg)
obj._init_(arg)
インスタンス生成時のカスタマイズ等に利用される。 -> シングルトン等 - メタクラスの役割はクラス生成そのもののカスタマイズ クラスは組み込みクラスのtypeを使って下記のように動的に生成できる。
def __init__(self, arg):
    self.arg = arg

# type(name, base, dict, **kwds) -> new type
Class = type("class_created_by_type", (object,), {"__init__": __init__})
print(Class) # -> <class '__main__.class_created_by_type'>
これはtypeのインスタンスがクラスになっている。 このようにインスタンスがクラスになるようなクラスのことをメタクラスと呼ぶ。 通常のクラス定義ではメタクラスがtypeになっている。つまり暗黙的に下記と等価である。
class Class(metaclass=type): ....
最終的にはtypeが呼び出されインスタンスが返されればよいので、metaclassに渡すのはcallableなオブジェクトであればクラスでも関数でも構わない。
def add_test_to_method_name(name, bases, dict):
    new_dict = {f"{key}_test": val for key, val in dict.items()}
    return type(name, bases, new_dict)

class Test(metaclass=add_test_to_method_name):
    def print_text(self, txt: str):
        print(txt)


t = Test()
t.print_text_test("print_text_test")
t.print_text("print_text")

"""実行結果
print_text_test
Traceback (most recent call last):
  File "c:\github\design-pattern\test.py", line 12, in <module>
    t.print_text("print_text")
AttributeError: 'Test' object has no attribute 'print_text'
"""
クラスの場合 メタクラスはオブジェクトであるため、メイン処理の実行前にメタクラスのインスタンス化が実行される。
class AddTest(type):
    def __new__(cls, *args, **kwds):
        print("metaclass: __new__")
        d = {f"{key}_test": val for key, val in args[2].items()}
        new_args = (args[0], args[1], d)
        return super().__new__(cls, *new_args, **kwds)

    def __init__(self, *args, **kwds):
        print("metaclass: __init__")
        return super().__init__(*args, **kwds)

class Test(metaclass=AddTest):
    def print_text(self, txt: str):
        print(txt)


print("==== start ====")
t = Test()
t.print_text_test("print_text_test")
print("==== end ====")

"""実行結果
metaclass: __new__
metaclass: __init__
==== start ====
print_text_test
==== end ====
"""

【Python】__call__

インスタンス化したオブジェクトを関数のようにobj()呼び出したときに実行される。 また生成オブジェクトよりメタクラスの__call__のほうが優先される様子。

from typing import Any

class Test():
    def __call__(self, *args: Any, **kwds: Any) -> Any:
        print("Test __call__ function")

t = Test()
t() # -> Test __call__ function
メタクラス内で定義した場合はインスタンス生成の際に呼び出されることになる。
from typing import Any

class TestMeta(type):
    def __new__(cls, name, bases, dict):
        return super().__new__(cls, name, bases, dict)

    def __call__(self, *args: Any, **kwds: Any) -> Any:
        print("TestMeata __call__ function")
        return self


class Test(metaclass=TestMeta):
    def __call__(self, *args: Any, **kwds: Any) -> Any:
        print("Test __call__ function")

print("テストインスタンス初期化: 開始")
t = Test()
print("テストインスタンス初期化: 終了")
t() 

"""実行結果
テストインスタンス初期化: 開始
TestMeata __call__ function
テストインスタンス初期化: 終了
TestMeata __call__ function
"""