9cubed
ブログ | Tailwind | Vite | Python | Node.js | Linux | PowerShell | その他 | 将棋ウォーズ | 歌の練習
< 前の記事

Python 用の VS Code の設定

次の記事 >

外部ファイルのインポート

Python

[Python]Python の基本

公開日:2025-12-11
更新日:2025-12-11

1. 概要

Python の基本です。

動作確認:Python 3.13.9

2. 型

コード
# 数値
v = 1          # int
v = 123.456    # float

# 文字列
v = 'hello'    # str

# Bool
v = True       # bool
v = False      # bool

# NoneType(他の言語の null)
v = None       # NoneType

# リスト
v = [1, 2, 3]  # <class 'list'>

# タプル(イミュータブル)
v = (1, 2, 3)  # <class 'tuple'>

# 集合
v = {1, 2, 3}  # <class 'set'>

# 辞書(マップ)
v = {'name': '太郎', 'age': 12}  # <class 'dict'>

# バイナリー
v = b"hello"  # <class 'bytes'>

Python では、値には型がありますが、変数自体には型がないため、他の型の値を代入することができます。

型の表示
コード
print(type(123))  # <class 'int'>

3. コメント

コード
# コメント
print('test')  # コメント

Python にはブロックコメントがありません。
次のように書くと何も実行されないため、ブロックコメントのように扱えますが、裏では文字列が生成されて破棄されたり、__doc__ に代入されたりします。
コード
'''
コメント
コメント
コメント
'''

4. 複数行文字列リテラル(ヒアドキュメントのようなもの)

コード
s = '''test
Test
TEST
'''

print(s)
実行結果
test
Test
TEST

末尾に「\」を付けると行が結合されます。
コード
s = '''test\
Test\
TEST
'''

print(s)
実行結果
testTestTEST

5. f-string(formatted string literal)

