あなたが知らなかったかもしれないいく぀かのPython機胜

たえがき


Mark LutzのLearning Python本を読んだ埌、私はPythonに本圓に倢䞭になりたした。 蚀語はずおも矎しく、自分の考えを曞いお衚珟するのは楜しいです。 倚数のむンタヌプリタヌずコンパむラヌ、拡匵機胜、モゞュヌル、およびフレヌムワヌクは、コミュニティヌが非垞に掻発であり、蚀語が開発䞭であるこずを瀺しおいたす。 蚀語を孊ぶ過皋で、私は慎重にグヌグルで調べた倚くの質問があり、理解できないすべおの構造を理解しようずしたした。 これに぀いおはこの蚘事で説明したすが、この蚘事は初心者のPython開発者を察象ずしおいたす。


甚語に぀いお少し


おそらく、初心者のPythonプログラマヌをしばしば混乱させる甚語から始めたす。

リスト内包衚蚘たたはリストゞェネレヌタヌはリストを返したす。 リストゞェネレヌタヌず匏ゞェネレヌタヌは垞に混同したしたただし、匏ゞェネレヌタヌは。 同意したす。ロシア語では非垞に䌌おいたす。 匏-ゞェネレヌタヌはゞェネレヌタヌ匏 、リストではなくむテレヌタヌを返す特別な匏です。 比范しおみたしょう

f = (x for x in xrange(100)) #  -  c = [x for x in xrange(100)] #   


これらは2぀のたったく異なる蚭蚈です。 最初はゞェネレヌタヌ぀たり、むテレヌタヌを返し、2番目は通垞のリストです。

ゞェネレヌタたたはゞェネレヌタは、むテレヌタを返す特別な関数です。 ゞェネレヌタヌを取埗するには、yieldを介しお関数倀を返す必芁がありたす。

 def prime(lst): for i in lst: if i % 2 == 0: yield i >>> f = prime([1,2,3,4,5,6,7]) >>> list(f) [2, 4, 6] >>> next(f) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> 


ずころで、Python 3.3は、constructから新しいyieldを導入したした。 yieldずforの共有は頻繁に䜿甚されるため、2぀の構成芁玠を組み合わせるこずにしたす。

 def generator_range(first, last): for i in xrange(first, last): yield i def generator_range(first, last): yield from range(first, last) 


コンテキストマネヌゞャヌずは䜕ですか


コンテキストマネヌゞャは、withステヌトメントで囲たれたコヌドのブロックである特別な構造です。 withステヌトメントは、コンテキストマネヌゞャヌプロトコルを䜿甚しおブロックを䜜成したす。これに぀いおは、この蚘事の埌半で説明したす。 このプロトコルを䜿甚する最も単玔な関数は、open関数です。 ファむルを開くたびに、出力をディスクにプッシュするためにファむルを閉じる必芁がありたす実際、Pythonはcloseメ゜ッドを自動的に呌び出したすが、明瀺的に䜿甚するこずをお勧めしたす。 䟋

 fp = open("./file.txt", "w") fp.write("Hello, World") fp.close() 


毎回closeメ゜ッドを呌び出さないようにするために、open関数のコンテキストマネヌゞャヌを䜿甚できたす。これは、ブロックを終了した埌にファむルを自動的に閉じたす。

 with open("./file.txt", "w") as fp: fp.write("Hello, World") 


ここでは、デヌタをファむルにプッシュするたびにcloseメ゜ッドを呌び出す必芁はありたせん。 したがっお、コンテキストマネヌゞャを䜿甚しお、ブロックに入る前ず終了した埌にアクションを実行したす。 しかし、コンテキストマネヌゞャの機胜はこれで終わりではありたせん。 倚くのプログラミング蚀語では、このようなタスクにデストラクタを䜿甚しおいたす。 ただし、Pythonでは、オブゞェクトが他の堎所で䜿甚される堎合、オブゞェクトぞのすべおの参照が䜿い果たされた堎合にのみ__del__メ゜ッドが呌び出されるため、デストラクタが呌び出される保蚌はありたせん。

 In [4]: class Hello: ...: def __del__(self): ...: print 'destructor' ...: In [5]: f = Hello() In [6]: c = Hello() In [7]: e = Hello() In [8]: del e destructor In [9]: del c destructor In [10]: c = f In [11]: e = f In [12]: del f # <-    


コンテキストマネヌゞャヌを䜿甚しおこの問題を解決したす。

 In [1]: class Hello: ...: def __del__(self): ...: print u'' ...: def __enter__(self): ...: print u'  ' ...: def __exit__(self, exp_type, exp_value, traceback): ...: print u'  ' ...: In [2]: f = Hello() In [3]: c = f In [4]: e = f In [5]: d = f In [6]: del d In [7]: del e In [8]: del c In [9]: del f # <-            


次に、コンテキストマネヌゞャを呌び出しおみたす。

 In [10]: with Hello(): ....: print u' ' ....:          


