問題はどのようなものですか?
タスクをタスクリストに追加するための関数を書いていると想像してみてください。
def add_task(task, todo_list=[]):
todo_list.append(task)
return todo_list
# 最初のタスクを追加しますprint (add_task ("ミルクを買う")) # ['ミルクを買う']クを追加しますprint(add_task("犬を散歩させる")) # 期待: ['犬を散歩させる'] # 結果は次のとおりです。 ['牛乳を買う', '犬を散歩させる']⚠️ 何が起こったのか? 1つのタスクを含む新しいリストを受け取ることを期待していましたが、2つ目のタスクが最初のタスクに追加されました。しかも、前のリストはどこにも保存していません。魔法?いいえ、これはmutable default argumentsです。
なぜこのようなことが起こるのでしょうか?
Pythonが関数を作成するとき、デフォルトでオブジェクトを作成します 1回 関数が定義されたときに、関数が呼び出されるたびにではなく。このオブジェクトは保存され、次の呼び出しごとに再利用されます。
実際に何が起こっているのか見てみましょう。
def add_task(task, todo_list=[]):
todo_list.append(task)
print(f"List ID: {id(todo_list)}")
return todo_list
add_task("First task") # リストID:140234567891234add_task (" 2番目のタスク") #リストID:140234567891234 (同じものです!) add_task (" 3番目のタスク") #リストID:140234567891234 (また同じものです!)💡 重要なポイント: 毎回同じリストオブジェクトが使用されます。すべての関数呼び出しはメモリ内の同じリストで動作するため、すべての変更が累積されます。
どのような種類のデータが影響を受けますか?
この問題は、Pythonのすべての可変データ型に関係します。これには、リスト、辞書、セット、カスタムオブジェクトが含まれます。しかし、数値、文字列、タプル(不変タイプ)は変更できず、新しいものを作成することしかできないため、正常に動作します。
# 辞書付きの問題コードdef add_user(name, users_dict={}):
users_dict[name] = True
return users_dict
print(add_user("Alexey")) # {'Alexey': True}print(add_user("Maria")) # {'Alexey': True, 'Maria': True} - 両方ともここにあります!# 文字列を含む安全なコード (immutable)def greet(name, greeting="こんにちは"): greeting = greeting + ", " + name
return greeting
print(greet("Ivan")) # こんにちは、Ivanprint(greet("オルガ")) # こんにちは、オルガ - すべて期待通りに動作します正しい選択です。
この問題の古典的な解決策は、Noneをデフォルト値として使用し、関数内に新しいオブジェクトを作成することです。
def add_task(task, todo_list=None):
if todo_list is None:
todo_list = []
todo_list.append(task)
return todo_list
# これで、すべてが正しく機能しますprint(add_task("牛乳を買う")) # ['牛乳を買う']print(add_task("犬を散歩させる")) # ['犬を散歩させる']✅ ルール: リストを渡さないと、新しい空のリストが作成されます。問題が解決しました!
辞書でも同じように機能します。
def create_profile(name, data=None):
if data is None:
data = {}
data['name'] = name
data['created_at'] = 'today'
return data
profile1 = create_profile("Alexey")
profile2 = create_profile("Maria")
print(profile1) # {'name': 'アレクセイ', 'created_at': '今日'}print(profile2) # {'name': 'マリア', 'created_at': '今日'}
いつ役に立ちますか?
興味深いことに、この動作は、たとえば結果をキャッシュするために、特別に使用できることがあります。
def get_config(config_cache={}):
if 'data' not in config_cache:
print("Loading configuration...")
config_cache['data'] = "important settings"
return config_cache['data']
print(get_config()) # 構成を読み込んでいます... 重要な設定print (get_config ()) # 重要な設定(読み込みはありません)💡 注意: しかし、実際のコードでは、キャッシュには functools.lru_cache のような特別なツールを使用することをお勧めします。これにより、コードがよりわかりやすくなります。
このミスを避けるにはどうすればよいですか?
単純なルールを覚えておいてください。関数の引数のデフォルト値として、変更可能なオブジェクト(リスト、辞書、セット)を使用しないでください。常に None を使用し、関数内に必要なオブジェクトを作成してください。
このエラーは非常に一般的であるため、多くのリント(コード検証ツール)は特にこのエラーについて警告しています。PyCharm、pylint または flake8 を使用した VSCode を使用している場合、このようなコードを書こうとすると警告が表示されます。
初めてのPythonを正しく学びましょう!
このトピックやその他の重要なトピックについては、 コディケ - すべてのニュアンスを明確な説明で理解し、興味深いタスクで実践的に強化します。各テーマには実際の開発例が添えられており、実践的な課題は得られた知識をすぐに応用するのに役立ちます。
学習中に質問があったり、複雑な点について話し合いたい場合は、 2000人の志を同じくする人々 アクティブ Telegramチャンネル、プログラミングの道を理解し、サポートするのに常に役立ちます!