「'」「"」「'''」「"""」の前に f を付けると、「{変数名} 」が値に展開されます。
コード
name = '太郎'

print(f'{name}さん、こんにちは')
実行結果
太郎さん、こんにちは

「{変数名=} 」にすると、「変数名=値」で展開されます。ログ出力に便利。
コード
name = '太郎'

print(f'{name=}さん、こんにちは')
実行結果
name='太郎'さん、こんにちは

6. 算術演算

コード
import math

print(20 + 10) # 30
print(20 - 10) # 10
print(20 * 10) # 200
print(20 / 10) # 2.0

print(20 // 3) # 6 商(整数除算)
print(20 % 3)  # 2 余り

# 累乗
print(2 ** 3)  # 8

# 平方根
print(math.sqrt(2))  # 1.4142135623730951

7. 文字列操作

コード
# 文字列結合
print('abc' + 'def') # abcdef

# 文字列の長さ(バイト数ではなく文字数)
print(len('test'))      # 4
print(len('こんにちは')) # 5

# 部分文字列取得
print('abcdefg'[3:])  # defg インデックス3からから最後まで
print('abcdefg'[:4])  # abcd 先頭からインデックス4まで
print('abcdefg'[2:6]) # cdef インデックス2から6まで 
print('abcdefg'[:])   # abcdefg 先頭から最後まで

# 部分文字列取得(負のインデックス)
# マイナスを指定した場合は、インデックスを末尾から先頭に向けて数えるだけで、
# [開始:終了] の形式は同じ。
print('abcdefg'[-4:])  # defg 末尾から4文字目のインデックス~最後
print('abcdefg'[:-4])  # abc  先頭~末尾から4文字目のインデックス

# 大文字・小文字変換
print('hello'.upper()) # HELLO
print('WORLD'.lower()) # world

# 文字列の繰り返し(リピート)
print('0' * 5)         # 00000
print('abc' * 3)       # abcabcabc

# 頭0埋め
print('1'.zfill(5))    # 00001
print('123'.zfill(5))  # 00123

# 文字列の整列
print('|' + '123'.ljust(7)  + '|')  # |123    | 左揃え
print('|' + '123'.rjust(7)  + '|')  # |    123| 右揃え
print('|' + '123'.center(7) + '|')  # |  123  | 中央揃え

# トリム(空白の削除)
print('|' + '  Test  '.strip()  + '|') # |Test|    先頭と末尾の空白削除
print('|' + '  Test  '.lstrip() + '|') # |Test  |  先頭の空白削除
print('|' + '  Test  '.rstrip() + '|') # |  Test|  末尾の空白削除

# 改行コード
print('123\n456') # 123
                  # 456
print("123\n456") # 123
                  # 456

# 改行のエスケープ
print('123\\n456') # 123\n456
print("123\\n456") # 123\n456
print(r'123\n456') # 123\n456

# 文字列検索(大文字・小文字の区別あり)
print('abcdefg'.find('cd'))  # 2
print('abcdefg'.index('cd')) # 2

# 文字列検索(大文字・小文字の区別なし)
print('abcdefg'.lower().find('CD'.lower())) # 2

# 文字列検索で文字列が見つからない場合の挙動
print('abcdefg'.find('x'))  # -1
# print('abcdefg'.index('x')) # ValueError が発生する

# 文字列の出現回数のカウント
print('abc ABC abc'.count('abc')) # 2

# 文字列の有無のチェック
print('cd'  in 'abcdefg')  # True
print('xyz' in 'abcdefg')  # False

# 先頭の文字列チェック
print('[test]'.startswith('[')) # True
print('[test]'.startswith('@')) # False

# 末尾の文字列チェック
print('[test]'.endswith(']'))   # True

# 置換
print('test test'.replace('t', 'T')) # TesT TesT

# 文字列の分割
print('This is a pen.'.split()) # ['This', 'is', 'a', 'pen.']
print('2025-12-31'.split('-'))  # ['2025', '12', '31']

# 配列を結合して文字列にする
print('-'.join(['2025', '12', '31'])) # 2025-12-31

# UTF-8 のバイナリーに変換
print("あいうえお".encode('utf-8')) # b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a'

# バイナリーを UTF-8 の文字列に変換
print(b'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a'.decode('utf-8')) # あいうえお

8. 値の比較

コード
print(1 == 1)          # True
print(1 == '1')        # False
print(5 >= 1)          # True
print(5 != 1)          # True
print(1.0 == 1)        # True

print('abc' == 'abc')  # True
print('abc' < 'xyz')   # True
print('abc' < 'abcd')  # True

print(1 == True)       # True
print(0 == False)      # True

print(None == 0)       # False
print(None == False)   # False
print(None == True)    # False

print("abc" is None)    # False
print("abc" == None)    # False : 非推奨

list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 == list2)  # True  : 値の比較
print(list1 is list2)  # False : オブジェクトの比較

Python は自動的に型変換を行わないため、=== はありません。

【重要】
「==」は値の比較ですが、クラスで __eq__ をオーバーライドして値を比較するようにしていない場合は、同一オブジェクトかどうかの比較になるため要注意。

9. 型の比較

コード
print(type(1)     is int)     # True
print(type(1.0)   is int)     # False
print(type(1.1)   is int)     # False
print(type(1.1)   is float)   # True

print(type("abc") is str)     # True
print(type(True)  is bool)    # True
print(type((1,2,3)) is tuple) # True
print(type({'name': '太郎', 'age': 12}) is dict) # True

print(isinstance([1, 2, 3], list)) # True : サブクラスの場合も True 
print(type([1, 2, 3]) is list)     # True : 完全に同じクラスの場合のみ True
                                   #        list のサブクラスの場合は False になるので要注意

print(isinstance(True, int))       # True : bool は int のサブクラスのため True

10. 型の変換

コード
# 文字列を数値に変換
print(1 + int('2'))        # 3
print(1 + int('002'))      # 3
print(1 + int('  2'))      # 3
print(1 + int(' 2 '))      # 3
# print(1 + int('abc2'))   # ValueError
print(1 + float('123.4'))  # 124.4
print(1 + float('1'))      # 2.0
print(bool('True'))        # True

# 数値を文字列に変換
print('123' + str(456))    # 123456
print('123' + str(456.0))  # 123456.0
print('123' + str(456.00)) # 123456.0
print('[' + str(True) + ']')  # [True]

11. 10進数と16進数の変換

コード
# 数値を16進数の文字列に変換
print(hex(65535))     # 0xffff
print(hex(65535 + 1)) # 0x10000

# 16進数の文字列を数値に変換
print(int('0xffff',  16))  # 65535
print(int('0x10000', 16))  # 65536

12. 論理演算

or, and, not を使用します。!, &&, || は使えません。

コード
x = 10
print(x == 10 or x == 20)  # True : x が10または20の場合にTrue
print(100 >= x and x >= 0) # True : x が0以上100以下の場合にTrue

x = 10
print(not (100 >= x and x >= 0)) # False : 「x が0以上100以下」でない場合にTrue

重要

論理演算の結果は、
他の言語では bool になることが多いですが、
Python では、最後に評価した値になります。

例えば次のコードの場合は、[1, 2, 3] が返ります。
コード
print([] or  [] or  [1, 2, 3] or  [])  # [1, 2, 3]
左から評価して行き、
[] は bool([]) で False、
次の [] も False、
次の [1, 2, 3] は bool([1, 2, 3]) で True となるため、この時に評価が終了し、[1, 2, 3] が返ります。

制御構文で使う場合は、[1, 2, 3] をまた bool にして判定します。
コード
if [1, 2, 3]:
    print("実行される")

また、これを利用して、値が空だった場合は、初期値を設定することができます。
コード
s1 = ''
s2 = s1 or 'default'  # bool(s1) は False、bool('default') は True のため、'default' が返る
print(s2)  # default

and の場合も同様です。
コード
print([] and [] and [1, 2, 3] and [])  # []
[] は bool([]) で False となり、この時点で評価が終了するため、[] を返します。

参考

コード
# True として扱われる
print(bool(123))       # True
print(bool('abc'))     # True
print(bool([1, 2, 3])) # True

# False として扱われる
print(bool([]))        # False
print(bool(''))        # False
print(bool(0))         # False
print(bool(None))      # False

ちなみに、論理演算では [] は False のように扱われますが、[] == False と言う訳ではなありません。

13. 条件分岐 - if

コード
a = 1
b = 2
if a > b:
    print('a は b より大きい')
elif a < b:
    print('a は b より小さい')
else:
    print('a と b は等しい')

# 何も処理しない場合は、pass と書く必要がある
if a > b:
    pass

# s が None でない場合に実行
s = 'hello'
if s and print(s):
    pass

14. ループ - for

コード
for i in range(10): # range(10) は [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    if i == 6:
        break
    if i % 2 == 0:
        continue
    print(i) # 1 3 5
else:
    print("6 が見つかりませんでした") # break が実行されなかった場合に実行される


for i in range(5):
    print(i) # 0 1 2 3 4

for i in range(1, 6):
    print(i) # 1 2 3 4 5

# 文字列の文字でループ
for char in "Test":
    print(char) # T e s t

# リストの値でループ
for name in ["Taro", "Hanako", "Ichiro"]:
    print(name) # Taro Hanako Ichiro

# リストのインデックスと値でループ
for i, name in enumerate(["Taro", "Hanako", "Ichiro"]):
    print(f'{i}:{name}') # 0:Taro 1:Hanako 2:Ichiro

# 辞書のキーと値でループ
for key, value in {'name': 'Taro', 'age': 15}.items():
    print(f'{key}:{value}') # name:Taro age:15

15. ループ - while

コード
i = 0
while i < 5:
    print(i) # 0 1 2 3 4
    i += 1

16. リスト

コード
values = [10, 20, 30, 40, 50]
print(values) # [10, 20, 30, 40, 50]
print(values[0])   # 10
print(values[4])   # 50
print(values[-1])  # 50  [-1] は末尾から1番目の要素のインデックス
print(values[-2])  # 40  [-2] は末尾から2番目の要素のインデックス
print(values[1:4]) # [20, 30, 40]
print(values[:])   # [10, 20, 30, 40, 50]  全要素。リストのコピーの作成

# 要素数
print(len(values)) # 5 

# 末尾に要素の追加
values.append(60)  
print(values) # [10, 20, 30, 40, 50, 60]

# 指定した位置に要素の追加
values.insert(2, 25) 
print(values) # [10, 20, 25, 30, 40, 50, 60]

# 指定した位置の要素の削除(スライスで指定可能)
del values[0]
print(values) # [20, 25, 30, 40, 50, 60]

# 末尾の要素の削除
del values[-1] # [20, 25, 30, 40, 50]
print(values)

# 末尾の要素の取得と削除
value = values.pop()
print(values) # [20, 25, 30, 40]
print(value)  # 50

# リストを結合して新しいリストの作成(元のリストは影響を受けない)
values2 = values + [100, 100, 100]
print(values2) # [20, 25, 30, 40, 100, 100, 100]
print(values)  # [20, 25, 30, 40]

# 指定した値を検索して、最初に見つかった要素を削除。要素が見つからない場合は ValueError
values2.remove(100) 
print(values2) # [20, 25, 30, 40, 100, 100]

# リストを繰り返す
print([1, 2, 3] * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
print([0] * 10)      # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]  初期化でよく使う

# 出現数のカウント
print([0, 0, 0, 0, 0, 1, 1, 1].count(1))  # 3

# リストの反転
print(reversed([0, 1, 2, 3]))       # <list_reverseiterator object>
print(list(reversed([0, 1, 2, 3]))) # 3, 2, 1, 0

values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(sum(values)) # 55  合計
print(min(values)) # 1   最小値
print(max(values)) # 10  最大値
print(sum(values) / len(values)) # 5.5  平均

# ソート
print(sorted([3, 5, 2, 1, 4])) # [1, 2, 3, 4, 5] 昇順
print(sorted([3, 5, 2, 1, 4], reverse=True)) # [5, 4, 3, 2, 1] 降順

# 全て True なら True
print(all([True, True, True,  True])) # True
print(all([True, True, False, True])) # False
print(list(n == 0 for n in [0, 0, 0, 0, 0])) # [True, True, True, True, True] リスト内包表記の結果
print(all(n == 0 for n in [0, 0, 0, 0, 0]))  # True : 全て 0 なら True

# 1つでも True なら True
print(any([False, False, True,  False])) # True
print(any([False, False, False, False])) # False
print(any(n == 1 for n in [0, 0, 0, 0, 0]))  # False : 1つも 1 がないので False
print(any(n == 1 for n in [0, 0, 1, 0, 0]))  # True  : 1つでも 1 があるので True

# 存在チェック
print(2 in [0, 1, 2, 3, 4])  # True
print(5 in [0, 1, 2, 3, 4])  # False

# 検索
print(['a', 'b', 'c', 'd'].index('d'))              # 3
print(['a', 'b', 'c', 'a', 'b', 'c'].index('a', 2)) # 3 : インデックス 2 から検索開始

# 置換
values = [0] * 10
values[2:8] = ['a', 'b']  # [2] ~ [7] を置換([2] ~ [7] を削除して、'a' と 'b' を挿入)
print(values) # [0, 0, 'a', 'b', 0, 0]

values[4:] = []  # 空のリストを代入して、要素の削除も可能
print(values) # [0, 0, 'a', 'b']

# 注意 スライスを使わない場合は、指定したインデックスの要素にリストが代入される
values[1] = []
print(values) # [0, [], 'a', 'b']

# アンパック
values = [4, 5, 6]
print([1, 2, 3, *values, 7, 8, 9])  # [1, 2, 3, 4, 5, 6, 7, 8, 9] # リストの要素を展開します

17. 辞書(ディクショナリー、マップ、連想配列)

コード
# 辞書の生成
user = {
    'name': 'Taro',
    'age': 15,
}

# 取得
print(user['name']) # Taro

# 存在しないキーを指定して取得
# print(user['tel']) # KeyError が発生する
print(user.get('tel', 'default')) # default : キーがない場合は、デフォルト値を返す

# 設定
user['name'] = 'Tarooo'
user['address'] = 'Tokyo' # 追加
print(user) # {'name': 'Tarooo', 'age': 15, 'address': 'Tokyo'}

# 削除
del user['address']
print(user) # {'name': 'Tarooo', 'age': 15}

# アンパック 辞書の内容を展開します
user2 = {**user, 'address': 'Tokyo'}
print(user2) # {'name': 'Tarooo', 'age': 15, 'address': 'Tokyo'}

# コピー
user2 = { **user } # アンパック : user の要素を展開して辞書の再作成
print(user ) # {'name': 'Tarooo', 'age': 15}
print(user2) # {'name': 'Tarooo', 'age': 15}
print(id(user )) # 2334474518336
print(id(user2)) # 2334473972992

# 要素数の取得
print(user)      # {'name': 'Tarooo', 'age': 15}
print(len(user)) # 2

# キーの存在チェック
print('name' in user) # True : name が存在する

# キーでループ
for key in user.keys():
    print(key)       # name age
    print(user[key]) # Tarooo 15

# 値でループ
for value in user.values():
    print(value) # Tarooo 15

# キーと値でループ
for key, value in user.items():
    print(key)   # name age
    print(value) # Tarooo 15

# 他の辞書で上書き
user = {
    'name': 'Taro',
    'age': 15,
}
user.update({'age': 16, 'address':'Tokyo'})
print(user) # {'name': 'Taro', 'age': 16, 'address': 'Tokyo'}

18. タプル

コード
# タプルの生成
values = (1, 2, 3, 4, 5)
print(values[0])   # 1
print(values[1])   # 2
print(values[2:])  # (3, 4, 5)

# タプルは代入不可
# values[0] = 10  # TypeError

# 多重代入
a, b, c, d, e = values
print(a)  # 1
print(b)  # 2

# 要素でループ。順序は維持されている
for value in values:
    print(value) # 1 2 3 4 5

# タプルを使って、関数で複数の戻り値を返す
def getPoint():
    return (10, 20)

x, y = getPoint()
print(x)  # 10
print(y)  # 20

# タプルの展開
def add(a, b, c):
    return a + b + c

values = (1, 2, 3)
print(add(*values)) # 6 : タプルを展開して複数の引数にしてる

# 辞書のキーとしてタプルを使う
values = {}
values[(3, 5)] = "Test"
print(values[(3, 5)]) # test
print(id((3, 5))) # 2461735692352 : ID が変わらない
print(id((3, 5))) # 2461735692352

# 複数の値で比較
version = (5, 3) # メジャーバージョン:5  マイナーバージョン:3
print(version >= (4, 9)) # True
print(version >= (5, 0)) # True
print(version >= (6, 1)) # False

19. 集合

コード
# 集合の生成。重複は除去される
ids = {1, 1, 1, 2, 2, 2, 3, 3, 3, 0, 0, 0}
print(ids) # {0, 1, 2, 3}

# インデックスを指定した値の取得は不可
# print(ids[2]) # TypeError

# 要素の追加
ids.add(4)
print(ids) # {0, 1, 2, 3, 4}

# 集合の追加。重複は除去される
ids |= {4, 5, 6, 7}
print(ids) # {0, 1, 2, 3, 4, 5, 6, 7}

# 要素の削除
ids.discard(4)
ids -= {5, 6, 7}
ids.discard(999)  # 存在しない値を指定してもエラーにならない
# ids.remove(999) # 存在しない値を指定するとエラーになる
print(ids) # {0, 1, 2, 3}

# 要素のクリア
ids.clear()
print(ids) # set()

# 要素でループ。順序は保証されない
ids = {0, 1, 2, 3}
for id in ids:
    print(id) # 0 1 2 3

# 要素数
print(len(ids)) # 4

# 空かどうかの判定
print(not {1, 2, 3}) # False
print(not {})        # True

# 値を取得して削除
print(ids.pop()) # 0 : どの要素が取得されるかは保証されない
print(ids)       # {1, 2, 3}  pop された値は削除される

# 空になるまで値を取り出してループ
ids = {1, 2, 3, 4, 5}
while (ids):
    print(ids.pop()) # 1 2 3 4 5

print(len(ids)) # 0 : 空になる

# 集合の計算
oldIds = {1, 2, 3}       # 前回のIDの集合
newIds = {      3, 4, 5} # 今回のIDの集合
print(newIds - oldIds) # {4, 5}       : 差集合。今回追加された集合
print(newIds & newIds) # {3, 4, 5}    : 積集合。今回と前回で変更がなかった集合
print(oldIds - newIds) # {1, 2}       : 差集合。今回削除された集合
print(newIds ^ oldIds) # {1, 2, 4, 5} : 対称差集合(XOR)。今回追加または削除された集合

20. 条件演算子(三項演算子)

コード
#     True の時の値 if 条件    else False の時の値
a = 5
print(True         if a <= 10 else False) # True
a = 15
print(True         if a <= 10 else False) # False

# デフォルト値の設定でも使えるが、通常は or を使う
name = 'Taro'
print(name if name else '名無し') # Taro
print(name or '名無し')           # Taro

name = ''
print(name if name else '名無し') # 名無し
print(name or '名無し')           # 名無し

21. 関数

21.1 基本

コード
def add(value1, value2):
    return value1 + value2

print(add(1, 2)) # 3

21.2 引数の省略、引数のデフォルト値

コード
def add(value1 = 0, value2 = 0, value3 = 0):
    return value1 + value2 + value3

print(add(1))       # 1
print(add(1, 2))    # 3
print(add(1, 2, 3)) # 6

21.3 リストをアンパックして複数の引数を渡す

コード
def add(value1, value2, value3):
    return value1 + value2 + value3

values = [1, 2, 3]
print(add(*values)) # 6

21.4 可変長引数

コード
def add(*args):
    print(type(args)) # <class 'tuple'> 引数はタプルで受け取れる
    result = 0
    for value in args:
        result += value

    return result

print(add(1, 2, 3, 4, 5))  # 15

21.5 キーワード引数

引数の名前を指定して、値を渡す
コード
def printUser(name = '', age = '', address = '', tel = ''):
     print(f'名前:{name}')
     print(f'年齢:{age}')
     print(f'住所:{address}')
     print(f'電話:{tel}')

printUser(address='Tokyo', name='Taro', age=15)
# 名前:Taro
# 年齢:15
# 住所:Tokyo
# 電話:


# 辞書をアンパックして、引数を渡す
user = {'name':'Hanako', 'address':'Chiba', 'tel':'xxx-xxx', 'age':20}
printUser(**user)
# 名前:Hanako
# 年齢:20
# 住所:Chiba
# 電話:xxx-xxx

21.6 キーワード引数を辞書で受け取る

コード
def test(p1: int, p2: int, p3:int, **kwargs):
    print(type(kwargs)) # <class 'dict'> 辞書

    for key, value in kwargs.items():
        print(f'key:{key}, value:{value}')
        # key:a, value:10
        # key:b, value:20
        # key:c, value:30

test(1, 2, 3, a=10, b=20, c=30)

21.7 複数の戻り値を返す

コード
def getPoint():
    return (10, 20)

x, y = getPoint()
print(x)  # 10
print(y)  # 20

21.8 関数を変数に代入

コード
def add(value1, value2):
    return value1 + value2

def sub(value1, value2):
    return value1 - value2

def calc(f, value1, value2):
    return f(value1, value2)

# 関数を変数に代入
f = add
print(add(1, 2)) # 3

# 関数を引数として渡す
print(calc(add, 1, 2)) # 3
print(calc(sub, 1, 2)) # -1

21.9 クロージャ

コード
def get_counter():
    count = 0

    def counter():
        nonlocal count # 関数の外側の変数を参照可能にする
        count += 1
        return count
    
    return counter # 関数を返す

counter1 = get_counter()
counter2 = get_counter()

print(counter1()) # 1
print(counter1()) # 2
print(counter1()) # 3
print(counter2()) # 1 : counter1 と counter2 が参照する count が異なるため、counter2 は 1

21.10 属性の追加

コード
def test():
    print(test.name) # 関数自身は同じ関数名で参照できる

# test()           # AttributeError : name が参照できない

test.name = 'Test' # 属性

test()           # Test
print(test.name) # Test

21.11 型ヒント

引数や戻り値の型のヒントを指定できます。
あくまでもヒントのため、実行時には影響しません。
そのため、int に文字列を設定してもエラーになりません。
コード
def add(value1: int, value2: int) -> int:
    return value1 + value2

print(add(1, 2))     # 3
print(add('a', 'b')) # ab

22. クラス

22.1 基本

コード
class User:

    # コンストラクタ
    def __init__(self, name: str):
        # name プロパティの setter で設定する
        self.name = name

    # プロパティ getter
    @property
    def name(self) -> str:
        return self._name
    
    # プロパティ setter
    @name.setter
    def name(self, value: str) -> None:
        if not value or not value.strip():  # not value は not bool(value) と同じ。空の場合は True になる
            raise ValueError('name は必須です')
        self._name = value.strip()  # 先頭が _ の場合は、慣習的に private の意味。但し、外部から参照可能

    # メソッド
    def printName(self) -> None:
        print(self.name)

user = User('Taro')
print(user.name)  # Taro
print(user._name) # Taro : 参照できるが禁止

user.name = 'Hanako'
print(user.name) # Hanako

user.printName() # Hanako

22.2 クラス属性

コード
class Test:
    value:int = 1

obj1 = Test()
obj2 = Test()
print(Test.value) # 1
print(obj1.value) # 1 
print(obj2.value) # 1

Test.value = 123
print(Test.value) # 123
print(obj1.value) # 123
print(obj2.value) # 123

obj1.value = 456 # obj1 にだけ、動的にインスタンス属性が追加される
print(Test.value) # 123
print(obj1.value) # 456 : 動的に追加したインスタンス属性が優先される
print(obj2.value) # 123
同じ名前のインスタンス属性とクラス属性があった場合、エラーにはならずに、インスタンス属性が優先されるので要注意。

22.3 staticメソッド

コード
class Test:
    @staticmethod
    def test():
        print('Test')

    def test2():
        print('Test2')

Test.test()
Test.test2()

obj1 = Test()
obj1.test()

obj2 = Test()
# obj2.test2() # エラー

@staticmethod を付けなくても static メソッドとして実行できますが、基本的には付けた方が良いです。
@staticmethod を付けなかった場合、インスタンスから static メソッドを実行した際に、引数に self がないためエラーとなります。

22.4 クラスメソッド

staticメソッドの違いは、引数に呼び出したクラスが渡されてきます。
これにより、オーバーライドされたクラス属性を参照することができます。
コード
class Test:
    NAME: str= 'Test'  # クラス属性

    @classmethod
    def get_name(cls): # 引数にはインスタンスではなくクラスが渡される
        return cls.NAME

class TestEx(Test):
    NAME = 'TestEx' # クラス属性のオーバーライド

print(Test.get_name())   # Test
print(TestEx.get_name()) # TestEx

22.5 クラスの継承

コード
class Test:
    def __init__(self, value):
        self.value = value

    def test(self):
        self.subTest()

    def subTest(self):
        print(f'SubTest:{self.value}')

# Testクラスを継承
class TestEx(Test):
    def __init__(self, value):
        super().__init__(value) # 親のコンストラクタを呼ぶ

    # オーバーライド
    def subTest(self):
        print(f'SubTestEx:{self.value}')

obj1 = Test(1)
obj1.test() # SubTest:1

obj2 = TestEx(2)
obj2.test() # SubTestEx:2

他の言語のプライマリーコンストラクタと間違えやすいので要注意。

22.6 抽象メソッド

コード
from abc import ABC, abstractmethod

class AbstractTest(ABC): # ABC : Abstract Base Class(抽象基底クラス)を継承する
    def test(self):
        self.subTest()

    @abstractmethod
    def subTest(self):
        pass

class Test(AbstractTest):
    # 抽象メソッドの実装。このメソッドを実装しないとエラー
    def subTest(self):
        print('SubTest')

obj2 = Test()
obj2.test() # SubTest

22.7 多重継承

コード
class TestA:
    def test(self):
        print("A")

class TestB(TestA):
    def test(self):
        print("B")
        super().test() # TestD のインスタンスの場合、TestB の super() は TestC を指す

class TestC(TestA):
    def test(self):
        print("C")
        super().test() # TestD のインスタンスの場合、TestC の super() は TestA を指す

class TestD(TestB, TestC): # TestB と TestC を多重継承。基本的には TestB のメソッドが先に動く
    def test(self):
        print("D")
        super().test() # 

objD = TestD()
objD.test() # D B C A

# メソッド解決順序の確認
print(TestD.__mro__) # MRO(Method Resolution Order)
# (<class '__main__.TestD'>, <class '__main__.TestB'>, <class '__main__.TestC'>, <class '__main__.TestA'>, <class 'object'>)

objB = TestB()
objB.test() # B A

objC = TestC()
objC.test() # C A

22.8 名前マングリング(Name Mangling)

インスタンス属性の名前の先頭が「__」で始まる場合は、名前が「_クラス名__属性名」に変更されて、外部から参照しづらくなります。
プライベートなインスタンス属性を定義したい場合に使用します。
クラスを継承した場合、子クラスのマングリングされた属性と、親クラスのマングリングされた属性は、異なる属性となるので要注意。

Mangling : 切り刻む

コード
class Test:
    def __init__(self):
        self.__value = 1  # __ で始まる属性は、「_クラス名__属性名」に名前が変更される。

class TestEx(Test):
    def __init__(self):
        super().__init__()
        self.__value = 2  # 裏では _TestEx__value のため、Test の __value とは別物になる

obj1 = TestEx()
# print(obj1.__value) # エラー : __value は参照できないのではなく、存在しない
print(obj1._Test__value)    # 1 : 強引に参照できるが、基本的には禁止
print(obj1._TestEx__value)  # 2

メソッドも同様
コード
class Test:
    def __test(self): # __ で始まるため、名前が変更される
        print('Test')

    def test(self):
        self.__test() # クラス内からは参照可能

obj = Test()
obj.test()        # Test
obj._Test__test() # Test : 強引に参照できるが、基本的には禁止
# obj.__test()    # エラー : __test() は名前が変更されているため、存在しない

23. データクラス(dataclass)

クラスに @dataclass を付けると、データを持つクラスを簡単に作成できます。

23.1 基本

通常のクラスの場合
コード
class User:
    def __init__(self, id: int, name: str):
        self.id = id
        self.name = name

obj1 = User(1, 'Taro')
print(obj1)      # <__main__.OldUser object at 0x000001B1DF5B6E40>
print(obj1.id)   # 1
print(obj1.name) # Taro

obj2 = User(1, 'Taro')
print(obj1 == obj2) # False : __eq__ をオーバーライドしていないため、同一オブジェクトかどうかの比較になる

データクラスの場合
コード
from dataclasses import dataclass

@dataclass
class UserData:
    id: int
    name: str

obj = UserData(1, 'Taro') # 全ての型ヒントが __init__ の引数となる
print(obj)      # User(id=1, name='Taro')
print(obj.id)   # 1
print(obj.name) # Taro

obj2 = UserData(1, 'Taro')
print(obj == obj2) # True : インスタンス属性の値で比較される
@dataclass を付けると、型ヒントからインスタンス属性が自動生成されます。
また、__init__、__repr__、__eq__、__ne__、__hash__、__order__ が自動生成されます。

__init__ の引数は、型ヒントで指定した全てのフィールドです。
型ヒントにデフォルト値を指定していない場合は、全ての引数を指定する必要があります。

__eq__ と __ne__ がオーバーライドされて、== と != がインスタンス属性の値で比較するようになります。

__repr__ は、わかりやすい形式になります。
「<__main__.OldUser object at 0x000001B1DF5B6E40>」が「User(id=1, name='Taro')」 になる。

23.2 __hash__ の違い

__hash__ は、@dataclass(frozen=True) にした場合、インスタンス属性の値がハッシュ値となります。
そのため、異なるオブジェクトでも、インスタンス属性の値が同じであれば、同じハッシュ値となります。
通常のクラスでは、オブジェクトごとにハッシュ値が異なるため、辞書のキーとして使用した場合は、動作が変わります。

コード
# 通常のクラス
class User:
    def __init__(self, name: str):
        self.name = name

user1 = User('Taro')
user2 = User('Taro')

data  = {}
data[user1] = 1
data[user2] = 2 # 異なるキーのため、上書きされない
print(data[user1]) # 1
print(data[user2]) # 2


# データクラス
from dataclasses import dataclass

@dataclass(frozen=True)
class UserData:
    name: str

userData1 = UserData('Taro')
userData2 = UserData('Taro')

data  = {}
data[userData1] = 1
data[userData2] = 2 # 同じキーのため、上書きされる
print(data[userData1]) # 2
print(data[userData2]) # 2

23.3 属性の比較

@dataclass(frozen=True, order=True) のように、order=True をつけると、「<」「<=」「>」「>=」で比較できるようになります。
型ヒントが複数ある場合は、指定した順番で評価されます

コード
from dataclasses import dataclass

@dataclass(frozen=True, order=True)
class UserData:
    id: int
    name: str

print(UserData(1, 'aaa') < UserData(1, 'bbb')) # True
print(UserData(1, 'aaa') < UserData(2, 'aaa')) # True
print(UserData(1, 'aaa') < UserData(2, 'bbb')) # True
print(UserData(2, 'aaa') < UserData(1, 'bbb')) # False : 左辺の方が id が大きいため False

23.4 インスタンス属性を変更不可にする

@dataclass(frozen=True) にすると、インスタンス生成後、インスタンス属性の変更が不可になります。

コード
from dataclasses import dataclass

@dataclass(frozen=True)
class UserData:
    name: str

user = UserData('Taro')
# user.name = 'Hanako' # エラー
# del user.name        # エラー

変更不可にする実現方法は、__setattr__ と __delattr__ をオーバーライドしてると思われます。
コード
class User:
    def __init__(self, name: str):
        self.name = name

    # 代入される時に呼ばれる
    def __setattr__(self, name, value):
        print('set')
    
    # del される時に呼ばれる
    def __delattr__(self, name):
        print('del')

user = User('Taro')
user.name = 'Hanako'
del user.name

24. 定数

24.1 全て大文字の変数を定数とする

慣習的なものなので、変更は可能。
コード
MAX_VALUE = 100

MAX_VALUE = 200
print(MAX_VALUE) # 200 : 変更できてしまうので要注意

24.2 読み取り専用のプロパティを定数とする

コード
class AppConst:
    @property
    def MAX_VALUE(self) -> int:
        return 256
    
appConst = AppConst()
print(appConst.MAX_VALUE) # 256
# appConst.MAX_VALUE = -1 # エラー : setter がないため。

25. ビット演算

コード
# OR
a = 0b00000001
b = 0b00000110
c = a | b
print(f'{c:08b}') # 00000111

# AND
a = 0b00001111
b = 0b00001000
c = a & b
print(f'{c:08b}') # 00001000

#XOR
a = 0b00001111
b = 0b11111111
c = a ^ b
print(f'{c:08b}') # 11110000 : 全てのビットを1にして XOR するとビットが反転する

# NOT
a = 0b00000001
c1 = ~a 
c2 = ~a & 0b11111111  # 符号なしにするには、必要なビット数で AND する
print(f'{c1:08b}') # -0000010 = -2
print(f'{c2:08b}') # 11111110

# 特定のビットをクリアする
a = 0b11111111
b = 0b00001000 # 4ビット目をクリアする場合
c = a & ~b     # 反転させて AND する
print(f'{c:08b}') # 11110111 : 4ビット目がクリアされる

# ビットが立っているか確認
a = 0b00000111
print(bool(a & 0b00000100)) # True  : 3ビット目が立っている
print(bool(a & 0b00001000)) # False : 4ビット目は立っていない

26. 列挙型

コード
from enum import Enum, StrEnum, Flag, auto

# 値が数値の列挙型
class YesNo(Enum):
    YES = 1
    NO  = 2

result = YesNo.YES
print(result == YesNo.YES) # True
print(result == YesNo.NO)  # False
print(YesNo.YES.name)      # YES
print(YesNo.YES.value)     # 1
print(YesNo.NO.value)      # 2

# auto() で自動採番
class Color(Enum):
    RED   = auto()
    GREEN = auto()
    BLUE  = auto()

print(Color.RED.name)    # RED
print(Color.RED.value)   # 1
print(Color.GREEN.value) # 2
print(Color.BLUE.value)  # 3

# auto() はクラスごとに採番される
class Color2(Enum):
    RED   = auto()
    GREEN = auto()
    BLUE  = auto()

print(Color2.RED.value) # 1 : Color とは別に採番されている


class Color3(Enum):
    NONE  = 10
    RED   = auto()
    GREEN = auto()
    BLUE  = auto()

print(Color3.RED.value)   # 11 : auto() の直前の値 + 1
print(Color3.GREEN.value) # 12
print(Color3.BLUE.value)  # 13


# 値が文字列の列挙型
class Color4(StrEnum):
    NONE  = 'NONE'
    RED   = auto()
    GREEN = auto()
    BLUE  = auto()

print(Color4.RED.value)   # red   : auto() で小文字の名前が設定される
print(Color4.GREEN.value) # green


# フラグとして使う列挙型。auto() で 2 の累乗が自動採番される
class Permission(Flag):
    READ    = auto()
    WRITE   = auto()
    DELETE  = auto()
    EXECUTE = auto()

print(Permission.READ.value)     # 1
print(Permission.WRITE.value)    # 2
print(Permission.DELETE.value)   # 4
print(Permission.EXECUTE.value)  # 8

# 2 の累乗で定義されているため、ビット演算が可能
permission = Permission.READ | Permission.WRITE

# フラグの確認
print(f'{permission.value:08b}')      # 00000011 : 2 進数で表示
print(Permission.READ    in permission) # True
print(Permission.WRITE   in permission) # True
print(Permission.DELETE  in permission) # False
print(Permission.EXECUTE in permission) # False

# フラグの追加
permission |= Permission.DELETE
print(f'{permission.value:08b}')      # 00000111

# フラグの削除
permission &= ~Permission.READ        # ~ でフラグを反転させて、削除したいフラグだけが 1 の状態にして AND
print(f'{permission.value:08b}')      # 00000110

27. ラムダ式

コード
# 基本は1行で書く
#            引数  : 式
add = lambda v1, v2: v1 + v2
print(add(1, 2)) # 3

# 括弧で囲むと改行可能
add = lambda v1, v2: (
    v1 + v2
)
print(add(1, 2)) # 3

# バックスラッシュで改行可能
add = lambda v1, v2: \
    v1 + v2
print(add(1, 2)) # 3

# 奇数の場合 True
isOdd = lambda v: v % 2 == 1
print(isOdd(1)) # True
print(isOdd(2)) # False

28. map

リストの各要素に関数を適用します。
map の戻り値は、使い切り型のイテレータです。
for や list() で使った時に評価されます。

コード
values = [1, 2, 3, 4, 5]
values2 = map(lambda value:value + 5, values)

# list() を実行した時に、map の中のラムダ式が実行される
print(list(values2))  # [6, 7, 8, 9, 10]

print(values)         # [1, 2, 3, 4, 5] 元のリストは影響を受けない


# map に関数を渡す場合
def add5(value):
    return value + 5

values3 = map(add5, values)
print(list(values3))  # [6, 7, 8, 9, 10]

注意:map() は使い切り型のイテレータのため、2回目の for は何も実行されません。
コード
values = [1, 2, 3, 4, 5]
values2 = map(lambda value:value + 5, values)

for value in values2:
    print(value)  # 6 7 8 9 10

# values2 はイテレータの現在位置が最後まで移動しているため、再度ループしても何も実行されない
# 繰り返し使う場合は、list() でリスト化しておく
for value in values2:
    print(value)  # 実行されない

29. filter

リストの各要素を関数で絞り込みます。
map() と同様に遅延評価です。

コード
values = [1, 2, 3, 4, 5]

# 奇数だけ抽象する
values2 = filter(lambda v: v % 2 == 1, values)

print(list(values2))  # [1, 3, 5]
print(values)         # [1, 2, 3, 4, 5] 元のリストは影響を受けない

30. zip

各リストの同一インデックスの要素をタプルにして取得します。

コード
ids   = [1, 2, 3, 4, 5, 6, 7]
names = ['Taro', 'Hanako', 'Jiro', 'Saburo', 'Shiro']

for id, name in zip(ids, names):
    print(f'ID:{id}, Name:{name}')
    # ID:1, Name:Taro
    # ID:2, Name:Hanako
    # ID:3, Name:Jiro
    # ID:4, Name:Saburo
    # ID:5, Name:Shiro
    # 6 と 7 は、対応する names の要素がないため出力されない

31. リスト内包表記

コード
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 値を2倍にしたリストを作成する
print([v * 2 for v in values])
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 奇数の値だけを抽出したリストを作成する
print([v for v in values if v % 2 == 1])
# [1, 3, 5, 7, 9]

# if を複数指定することもできる
print([v for v in values if v % 2 == 1 if v >= 5])
# [5, 7, 9]

# 2重ループ
print([f'{i}-{j}' for i in range(1, 4) for j in range(1, 6)])
# ['1-1', '1-2', '1-3', '1-4', '1-5', '2-1', '2-2', '2-3', '2-4', '2-5', '3-1', '3-2', '3-3', '3-4', '3-5']

# 2重ループ  + if
print([f'{i}-{j}' for i in range(1, 9) if i > 5 for j in range(1, 9) if j > 5])
print([f'{i}-{j}' for i in range(1, 9) for j in range(1, 9) if i > 5 if j > 5])
# ['6-6', '6-7', '6-8', '7-6', '7-7', '7-8', '8-6', '8-7', '8-8']
# 同じ結果になるが、前者は i > 5 の時だけ内側のループが実行されるため速い

32. ウォルラス演算子(Walrus Operator)

「:=」で代入すると、代入後にその値をそのまま利用することができます。
コード
# 式で代入を行い、その値を利用する
b = (a := 3) + 5  # a に 3 が代入されて、そのあとで a + 5 が計算される
print(a) # 3
print(b) # 8

# if の式で代入を行い、その値を利用する
values = [1, 2, 3, 4, 5]
if (length := len(values)) > 3:
    print(length) # 5

# 2倍にした値が 3 より大きい値のリストを作成する
# 比較の時に使った tmp を、そのままリストの要素として使える
print([tmp for v in values if (tmp := v * 2) > 3])
# [4, 6, 8, 10]

33. 例外処理

コード
import sys, traceback

try:
    result = 5 / 0  # 0 で割っているため、ZeroDivisionError が発生する
except Exception as e:
    # 例外が起きた場合に実行される
    print(e.__class__.__name__) # ZeroDivisionError : 例外の名前
    print(type(e).__name__)     # ZeroDivisionError : 例外の名前
    print(str(e))               # division by zero : エラーメッセージ
    print(repr(e))              # ZeroDivisionError('division by zero')

    # トレースバック(スタックトレース)の表示
    traceback.print_exc()
    print(''.join(traceback.format_tb(e.__traceback__)))

    # 例外情報の取得
    exc_type, exc_value, exc_traceback = sys.exc_info()
    print(exc_type)        # <class 'ZeroDivisionError'>
    print(exc_value)       # division by zero
    print(exc_traceback)   # <traceback object at 0x0000028AEBA02E40>
    print(e.__traceback__) # <traceback object at 0x0000028AEBA02E40>
else:
    # 例外が起きなかった場合に実行される
    print(result)
finally:
    # 必ず実行される
    print('finally')

34. ダンダー属性、ダンダーメソッド

先頭に「__(ダブルアンダースコア)」が付く属性やメソッドです。

34.1 __init__

コンストラクタのメソッド
コード
class Test:
    def __init__(self, value: int):
        self.value = value

obj = Test(5)
print(obj.value) # 5

34.2 __class__

オブジェクトからクラスを参照できます。
コード
class Test:
    MAX_VALUE: int = 100 # クラス属性

class TestEx(Test):
    MAX_VALUE: int = 1000 # クラス属性のオーバーライド

obj = Test()
print(obj.__class__)           # <class '__main__.Test'>
print(obj.__class__.__name__)  # Test : クラス名。  ハードコーディングしないで参照できる
print(obj.__class__.MAX_VALUE) # 100  : クラス属性。ハードコーディングしないで参照できる

obj = TestEx()
print(obj.__class__.MAX_VALUE) # 1000

34.3 __dict__

コード
class Test:
    MAX_VALUE: int = 100 # クラス属性

    def __init__(self):
        self.value1 = 0 # インスタンス属性
        self.value2 = 0 # インスタンス属性
        
    def test(self):
        pass

obj = Test()
print(obj.__dict__)  # {'value1': 0, 'value2': 0}

for key, value in Test.__dict__.items():
    print(f'key:{key}, value:{value}')
    # key:__module__, value:__main__
    # key:__firstlineno__, value:972
    # key:__annotations__, value:{'MAX_VALUE': <class 'int'>}
    # key:MAX_VALUE, value:100
    # key:__init__, value:<function Test.__init__ at 0x0000025824ACFA60>
    # key:test, value:<function Test.test at 0x0000025824AE4720>
    # key:__static_attributes__, value:('value1', 'value2')
    # key:__dict__, value:<attribute '__dict__' of 'Test' objects>
    # key:__weakref__, value:<attribute '__weakref__' of 'Test' objects>
    # key:__doc__, value:None

34.4 __str__ 、__repr__

コード
class Test:
    def __init__(self, value):
        self.value = value

    # print() や str() で呼ばれる
    def __str__(self):
        return str(self.value)
    
    # repr() で呼ばれる。または __str__ がない場合に、print() や str() で呼ばれる
    def __repr__(self):
        return f'value:{self.value}' # 開発者に向けた内容

obj = Test(5)
print(obj)       # 5       : __str__ が呼ばれる。ない場合は、__repr__ が呼ばれる
print(str(obj))  # 5       : __str__ が呼ばれる。ない場合は、__repr__ が呼ばれる
print(repr(obj)) # value:5 : __repr__ が呼ばれる

# 辞書を print() した場合、__repr__ の有無で表示が変わる
values = [obj, obj, obj]
print(values) # [value:5, value:5, value:5]
              #
              # __repr__ がない場合は、次のように表示されるため、わかりづらい
              # [<__main__.Test object at 0x0000014A90536E40>, 
              #  <__main__.Test object at 0x0000014A90536E40>, 
              #  <__main__.Test object at 0x0000014A90536E40>]

34.5 __getitem__

オブジェクトの属性をリストや辞書のように参照できます。
コード
class Test:
    def __init__(self, values):
        self.values = values

    def __getitem__(self, index):
        return self.values[index]

obj = Test([1, 2, 3, 4, 5])
print(obj[0])   # 1 : __getitem__ により、リストのように参照できる
print(obj[1])   # 2
print(obj[2:4]) # [3, 4] : __getitem__ の index には slice オブジェクトが渡される


class Test2:
    def __init__(self, id, name):
        self.id   = id
        self.name = name

    def __getitem__(self, key):
        return getattr(self, key) # 指定した名前のインスタンス属性の取得

obj2 = Test2(1, 'Taro')
print(obj2['id'])   # 1    : __getitem__ により、辞書のように参照できる
print(obj2['name']) # Taro

34.6 __call__

オブジェクトを関数のように呼び出せるようにします。
コード
# 加算
class Add:
    def __call__(self, value1, value2):
        return value1 + value2

add = Add()
print(add(1, 2)) # 3 : __call__ により、関数のように呼び出せる

# カウンター
class Counter:
    def __init__(self):
        self.count = 0
    def __call__(self):
        self.count += 1
        return self.count

counter = Counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3

34.7 __doc__

コード
def add(v1, v2):
    """
    引数の値を加算します。
    """
    return v1 + v2

f = add(1, 2)
print(add.__doc__) # 「引数の値を加算します。」が表示される
関数やクラスの直下に """ で文字列を囲うと、自動的に __doc__ に代入されます。
これにより、VS Code などで関数にマウスカーソルを合わせた時などに、__doc__ の内容がヒントとして表示されるようになります。

