ゼファーネットのロゴ

5 つのよくある Python の落とし穴 (およびその回避方法) – KDnuggets

日付:

Python でよくある 5 つの落とし穴 (およびその回避方法)
著者による画像
 

Python は、そのシンプルさと読みやすさで知られる、初心者に優しい多用途のプログラミング言語です。ただし、そのエレガントな構文には、経験豊富な Python 開発者さえ驚くような癖がつきものです。これらを理解することは、バグのないコードを書くために、あるいは、苦痛のないデバッグを行うために不可欠です。

このチュートリアルでは、変更可能なデフォルト、ループ内の変数スコープと内包表記、タプルの代入など、注意点のいくつかについて説明します。簡単な例をコーディングして見てみましょう なぜ 物事はそのように機能し、また、 これらは回避できます (実際にできるなら 🙂)。 

だから始めましょう!

Python では、変更可能なデフォルトは一般的な鋭い角です。リストや辞書などの変更可能なオブジェクトをデフォルトの引数として使用して関数を定義すると、予期しない動作が発生することがあります。 

デフォルト値は、関数が呼び出されるたびではなく、関数の定義時に 1 回だけ評価されます。。これにより、関数内でデフォルトの引数を変更すると、予期しない動作が発生する可能性があります。

例を見てみましょう:

def add_to_cart(item, cart=[]):
    cart.append(item)
    return cart

 

この例では、 add_to_cart 項目を取得してリストに追加する関数です cart。 のデフォルト値 cart は空のリストです。つまり、追加するアイテムを指定せずに関数を呼び出すと、空のカートが返されます。 

以下にいくつかの関数呼び出しを示します。

# User 1 adds items to their cart
user1_cart = add_to_cart("Apple")
print("User 1 Cart:", user1_cart)  

 

Output >>> ['Apple']

 

これは期待どおりに機能します。しかし、今はどうなっているのでしょうか?

# User 2 adds items to their cart
user2_cart = add_to_cart("Cookies")
print("User 2 Cart:", user2_cart) 

 

Output >>>

['Apple', 'Cookies'] # User 2 never added apples to their cart!

 

デフォルトの引数はリスト (可変オブジェクト) であるため、関数呼び出し間でその状態が保持されます。だからあなたが電話するたびに add_to_cart、関数定義中に作成された同じリスト オブジェクトに値を追加します。この例では、すべてのユーザーが同じカートを共有しているようなものです。

避ける方法

回避策として、次のように設定できます。 cart 〜へ None 次のように関数内でカートを初期化します。

def add_to_cart(item, cart=None):
    if cart is None:
        cart = []
    cart.append(item)
    return cart

 

したがって、各ユーザーは個別のカートを持つようになりました。 🙂

Python 関数と関数引数について復習が必要な場合は、こちらをお読みください。 Python 関数の引数: 決定版ガイド.

Python のスコープには奇妙な点があるため、独自のチュートリアルが必要です。しかし、ここではそのような奇妙な点を一つ見てみましょう。

次のスニペットを見てください。

x = 10
squares = []
for x in range(5):
    squares.append(x ** 2)

print("Squares list:", squares)  

# x is accessible here and is the last value of the looping var
print("x after for loop:", x)

 

変数 x は 10 に設定されています。 x ループ変数でもあります。しかし、ループ変数のスコープは for ループ ブロックに限定されていると仮定します。

出力を見てみましょう。

Output >>>

Squares list: [0, 1, 4, 9, 16]
x after for loop: 4

 

私たちはそれを見る x は、ループ内で取得する最終値である 4 になり、設定した初期値 10 ではありません。

for ループを内包表記に置き換えるとどうなるかを見てみましょう。

x = 10
squares = [x ** 2 for x in range(5)]

print("Squares list:", squares)  

# x is 10 here
print("x after list comprehension:", x)

 

ここでは、 x は 10 で、内包表記の前に設定した値です。

Output >>>

Squares list: [0, 1, 4, 9, 16]
x after list comprehension: 10

避ける方法

予期しない動作を回避するには: ループを使用している場合は、ループ変数に、後でアクセスする別の変数と同じ名前を付けないように注意してください。

Python では、 is オブジェクトの同一性をチェックするためのキーワード。つまり、2 つの変数がメモリ内の同じオブジェクトを参照しているかどうかをチェックします。等しいかどうかをチェックするには、 == オペレーター。はい?

ここで、Python REPL を開始し、次のコードを実行します。

>>> a = 7
>>> b = 7
>>> a == 7
True
>>> a is b
True

 

これを実行してください:

>>> x = 280
>>> y = 280
>>> x == y
True
>>> x is y
False

 

待って、なぜこんなことが起こるのでしょうか? これは、Python の標準実装である CPython の「整数キャッシュ」または「インターニング」によるものです。

CPython 整数オブジェクトをキャッシュします -5 ~ 256 の範囲。 つまり、この範囲内の整数を使用するたびに、Python はメモリ内の同じオブジェクトを使用することになります。 したがって、この範囲内の 2 つの整数を比較すると、 is キーワード、結果は True なぜなら彼ら メモリ内の同じオブジェクトを参照する.

それが理由です a is b 収益 True。印刷して確認することもできます id(a) & id(b).