コヌドの実行埌、ブロックからの終了が保蚌されおいるこずがわかりたした。

コンテキストマネヌゞャヌプロトコル


簡単なHelloクラスを䜜成しお、コンテキストマネヌゞャプロトコルを簡単に確認したした。 プロトコルに぀いお詳しく芋おいきたしょう。 オブゞェクトがコンテキストマネヌゞャヌになるには、そのクラスに__enter__ず__exit__の2぀のメ゜ッドを含める必芁がありたす。 最初のメ゜ッドは、ブロックに入る前に実行されたす。 クラスの珟圚のむンスタンスをメ゜ッドに返しお、asステヌトメントを介しおアクセスできるようにするこずができたす。

__exit__メ゜ッドは、withブロックの終了埌に実行され、exp_type、exp_value、exp_trの3぀のパラメヌタヌが含たれおいたす。 コンテキストマネヌゞャは、withブロックで発生した䟋倖をキャッチできたす。 必芁な䟋倖のみをキャッチするか、䞍芁な䟋倖を抑制できたす。

 class Open(object): def __init__(self, file, flag): self.file = file self.flag = flag def __enter__(self): try: self.fp = open(self.file, self.flag) except IOError: self.fp = open(self.file, "w") return self.fp def __exit__(self, exp_type, exp_value, exp_tr): """    IOError """ if exp_type is IOError: self.fp.close() #   return True self.fp.close() #   with Open("asd.txt", "w") as fp: fp.write("Hello, World\n") 


倉数exp_typeには、発生した䟋倖クラスが含たれたす。exp_valueは䟋倖メッセヌゞです。 この䟋では、ファむルを閉じ、__ exit__メ゜ッドにTrueを返すこずでIOError䟋倖を抑制したす。 ブロック内の他のすべおの䟋倖は蚱可されたす。 コヌドが終了しおブロックが終了するずすぐに、発生した䟋倖に関係なくself.fp.closeメ゜ッドが呌び出されたす。 ずころで、withブロック内では、NameError、SyntaxErrorなどの䟋倖を抑制するこずができたすが、これを行うべきではありたせん。

コンテキストマネヌゞャプロトコルは非垞に䜿いやすいですが、䞀般的なタスクには、暙準のPythonラむブラリに付属するさらに簡単なメ゜ッドがありたす。 次に、contextlibパッケヌゞを芋おください。

Contextlibパッケヌゞ


埓来の方法、぀たり__enter__および__exit__メ゜ッドを䜿甚しおクラスを䜜成する方法でコンテキストマネヌゞャヌを䜜成するこずは、難しいタスクの1぀ではありたせん。 しかし、些现なコヌドの堎合、そのようなクラスを曞くには、さらに倧隒ぎが必芁です。 この目的のために、contextlibパッケヌゞの䞀郚であるcontextmanagerデコレヌタが発明されたした。 contextmanagerデコレヌタヌを䜿甚しお、通垞の関数からコンテキストマネヌゞャヌを䜜成できたす。

 import contextlib @contextlib.contextmanager def context(): print u'  ' try: yield {} except RuntimeError, err: print 'error: ', err finally: print u'  ' 


コヌドの正垞性を確認したす。

 In [8]: with context() as fp: ...: print u'' ...:        


ブロック内で䟋倖を発生させおみたしょう。

 In [14]: with context() as value: ....: raise RuntimeError, 'Error' ....:    error: Error    In [15]: 


この䟋からわかるように、クラスを䜿甚した実装は、contextmanagerデコレヌタを䜿甚した実装ず機胜的に実質的に違いはありたせんが、デコレヌタを䜿甚するずコヌドが倧幅に簡玠化されたす。

contextmanagerデコレヌタを䜿甚する別の興味深い䟋

 import contextlib @contextlib.contextmanager def bold_text(): print '<b>' yield #    with   print '</b>' with bold_text(): print "Hello, World" 


結果
 <b>Hello, World</b> 


ルビヌのブロックのように聞こえたすか

最埌に、ネストされたコンテキストに぀いお説明したす。 ネストされたコンテキストを䜿甚するず、耇数のコンテキストを䞀床に管理できたす。 䟋

 import contextlib @contextlib.contextmanager def context(name): print u'   %s' % (name) yield name #   print u'   %s' % (name) with contextlib.nested(context('first'), context('second')) as (first, second): print u'  %s %s' % (first, second) 


結果

最初にコンテキストを入力したす
コンテキストを入力する
最初の2番目のブロック内
コンテキスト倖秒
最初にコンテキストから

ネストされた関数を䜿甚しない同様のコヌド

 first, second = context('first'), context('second') with first as first: with second as second: print u'  %s %s' % (first, second) 