35. デコレーター

35.1 既存の関数への上書き

Python は、既存の関数に対して、他の値を上書きすることができます。
コード
def test():
    return 123

f = test    # f に関数の参照を代入
test = 1    # 既存の関数を上書き
print(test) # 1 : 上書きされたため、関数ではなくなる
print(f())  # 123 : 関数はオブジェクトのため、参照があれば実行可能

35.2 デコレーター構文

デコレーター構文を理解するため、あえて不完全なコードにしています。
コード
def test(func): # @test の次の行の関数が引数で渡されてくる
    print(func.__name__) # print_hello : @test の次の行の関数が渡されてきている
    return 0    # @test の次の行の関数が、0 になる

@test # デコレーター構文
def print_hello():
    print('Hello') # まだ実行されない

print(print_hello) # 0 : print_hello は、test() の戻り値で上書きされているため 0 になる
# print_hello()    # エラー : print_hello は関数ではなく、数値に置き換わっている

# 実行結果
# print_hello
# 0
@test が見つかると、次の行にある関数 print_hello() を引数にして test() を実行します。
test() の戻り値(0)は、print_hello に代入されます。
そのため、print(print_hello) は 0 が表示されます。
print_hello は関数ではなくなっているため、print_hello() をするとエラーになります。

35.3 デコレーター

