[Python]ぼーっとPython のコードを眺める
公開日:2025-12-14
更新日:2025-12-14
更新日:2025-12-14
1. 概要
ぼーっとPython のコードを眺めて、気になったところを確認します。
2. 親パッケージのインポート
見たコード
import は複数インポートの指定ができる。
改行しているため、( ) で囲っている。1行で書く場合は不要。
. は、相対パスの ./ のようなもので、現在のパッケージを表す
注意:プロジェクトのフォルダはパッケージではないため、「.」や「..」でプロジェクトのフォルダを指すことはできない。
コード
from .apibase import (
BINARY as BINARY,
DATETIME as DATETIME,
NUMBER as NUMBER,
ROWID as ROWID
)
import は複数インポートの指定ができる。
改行しているため、( ) で囲っている。1行で書く場合は不要。
. は、相対パスの ./ のようなもので、現在のパッケージを表す
コード
from . import pkg # 同じパッケージの pkg をインポート
from .. import pkg # 1つ上のパッケージの pkg をインポート
from ... import pkg # 2つ上のパッケージの pkg をインポート
注意:プロジェクトのフォルダはパッケージではないため、「.」や「..」でプロジェクトのフォルダを指すことはできない。
3. アンパック、スライス
見たコード
コード
return Date(*time.gmtime(ticks)[:3])
time.gmtime(ticks) は、time.struct_time と言う型の値を返してくる。実行結果
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=1, tm_sec=40, tm_wday=3, tm_yday=1, tm_isdst=0)
これは __getitem__ を実装しているため、time.gmtime(ticks)[:3] のようにスライスで使用できる。実行結果
(1970, 1, 1)
先頭に * がついているので、このタプルを展開して、Date() に3つの引数を渡している。4. Union型の型ヒント、位置専用引数
見たコード
コード
def gmtime(seconds: float | None = None, /) -> struct_time: ...seconds: float | None
Union型の型ヒント。seconds は、float または None の値が入る。
= None
デフォルト値
引数の最後の /
位置専用引数を示します。
add(v1 = 1, v2 = 2) のように、引数名を指定して渡すとエラーになります。
add(v1 = 1, v2 = 2) のように、引数名を指定して渡すとエラーになります。
...
メソッドのヘッダのみでここには実装がないと言う意味。
上記を使った使用例
コード
def add(v1: int | float = 0, v2: int | float = 0, /) -> int | float:
return v1 + v2
print(add(1, 2.2)) # 3.2
引数に int と float のどちらを指定しても型チェッカーでエラーが出ない。5. @final
クラスの継承の禁止を意味する。クラスを継承すると型チェッカーでエラーが出る。
コード
from typing import final
@final
class Test:
pass
class TestEx(Test): # 型チェッカーでエラーが出る
pass6. ジェネリクス
見たコード
[Any | int] はジェネリクス。
struct_time クラスは、ジェネリクスで型が指定された structseqクラス と _TimeTupleクラスを継承すると言う意味。
ジェネリクスの従来の書き方
ジェネリクスの Python 3.12 以降で使える書き方
コード
class struct_time(structseq[Any | int], _TimeTuple):
[Any | int] はジェネリクス。
struct_time クラスは、ジェネリクスで型が指定された structseqクラス と _TimeTupleクラスを継承すると言う意味。
ジェネリクスの従来の書き方
コード
from typing import TypeVar, Generic
T = TypeVar('T', int, float)
class Calculator(Generic[T]):
def add(self, v1: T, v2: T) -> T:
return v1 + v2
int_calc = Calculator[int]()
float_calc = Calculator[float]()
print(int_calc.add(1, 2)) # 3
print(float_calc.add(1.5, 2)) # 3.5
ジェネリクスの Python 3.12 以降で使える書き方
コード
from typing import TypeVar, Generic
# Python 3.12 以降で使える書き方
class Calculator[T:(int, float)]:
def add(self, v1: T, v2: T) -> T:
return v1 + v2
int_calc = Calculator[int]()
float_calc = Calculator[float]()
int_float_calc = Calculator[int | float]()
print(int_calc.add(1, 2)) # 3
print(float_calc.add(1.5, 2)) # 3.5
print(int_float_calc.add(1.5, 2)) # 3.57. キーと値の関連付け
見たコード
辞書にしてキーを指定することで、明示的にキーと値の関連付けをしています。
リストの場合は、たまたま 0 から 4 が関連付けられており、要素を途中に追加する場合は注意する必要がある。
コード
directions = {
0: "Unknown",
1: "Input",
2: "Output",
3: "InputOutput",
4: "Return",
}
キーが 0 ~ 4 のため、リストにした場合も directions[0] ~ [4] で参照できますが、辞書にしてキーを指定することで、明示的にキーと値の関連付けをしています。
リストの場合は、たまたま 0 から 4 が関連付けられており、要素を途中に追加する場合は注意する必要がある。
8. Final
再代入をすると、型チェッカーでエラーが出る。
コード
from typing import Final
a: Final = 1
b: Final[int] = 2 # Final と型を同時に指定する
a = 39. %
古い文字列のフォーマット方法。
% の右側の値を、左側の %s に入れる。
% の右側の値を、左側の %s に入れる。
コード
'Name: %s, Dir.: %s, Type: %s, Size: %s, Value: "%s", Precision: %s, NumericScale: %s'
% (
p.Name,
adc.directions[p.Direction],
adc.adTypeNames.get(p.Type, str(p.Type) + " (unknown type)"),
p.Size,
p.Value,
p.Precision,
p.NumericScale,
)
コード
print('%s %s %d' % ('abc', 'def', 123)) # abc def 12310. 関数の中の import
関数の中で import した場合は、関数の中だけでインポートしたものを参照可能。
同じ import は1回だけ実行される。2回目以降は実行されないので、繰り返し実行しても問題ない。
同じ import は1回だけ実行される。2回目以降は実行されないので、繰り返し実行しても問題ない。
コード
def test():
import sub # 2回目以降は実行されない
sub.test_sub()
test()
test()
test()
# sub.test_sub() # エラー : ここでは参照不可。sub は test() の中だけで参照できる。11. Callable, Iterable, Iterator, Mapping, Sequence の型チェック
コード
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
# 引数 : 関数
def test1(f: Callable[[int, str], bool]) -> bool:
return f(1, 'a')
values = [1, 2, 3, 4, 5]
# コールバック用の関数(引数:[int, str], 戻り値:bool] )
def func_for_test1(a:int, b:str) -> bool:
return True
print(test1(func_for_test1))
#--------------------
# 引数 : Iterable
def test2(values: Iterable[int]) -> bool:
return True
# Iterable(__iter__() を持つオブジェクト。リストも Iterable)
print(test2([1, 2, 3]))
print(test2({0:1, 1:2, 2:3}))
#--------------------
# 引数 : Iterator
def test3(values: Iterator[int]) -> bool:
return True
# Iterator(__iter__() と __next__() を持つオブジェクト。全体を1度しかアクセスできない)
iterator = iter([1, 2, 3])
print(test3(iterator))
# filter や map の戻り値も Iterator
filtered = filter(lambda v: v % 2 == 1, [1, 2, 3, 4, 5])
print(test3(filtered))
#--------------------
# 引数 : キーが str、値が int の辞書
def test4(users: Mapping[str, int]) -> bool:
return True
# Mapping : 辞書など。
print(test4({'key1': 1, 'key2':2}))
#--------------------
# 引数 : 値が str のリスト
def test5(items: Sequence[str]) -> bool:
return True
# Sequence : インデックスアクセスが可能なリストやタプルなど。
print(test5(['a', 'b', 'c']))12. オーバロード
見たコード
@overload を使うと、型チェッカーに、同じ名前の関数で、異なる引数があることを伝えられます。
Python にはオーバーロードの機能がないため、自分で関数内で分岐して、オーバーロードっぽい動作にする必要があります。
コード
@overload
def call(
self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT]
) -> T_Retval: ...
@overload を使うと、型チェッカーに、同じ名前の関数で、異なる引数があることを伝えられます。
Python にはオーバーロードの機能がないため、自分で関数内で分岐して、オーバーロードっぽい動作にする必要があります。
コード
from typing import overload
class Test:
@overload
def test(self, value: int) -> bool: ...
@overload
def test(self, value: str) -> bool: ...
#
def test(self, value: int | str) -> bool:
if isinstance(value, int):
print('test(int)')
return True
elif isinstance(value, str):
print('test(str)')
return True
obj = Test()
obj.test(1)
obj.test('a')13. __slots__
__dict__ はインスタンス毎に保持されているため、大量にインスタンスを作成すると、メモリを消費する。
__slots__ を使うと、__dict__ が保持されずに、全インスタンスで共通の固定の属性となる。動的な属性の追加は不可。
これにより、メモリ消費量を抑えることができる
__slots__ を使うと、__dict__ が保持されずに、全インスタンスで共通の固定の属性となる。動的な属性の追加は不可。
これにより、メモリ消費量を抑えることができる
コード
class Test:
def __init__(self, value1, value2):
self.value1 = value1
self.value2 = value2
obj1_1 = Test(1, 2)
obj1_2 = Test(1, 2)
obj1_2.value3 = 3
# __dict__ はインスタンス毎に保持されているため、値が異なる
print(obj1_1.__dict__) # {'value1': 1, 'value2': 2}
print(obj1_2.__dict__) # {'value1': 1, 'value2': 2, 'value3': 3}
# __slots__ を使うと、__dict__ が保持されずに、属性は固定になる。動的な属性の追加は不可。
# 大量にインスタンスを作成する場合には、これにより、メモリ消費量を抑えることができる
class Test2:
__slots__ = ['value1'] # 属性を固定にする
def __init__(self, value1, value2):
self.value1 = value1
# self.value2 = value2 # エラー : 動的に属性を追加できない
def test(self):
pass
obj2 = Test2(1, 2)
# print(obj2.__dict__) # エラー : __dict__ は存在しない
print(obj2.__slots__) # ['value1']14. with、コンテキストマネージャー
14.1 with
リソースを取得した場合、途中でエラーが発生しても解放できるようにする必要がありますが、コンテキストマネージャーと with を使うと簡単に書くことができます。
with を使った場合。finally や close を書かなくても、必ず close されます。
コンテキストマネージャーは、__enter__ と __exit__ を持っています。
with の時に __enter__ が呼ばれ、with を抜ける時に、__exit__ が呼ばれます。
メソッドの有無は、hasattr() で確認できる。
コード
f = open('test.txt', 'w')
try:
f.write('Test')
finally:
f.close()
with を使った場合。finally や close を書かなくても、必ず close されます。
コード
with open('test.txt', 'w') as f:
f.write('Test')
open() の戻り値はファイルオブジェクトですが、コンテキストマネージャーでもあります。コンテキストマネージャーは、__enter__ と __exit__ を持っています。
with の時に __enter__ が呼ばれ、with を抜ける時に、__exit__ が呼ばれます。
メソッドの有無は、hasattr() で確認できる。
コード
print(hasattr(f, '__enter__')) # True
print(hasattr(f, '__exit__')) # True14.2 yield
yield を含む関数は、実行すると generator オブジェクトを返します。
yield は return のように値を返します。また関数が呼ばれた時は、yield の次のコードから処理が再開されます。
yield は return のように値を返します。また関数が呼ばれた時は、yield の次のコードから処理が再開されます。
コード
def test():
print('1回目')
yield 1
print('2回目')
yield 2
print('3回目')
yield 3
# test() が返す generator オブジェクトでループ
for value in test():
print(value)
# 実行結果
# 1回目
# 1
# 2回目
# 2
# 3回目
# 314.3 独自のコンテキストマネージャーの実装
コード
class TestContextManager:
def __enter__(self):
print('with の中に入りました')
return self # as の右側に変数に入る値
def __exit__(self, exc_type, exc_value, traceback):
print('with から抜けました')
return False # True にすると、例外が発生した場合に例外をキャッチして正常終了してしまう
with TestContextManager() as tcm:
print(type(tcm))
print('Test')
raise Exception('Exception')
# 実行結果
# with の中に入りました
# <class '__main__.TestContextManager'>
# Test
# with から抜けました
# Traceback (most recent call last):
# File "main.py", line 17, in <module>
# raise Exception('Exception')
# Exception: Exception14.4 デコレータを使った実装
コード
from contextlib import contextmanager
@contextmanager
def test_context_manager():
print('with の中に入りました')
try:
yield # with の中で発生した例外を finally で処理できるようにするため、try の後ろに yield を書く
finally:
print('解放処理')
with test_context_manager():
print('Test')
raise Exception('Exception')
# 実行結果
# with の中に入りました
# Test
# 解放処理
# Traceback (most recent call last):
# File "main.py", line 68, in <module>
# raise Exception('Exception')
# Exception: Exception

