\

Pythonでは、変数に初期値がない場合、その変数にNoneを設定することが一般的です。また、関数の引数のデフォルト値としてNoneを設定し、その後で具体的な値を代入することもよくあります。

例えば、PytorchのResnetの実装では、norm_layerという引数のデフォルト値をNoneに設定し、その後でnn.BatchNorm2dを代入しています。

class BasicBlock(nn.Module):
    def __init__(self, ...(その他引数省略) norm_layer: Optional[Callable[..., nn.Module]] = None) -> None:
        super(BasicBlock, self).__init__()
        if norm_layer is None:
            norm_layer = nn.BatchNorm2d
        ...(実装続く)

このようにデフォルト値をNoneに設定する理由は、デフォルト値は実行時、モジュールがロードされた時ただ1度だけ評価されるため、動的な値に奇妙な振る舞いをもたらす可能性があるからです。

具体的な例を見てみましょう。以下の関数は、データを受け取り、json形式であればそれを辞書型に変換し、jsonでなければ空の辞書を返す関数です。

def bad_decode(data, default={}):
    try:
        return json.loads(data)
    except ValueError:
        return default

この関数を使って以下のように実行すると、予想外の結果が得られます。

foo = bad_decode('bad data')
foo['staff'] = 5
bar = bad_decode('also bad data')
bar['meep'] = 1
print("foo", foo)
print("bar", bar)
# 出力: foo {'staff': 5, 'meep': 1}
# 出力: bar {'staff': 5, 'meep': 1}

これを防ぐためには、デフォルト値をNoneに設定し、関数内で具体的な値を代入するようにします。

def good_decode(data, default=None):
    if default is None:
        default = {}
    try:
        return json.loads(data)
    except ValueError:
        return default

このようにすると、期待通りの結果が得られます。

foo = good_decode('bad data')
foo['staff'] = 5
bar = good_decode('also bad data')
bar['meep'] = 1
print("foo", foo)
print("bar", bar)
# 出力: foo {'staff': 5}
# 出力: bar {'meep': 1}

以上のように、Pythonではデフォルト値をNoneに設定することで、予期せぬ挙動を防ぐことができます。この知識を持つことで、Pythonのコードをより理解しやすく、安全に書くことができます。

投稿者 admin

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です