既存の関数の前後に処理を追加します。
コード
from functools import wraps

def test(func):
    # 関数の中で関数を定義して返す
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('-----')
        result = func(*args, **kwargs) # 元の関数を実行する
        print('-----')
        return result
    
    return wrapper

@test # デコレーター構文
def print_hello():
    """ Hello と表示します """
    print('Hello')

print_hello() # -----
              # Hello
              # -----

print(print_hello.__name__) # print_hello
help(print_hello)           # 正常に表示される
処理が @test に来ると、test() に 次の行の関数 print_hello が渡されます。
渡された関数は、test() の内部で定義される関数で使われます。この時に、print_hello の前後に処理を追加できる。
そして test() は内部で定義した関数を返し、
それが、@test の次の行の print_hello に代入されます。

ちなみに、wrapper() の前に @wraps(func) を付けなくても動作しますが、
付けない場合は、print_hello の __name__ や __doc__ が wrapper() のものになります。

クラスでも書ける
コード
from functools import wraps

class Test:
    def __init__(self, num):
        self.num = num
    
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('-' * self.num)
            result = func(*args, **kwargs) # 元の関数の実行
            print('-' * self.num)
            return result
        
        return wrapper

@Test(10)  # print_hello に wrapper() が代入される
def print_hello():
    """ Hello と表示します """
    print('Hello')
    return 0