ただし、この範囲外の整数はキャッシュされません。そして、そのような整数が出現するたびに、メモリ内に新しいオブジェクトが作成されます。 

したがって、キャッシュされた範囲外の 2 つの整数を比較する場合、 is キーワード (はい、 x & y この例では両方とも 280 に設定されています)、結果は次のようになります。 False なぜなら、これらは実際にメモリ内の 2 つの異なるオブジェクトだからです。

避ける方法

この動作は、 is 2 つのオブジェクトの同等性を比較します。したがって、常に使用してください == 演算子を使用して、2 つの Python オブジェクトが同じ値を持つかどうかを確認します。

Python の組み込みデータ構造に精通している場合は、タプルが 不変。 だからあなた それらを適切に変更します。一方、リストや辞書などのデータ構造は、 変わりやすい。あなたという意味 できる それらを所定の位置に変更します。

しかし、1 つ以上の可変オブジェクトを含むタプルはどうなるでしょうか?

Python REPL を開始して、この簡単な例を実行すると便利です。

>>> my_tuple = ([1,2],3,4)
>>> my_tuple[0].append(3)
>>> my_tuple
([1, 2, 3], 3, 4)

 

ここで、タプルの最初の要素は 3 つの要素を含むリストです。最初のリストに XNUMX を追加してみましたが、うまくいきました。そうですね、タプルを適切に変更しただけでしょうか?

次に、リストにさらに 2 つの要素を追加してみましょう。今回は += 演算子を使用します。

>>> my_tuple[0] += [4,5]
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'tuple' object does not support item assignment

 

はい、タプル オブジェクトが項目の割り当てをサポートしていないことを示す TypeError が返されます。それは予想されています。しかし、タプルを確認してみましょう: 

>>> my_tuple
([1, 2, 3, 4, 5], 3, 4)

 

要素 4 と 5 がリストに追加されていることがわかります。プログラムはエラーをスローすると同時に成功したのでしょうか?

+= 演算子は、内部的には __iadd__() インプレース加算を実行し、その場でリストを変更するメソッド。代入により TypeError 例外が発生しますが、リストの末尾への要素の追加はすでに成功しています。 += はおそらく最も鋭い角です。

避ける方法

プログラムでこのような癖を避けるには、タプルを使用してみてください。 不変コレクションの場合。また、可変オブジェクトをタプル要素として使用することはできる限り避けてください。

可変性は、これまでの議論で繰り返し取り上げられてきたトピックです。このチュートリアルを締めくくるために、もう 1 つ説明します。

場合によっては、リストの独立したコピーを作成する必要があるかもしれません。しかし、次のような構文を使用してコピーを作成するとどうなるでしょうか。 list2 = list1 コラボレー list1 オリジナルのリストですか?

作成されるのは浅いコピーです。したがって、リストの元の要素への参照のみがコピーされます。浅いコピーを介して要素を変更すると、次のような影響が生じます。 両言語で 元のリスト & 浅いコピー。 

次の例を見てみましょう。

original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Shallow copy of the original list
shallow_copy = original_list

# Modify the shallow copy
shallow_copy[0][0] = 100

# Print both the lists
print("Original List:", original_list)
print("Shallow Copy:", shallow_copy)

 

浅いコピーへの変更が元のリストにも影響を与えることがわかります。

Output >>>

Original List: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]
Shallow Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]

 

ここでは、浅いコピーの最初のネストされたリストの最初の要素を変更します。 shallow_copy[0][0] = 100。しかし、この変更は元のリストと浅いコピーの両方に影響を与えることがわかります。 

避ける方法

これを回避するには、次のようにディープ コピーを作成します。

import copy

original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Deep copy of the original list
deep_copy = copy.deepcopy(original_list)

# Modify an element of the deep copy
deep_copy[0][0] = 100

# Print both lists
print("Original List:", original_list)
print("Deep Copy:", deep_copy)

 

これで、ディープ コピーに変更を加えても、元のリストは変更されないままになります。

Output >>>

Original List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Deep Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]

そしてそれはラップです!このチュートリアルでは、変更可能なデフォルトの驚くべき動作から、浅いコピーリストの微妙な点まで、Python のいくつかの奇妙な点を調査しました。これは Python の奇妙な点の紹介にすぎず、決して完全なリストではありません。すべてのコード例を見つけることができます GitHubで.

Python で長くコーディングを続け、言語の理解が深まれば、さらに多くのことに遭遇することになるでしょう。したがって、コーディングを続け、探索を続けてください。

ああ、このチュートリアルの続きを読みたい場合は、コメントでお知らせください。
 
 

バラ プリヤ C インド出身の開発者兼テクニカル ライターです。彼女は、数学、プログラミング、データ サイエンス、コンテンツ作成が交わる場所で働くのが好きです。彼女の興味と専門分野には、DevOps、データ サイエンス、自然言語処理が含まれます。彼女は読書、執筆、コーディング、コーヒーが好きです。現在、彼女はチュートリアル、ハウツー ガイド、意見記事などを作成して、学習し、開発者コミュニティと知識を共有することに取り組んでいます。 Bala は、魅力的なリソースの概要やコーディング チュートリアルも作成しています。

スポット画像

最新のインテリジェンス

スポット画像