apeescape2.com
  • メイン
  • 革新
  • 人とチーム
  • 製品の担当者とチーム
  • モバイルデザイン
技術

バギーPythonコード:Python開発者が犯す最も一般的な10の間違い

Pythonについて

Python は、動的セマンティクスを備えた高レベルのオブジェクト指向およびインタープリター型プログラミング言語です。その高レベルの統合データ構造は、動的な書き込みとバインディングと組み合わされて、 迅速なアプリケーション開発 、および既存のコンポーネントやサービスを接続するためのスクリプト言語または接着剤として使用するため。 Pythonはモジュールとパッケージで動作するため、プログラムのモジュール性とコードの再利用が促進されます。

この記事について

Pythonのシンプルで習得しやすい構文で、に送信できます。 Python開発者 間違った方向に-特に言語を学んでいる人-途中でその微妙な部分のいくつかを失い、の力を過小評価している Pythonの多様な言語 。

そのことを念頭に置いて、この記事では、最も高度なPython開発者の一部でさえ不意を突かれる可能性のある微妙で見にくいバグの「トップ10」リストを紹介します。



(( 注意: この記事は、言語に不慣れな人を対象としたCommon Python ProgrammerErrorsよりも上級者を対象としています。 )

よくある間違い#1:関数の引数のデフォルト値としての式の誤用

Pythonでは、関数の引数をオプションとして指定し、デフォルト値を指定できます。これは言語の優れた機能ですが、デフォルトがである場合、混乱を招く可能性があります。 可変 。たとえば、Python関数の次の定義について考えてみます。

C ++コードを理解する
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified ... bar.append('baz') # but this line could be problematic, as we'll see... ... return bar

よくある誤解は、オプションの引数に値を指定しなくても関数が呼び出されるたびに、オプションの引数が特定のデフォルトの式に設定されるというものです。たとえば、上記のコードでは、foo()を呼び出すことを期待できます。複数回(つまり、bar引数を指定せずに)は常にbazを返します。これは、仮説が毎回foo()であるためです。呼び出されます(指定されたbar引数なしで)barは[]に設定されます(つまり、新しい空のリスト)。

しかし、これが行われたときに実際に何が起こるかを見てみましょう:

>>> foo() ['baz'] >>> foo() ['baz', 'baz'] >>> foo() ['baz', 'baz', 'baz']

ねえ? bazのデフォルト値がなぜだったのか既存のリストに 毎回 そのfoo()毎回新しいリストを作成する代わりに、が呼び出されましたか?最も高度なPythonプログラミングの答えは、 関数の引数のデフォルト値は、関数が定義されたときに1回だけ評価されます。 したがって、bar引数は、foo()の場合にのみ、デフォルト値(つまり、空のリスト)に初期化されます。最初に定義されましたが、次にfoo()を呼び出します。 (つまり、bar引数が指定されていない場合)barと同じリストを使用します。もともと初期化されました。

ちなみに、これに対する一般的な解決策は次のとおりです。

>>> def foo(bar=None): ... if bar is None: # or if not bar: ... bar = [] ... bar.append('baz') ... return bar ... >>> foo() ['baz'] >>> foo() ['baz'] >>> foo() ['baz']

よくある間違い#2:クラス変数の誤った使用

次の例を考えてみましょう。

>>> class A(object): ... x = 1 ... >>> class B(A): ... pass ... >>> class C(A): ... pass ... >>> print A.x, B.x, C.x 1 1 1

理にかなっています。

>>> B.x = 2 >>> print A.x, B.x, C.x 1 2 1

はい、予想通りです。

>>> A.x = 3 >>> print A.x, B.x, C.x 3 2 3

これは何ですか?変更するだけですA.xなぜC.x変わった?

Pythonでは、クラス変数は辞書のように内部的に処理され、しばしば呼ばれるものに従います。 メソッド解決順序(MRO) 。したがって、上記のコードでは、属性xがクラスCに見つからないため、その基本クラスで検索されます(Pythonは多重継承をサポートしていますが、上記の例ではAのみ)。言い換えると、CにはAとは独立した独自のプロパティxがありません。したがって、C.xへの参照は実際にはA.xへの参照です。適切に処理されない限り、これはPythonの問題を引き起こします。についての詳細 Pythonのクラス属性 。

よくある間違い#3:例外ブロックのパラメーターを誤って指定する

次のコードがあるとします。

>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except ValueError, IndexError: # To catch both exceptions, right? ... pass ... Traceback (most recent call last): File '', line 3, in IndexError: list index out of range

ここでの問題は、レポートexceptこの方法で指定された例外のリストは取得されません。対照的に、Python2.xの構文except Exception, eは、例外を2番目のパラメーターにバインドするために使用されます オプション さらなる検査に利用できるようにするために、指定されています(この場合はe)。その結果、上記のコードでは、例外IndexErrorレポートにキャプチャされていませんexcept;むしろ、例外は最終的にIndexErrorというパラメーターにバインドされます。

レポートで複数の例外をキャッチする正しい方法exceptは、最初のパラメーターをとして指定することです。 ダブル キャッチされるすべての例外を含む。また、移植性を最大化するには、構文がPython2およびPython3でサポートされているため、asキーワードを使用します。

>>> try: ... l = ['a', 'b'] ... int(l[2]) ... except (ValueError, IndexError) as e: ... pass ... >>>

よくある間違い#4:Pythonのスコープルールを理解していない

Pythonのスコープ解決は、ルールと呼ばれるものに基づいています LEGB 、の略です L オカル、 IS 閉会、 G ローブ、 B uilt-in。かなり簡単そうですね。実際、これがPythonで機能する方法にはいくつかの微妙な点があります。これにより、以下の一般的な、より高度なPythonプログラミングの問題が発生します。

次のことを考慮してください。

>>> x = 10 >>> def foo(): ... x += 1 ... print x ... >>> foo() Traceback (most recent call last): File '', line 1, in File '', line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment

何が問題ですか?

上記のエラーは、 割り当て スコープ内の変数に、 その変数は、Pythonによって自動的に、そのスコープ内でローカルと見なされます そして、任意の外部スコープで、同様の名前の任意の変数に従います。

したがって、それらの多くはUnboundLocalErrorを取得して驚いています。上記の作業コードで、関数の本体のどこかにレポートステートメントを追加して変更した場合。 (これについてもっと読むことができます ここに 。)

Linuxカーネルのほとんどが書かれているプログラミング言語はどれですか?

これは、使用時に開発者を混乱させることが特に一般的です リスト 。次の例を考えてみましょう。

>>> lst = [1, 2, 3] >>> def foo1(): ... lst.append(5) # This works ok... ... >>> foo1() >>> lst [1, 2, 3, 5] >>> lst = [1, 2, 3] >>> def foo2(): ... lst += [5] # ... but this bombs! ... >>> foo2() Traceback (most recent call last): File '', line 1, in File '', line 2, in foo UnboundLocalError: local variable 'lst' referenced before assignment

ねえ?なぜfoo2失敗しましたが、foo1とてもうまくいきましたか?

答えは前の例の問題と同じですが、確かにもっと微妙です。 foo1作っていない 割り当て a lst、foo2もしそれが。それを覚えているlst += [5]は実際にはlst = lst + [5]の略で、lstに値を割り当てようとしていることがわかります。 (したがって、Pythonはそれがローカルスコープにあると想定します)。ただし、lstに割り当てようとしている値は、同じlstに基づいています。 (ここでも、ローカルであると推定されます)、これはまだ定義されていません。ブーム。

よくある間違い#5:リストを繰り返して変更する

次のコードの問題はかなり明白なはずです。

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> for i in range(len(numbers)): ... if odd(numbers[i]): ... del numbers[i] # BAD: Deleting item from a list while iterating over it ... Traceback (most recent call last): File '', line 2, in IndexError: list index out of range

リストまたは配列からアイテムを繰り返し処理しながら削除することは、経験豊富なソフトウェア開発者にはよく知られているPythonの問題です。ただし、上記の例は非常に明白かもしれませんが、高度な開発者でさえ、このはるかに複雑なコードにうっかり気を紛らわされる可能性があります。

幸い、Pythonには多くの洗練されたプログラミングパラダイムが組み込まれており、正しく使用すると、コードが大幅に簡素化および合理化されます。これの副次的な利点は、単純なコードが誤ってリストからアイテムを削除し、繰り返し処理することによってバグに捕らえられる可能性が低いことです。そのようなパラダイムの1つは、[リスト内包表記]((https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps)です。一方、リスト内包表記は、この特定の問題を回避するのに特に役立ちます。上記のコードのこの代替実装に示されています。これは完全に機能します。

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> numbers [0, 2, 4, 6, 8]

よくある間違い#6:Pythonがクラスプ内の変数をバインドする方法の混乱

次の例を検討します。

>>> def create_multipliers(): ... return [lambda x : i * x for i in range(5)] >>> for multiplier in create_multipliers(): ... print multiplier(2) ...

次の結果が期待できます。

0 2 4 6 8

しかし、実際には次のようになります。

デザインのプロトタイピングとは
8 8 8 8 8

驚き!

これは行動が原因で発生します 遅いリンク Pythonは、クロージャで使用される変数の値が、内部関数が呼び出された瞬間にフェッチされることを示しています。したがって、上記のコードでは、返された関数のいずれかが呼び出されると、iの値が呼び出されます。欲しかった それが呼ばれたときのその周りの領域 (そしてその時点で、円は完成しているので、iにはすでに最終値4が割り当てられています)。

この一般的なPythonの問題の解決策は、ちょっとしたハックです。

>>> def create_multipliers(): ... return [lambda x, i=i : i * x for i in range(5)] ... >>> for multiplier in create_multipliers(): ... print multiplier(2) ... 0 2 4 6 8

Voilà!目的の動作を実現するために、デフォルトの引数を利用して無名関数を生成しています。これをエレガントと呼ぶ人もいます。微妙だと言う人もいます。嫌いな人もいます。ただし、Python開発者の場合、これを理解することが重要です。

よくある間違い#7:循環モジュールの依存関係を作成する

次のように、a.pyとb.pyの2つのファイルがあり、それぞれが他方をインポートするとします。

でa.py:

import b def f(): return b.x print f()

そしてb.py:

import a x = 1 def g(): print a.f()

まず、インポートしてみましょうa.py:

>>> import a 1

それは非常にうまくいきました。多分あなたは驚いたでしょう。結局のところ、ここには循環インポートがありますが、これはおそらく問題になるはずですよね?

答えは、単なる プレゼンス 循環インポートの問題は、Pythonではそれほど問題ではありません。モジュールがすでにインポートされている場合、Pythonはそれを再度インポートしようとしないほど賢いです。ただし、各モジュールが他のモジュールで定義されている関数または変数にアクセスしようとしているポイントによっては、問題が発生する可能性があります。

例に戻ると、a.pyをインポートしたとき、b.pyなので、b.pyのインポートに問題はありませんでした。何も必要ありませんb.pyインポート時に定義されます。 b.pyの唯一の参照a aは、a.f()への呼び出しです。しかし、その呼び出しはg()にありますa.pyには何もありませんまたはb.py g()を呼び出します。だから、人生は美しいです。

しかし、インポートしようとするとどうなりますかb.py (もちろん、以前にインポートしたことがなくてもa.py):

>>> import b Traceback (most recent call last): File '', line 1, in File 'b.py', line 1, in import a File 'a.py', line 6, in print f() File 'a.py', line 4, in f return b.x AttributeError: 'module' object has no attribute 'x'

ええとああ。それは良いことではありません!ここでの問題は、インポートプロセスb.pyで、a.pyをインポートしようとし、その結果、f()を呼び出し、b.xにアクセスしようとすることです。しかしb.xまだ定義されていません。したがって、例外AttributeError。

これに対する少なくとも1つの解決策は非常に簡単です。変更するだけですb.pyインポートするa.py内部g():

x = 1 def g(): import a # This will be evaluated only when g() is called print a.f()

インポートすると、すべて問題ありません。

>>> import b >>> b.g() 1 # Printed a first time since module 'a' calls 'print f()' at the end 1 # Printed a second time, this one is our call to 'g'

よくある間違い#8:Python標準ライブラリモジュールとの名前の衝突

Pythonの利点の1つは、最初から付属しているライブラリモジュールの数が多いことです。しかし、結果として、これを意識的に回避しなければ、モジュールの1つの名前と、Pythonに付属する標準ライブラリ内の同じ名前のモジュールとの間で名前の衝突に遭遇することはそれほど難しくありません(たとえば、コードにemail.pyという名前のモジュールがあり、同じ名前の標準ライブラリモジュールと競合する可能性があります。

スタートアップのための資金調達方法

これは、モジュールの標準Pythonライブラリバージョンをインポートしようとする別のライブラリをインポートするなど、非常に深刻な問題を引き起こす可能性がありますが、同じ名前のモジュールがすでにあるため、他のパッケージが誤ってバージョンをインポートします。標準のPythonライブラリ内にあるものの1つであり、これが最も深刻なエラーが発生する場所です。

したがって、標準のPythonライブラリモジュールと同じ名前を使用しないように注意する必要があります。 Pythonの改善提案を提示するよりも、パッケージ内のモジュールの名前を変更する方がはるかに簡単です。 (PEP) アップストリーム名の変更を要求し、承認を受けるため。

よくある間違い#9:Python2とPython3の違いに直面しない

次のファイルを検討してくださいfoo.py:

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e) bad()

Python 2では、これはうまく機能します。

$ python foo.py 1 key error 1 $ python foo.py 2 value error 2

しかし、Python3を試してみましょう。

$ python3 foo.py 1 key error Traceback (most recent call last): File 'foo.py', line 19, in bad() File 'foo.py', line 17, in bad print(e) UnboundLocalError: local variable 'e' referenced before assignment

ここで何が起こったのですか? 「問題」は、Python 3では、exceptブロックの範囲を超えて例外オブジェクトにアクセスできないことです。 (これは、ガベージコレクターが実行されて参照がメモリから削除されるまで、スタックフレームを使用して参照ループをメモリに保持するためです。これに関する技術的な詳細については、こちらをご覧ください。 ここに )。

この問題を回避する1つの方法は、例外オブジェクトへの参照をexceptブロックのスコープ外に保ち、アクセス可能な状態を維持することです。これは、この手法を使用する上記の例のバージョンです。これにより、コードが投与され、Python2およびPython3との互換性が高まります。

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def good(): exception = None try: bar(int(sys.argv[1])) except KeyError as e: exception = e print('key error') except ValueError as e: exception = e print('value error') print(exception) good()

これの実行はPy3kで行われます:

ノードjsでAPIを休ませます
$ python3 foo.py 1 key error 1 $ python3 foo.py 2 value error 2

¡ユピ!

(ちなみに、 Python採用ガイド コードをPython2からPython3に移行する際に注意すべき、いくつかの重要な違いについて説明します。)

よくある間違い#10:__del__の誤用

mod.pyというファイルにこれがあったとしましょう。

import foo class Bar(object): ... def __del__(self): foo.cleanup(self.myhandle)

そして、あなたはこれをanother_mod.pyからやろうとしました:

import mod mybar = mod.Bar()

醜い例外が発生しますAttributeError。

どうして?なぜなら、報告されているように ここに インタプリタがオフになると、モジュールのグローバル変数はNoneに設定されます。その結果、上記の例では、 __del__ 呼び出されると、fooという名前はすでにNoneに設定されています。

この問題の解決策は、Pythonプログラミングよりもいくらか高度です。 atexit.register() 代わりに。このように、プログラムが実行されると(つまり、正常に終了すると)、登録済みのマネージャーが解雇されます。 前 通訳がオフになっていること。

この知識があれば、前のコードの解決策mod.pyこれは次のようになります。

import foo import atexit def cleanup(handle): foo.cleanup(handle) class Bar(object): def __init__(self): ... atexit.register(cleanup, self.myhandle)

このアプリケーションは、プログラムの通常の終了後に必要なクリーニング機能を呼び出すためのクリーンで信頼性の高い方法を提供します。明らかに、self.myhandleという名前に付けられたオブジェクトをどう処理するかを決めるのはfoo.cleanup次第ですが、要点はわかります。

最後まで

Pythonは、生産性を大幅に向上させることができる多くのメカニズムとパラダイムを備えた強力で柔軟な言語です。ただし、他のソフトウェアや言語ツールと同様に、その機能の理解や認識が限られていると、資産よりも障害になることがあり、「危険を十分に理解している」ということわざの状態になります。

この記事で説明した中程度に高度なプログラミングの問題など、Pythonの重要なニュアンスに精通することで、言語の使用を最適化し、より一般的な間違いを回避できます。

あなたは私たちをチェックする必要があります Pythonインタビューのインサイダーガイド 、Pythonの専門家を特定するのに役立つ面接の質問に関する提案。

この記事のヒントがお役に立てば幸いです。フィードバックをお待ちしております。

Spotifyの非IPOはハイテク企業に道を開くでしょうか?

財務プロセス

Spotifyの非IPOはハイテク企業に道を開くでしょうか?
SQLインデックスの説明、Pt。 2

SQLインデックスの説明、Pt。 2

バックエンド

人気の投稿
JavaScript、Python、Ruby、Swift、ScalaのOption / Maybe、Either、Future Monads
JavaScript、Python、Ruby、Swift、ScalaのOption / Maybe、Either、Future Monads
SQLインデックスの説明、Pt。 2
SQLインデックスの説明、Pt。 2
すべてのトレンドは価値がありますか? Webデザイナーが犯す最も一般的なUXの間違いトップ5
すべてのトレンドは価値がありますか? Webデザイナーが犯す最も一般的なUXの間違いトップ5
Init.js:完全なJavaScriptスタックの理由と方法のガイド
Init.js:完全なJavaScriptスタックの理由と方法のガイド
Swiftプロパティのラッパーにアプローチする方法
Swiftプロパティのラッパーにアプローチする方法
 
SSOボタンを作成する方法–Flaskログインチュートリアル
SSOボタンを作成する方法–Flaskログインチュートリアル
ベジェ曲線とQPainterを使用してC ++で丸みを帯びた角の形状を取得する方法:ステップバイステップガイド
ベジェ曲線とQPainterを使用してC ++で丸みを帯びた角の形状を取得する方法:ステップバイステップガイド
Angular 2をオンにする:1.5からのアップグレード
Angular 2をオンにする:1.5からのアップグレード
最小の価値のある製品から最大の影響を得る
最小の価値のある製品から最大の影響を得る
WowzaとAmazonElasticTranscoderを使用したオンラインビデオ
WowzaとAmazonElasticTranscoderを使用したオンラインビデオ
人気の投稿
  • 視覚化の最も正確な定義は何ですか?
  • scorp vs c corp vs llc
  • スプリングブートレスト例外処理
  • 関数は関数javascriptではありません
  • ノードjsでのエラー処理
カテゴリー
収益と成長 データサイエンスとデータベース 技術 製品の担当者とチーム プロセスとツール Uxデザイン エンジニアリング管理 財務プロセス トレンド プロジェクト管理

© 2021 | 全著作権所有

apeescape2.com