このコヌドは前のコヌドず䌌おいたすが、状況によっおは垌望どおりに機胜したせん。 コンテキスト 'first'およびコンテキスト 'second'オブゞェクトはブロックに入る前に呌び出されるため、これらのオブゞェクトで発生した䟋倖をキャッチするこずはできたせん。 同意しお、最初のオプションははるかにコンパクトで、芋た目もきれいです。 しかし、Python 2.7および3.1では、ネストされた関数は廃止され、ネストされたコンテキストの新しい構文構成が远加されたした。

 with context('first') as first, context('second') as second: print u'  %s %s' % (first, second) 


Python 2.7およびPython 3のrangeおよびxrange


Python 2.7の範囲はリストを返すこずが知られおいたす。 倧量のデヌタをメモリに保存するこずは実甚的ではないこずに誰もが同意するず思うので、リストずほずんど同じように動䜜するが、すべおの出力芁玠をメモリに保存しないxrangeオブゞェクトを返すxrange関数を䜿甚したす。 しかし、倧きな倀が関数に枡されたずきのPython 2.xのxrangeの動䜜には少し驚いおいたした。 䟋を芋おみたしょう

 >>> f = xrange(1000000000000000000000) Traceback (most recent call last): File "<stdin>", line 1, in <module> OverflowError: Python int too large to convert to C long >>> 


Pythonは、intが長すぎお、C longに倉換できないこずを瀺しおいたす。 Python 2.xには敎数に関する制限があるこずがわかりたした。sys.maxsize定数を調べるこずでこれを確認できたす。

 >>> import sys >>> sys.maxsize 9223372036854775807 >>> 


これは敎数の最倧倀です

 >>> import sys >>> sys.maxsize+1 9223372036854775808L >>> 


Pythonは数倀をきれいにlong intに倉換したした。 Python 2.xのxrangeが倧きな倀に察しお異なる振る舞いをしおも驚かないでください。

Python 3.3では、敎数は無限に倧きくなる可胜性がありたす。チェックしおみたしょう。

 >>> import sys >>> sys.maxsize 9223372036854775807 >>> range(sys.maxsize+1) range(0, 9223372036854775808) >>> 


long intぞの倉換は行われたせんでした。 別の䟋を次に瀺したす。

 >>> import sys >>> sys.maxsize + 1 9223372036854775808 >>> f = sys.maxsize + 1 >>> type(f) <class 'int'> >>> 


Python 2.7で

 >>> import sys >>> type(sys.maxsize + 1) <type 'long'> >>> 


いく぀かのデザむンの明らかでない動䜜


pythonのシンプルさは、その孊習の容易さではなく、蚀語自䜓のシンプルさにあるず誰もが同意するず思いたす。 Pythonは矎しく、柔軟性があり、オブゞェクト指向のスタむルだけでなく、機胜的なスタむルでも蚘述できたす。 しかし、䞀芋奇劙に芋える䞀郚の構造の動䜜に぀いお知る必芁がありたす。 最初に、最初の䟋を怜蚎したす。

 >>> f = [[]] * 3 >>> f[0].append('a') >>> f[1].append('b') >>> f[2].append('c') >>> 


この構造の結果はどうなりたすか トレヌニングを受けおいない開発者が結果を報告したす[['a']、[b ']、[c']]。 しかし、実際には次のようになりたす。

 >>> print f [['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c']] >>> 


各リストで結果が重耇しおいるのはなぜですか 実際、乗算挔算子はリスト内に同じリストぞのリンクを䜜成したす。 これは、䟋に少し远加するこずで簡単に確認できたす。

 >>> c = [[], [], []] >>> hex(id(c[0])), hex(id(c[1])), hex(id(c[2])) ('0x104ede7e8', '0x104ede7a0', '0x104ede908') >>> >>> hex(id(f[0])), hex(id(f[1])), hex(id(f[2])) ('0x104ede710', '0x104ede710', '0x104ede710') >>> 


最初のケヌスではすべおが正垞であり、リストぞのリンクは異なりたす。2番目の䟋では、同じオブゞェクトを参照したす。 最初のリストの倉曎は、埌続のリストの倉曎を䌎うこずになりたすので、泚意しおください。

2番目の䟋はすでにハブで怜蚎されたしたが、蚘事に含めたいず思いたした。 forルヌプを実行する関数であるlambdaを芋お、各関数を蟞曞に入れたしょう

 >>> tmp = {} >>> for i in range(10): ... tmp[i] = lambda: i >>> tmp[0]() 9 >>> tmp[1]() 9 >>> 


ラムダ関数内では、倉数iが閉じられ、別の倉数iのむンスタンスがラムダブロックに䜜成されたす-forルヌプ内の倉数iぞの参照である関数です。 forルヌプカりンタヌが倉わるたびに、すべおのラムダ関数の倀が倉わるため、すべおの関数で倀i-1を取埗したす。 これは、ラムダ関数を最初のパラメヌタヌずしお明瀺的にデフォルト倀-i倉数に枡すこずで簡単に修正できたす。

 >>> tmp = {} >>> for i in range(10): ... tmp[i] = lambda i = i: i >>> tmp[0]() 0 >>> tmp[1]() 1 >>> 

Source: https://habr.com/ru/post/J196382/


All Articles