print_hello() # ----------
              # Hello
              # ----------

print(print_hello.__name__) # print_hello
help(print_hello)           # 正常に表示される
@Test(10) では、Test クラスの __call__ が呼ばれます。
__call__ は、これはクラスを関数と同じように呼べるようにする仕組みです。
引数は次の行の関数 print_hello です。
そして、__call__ の中で関数 wrapper を定義して返します。

これにより、
@Test(10)
は、
@wrapper
のようになります。

そして、この wrapper が、次の行の関数 print_hello に代入されます。
これにより、print_hello() を実行すると、
元の print_hello() を含んだ wrapper() が実行されるようになります。

36. スクリプトとして実行された時だけ関数を実行する

最初に実行されたモジュールでは、__name__ に、__main__ と言う文字列が設定されます。
インポートされた場合は、__name__ に、モジュール名が設定されます。

そのため、次のように書くと、スクリプトとして実行された時だけ、特定の関数を実行することができます。
インポートされた場合は実行されません。
コード
if __name__ == "__main__":
    main()
< 前の記事

Python 用の VS Code の設定

次の記事 >

外部ファイルのインポート

YouTube X

新着一覧

  • SCSS のインストールVite
  • Tailwind CSS のプロジェクトの作成Tailwind
  • TypeScriptのプロジェクトの作成Vite
  • Flask のインストールと動作確認Python
  • 簡易Webサーバーの作成Python
  • pipeline で文章の生成Python
  • pipeline で文章の要約Python
  • 音声から文字起こしPython
  • Node.js のインストールNode.js
  • .ps1(PowerShellスクリプト)を実行可能にするPowerShell

アーカイブ

  • 2025/12
  • 2025/11
  • 2025/10
  • 2025/09
  • 2025/08

以前のカテゴリー一覧

  • CakePHP3
  • CentOS7
  • HTML・CSS・JavaScript
  • Haskell
  • JavaScript
  • Kotlin
  • Laravel5
  • PHP
  • Python
  • Ruby
  • RubyOnRails5
  • TypeScript
  • Vue.js
  • Webサーバ講座
  • Webプログラミング講座
  • jQuery
  • linux
  • パソコン講座
  • ブログ
  • プログラミング講座
  • メモ帳作成講座
  • 数学

Copyright © 9cubed. All Rights Reserved.

プライバシーポリシー 利用規約
▲