Pythonステップ1-メタグループとリスト

4044 ワード

このシリーズの文章は一連の学習ノートで、Python 3の原理、性能をより深く分析することを望んで、文章の中の大部分の観点はすべて原作の作者の観点(以下)で、本人は本の中の例に対して実践と総括を加えて、そして相応のPythonのC言語のソースコード(3.6.1)を結びつけて、分かち合います.原著:
  • 『High Performance Python』by O'Relly Media、著者Micha Gorelick、Ian Ozsvald
  • 『Fluent Python』by O'Relly Media、著者Luciano Ramalho
  • 様々なシーケンス(メタグループ、リストなど)を深く理解することで、車輪を繰り返さないようにすることができます.
    シーケンスの分類
    一般的な分類は一般にMutableとImmutableによって分類され、Container sequence(要素がオブジェクトのシーケンス):List,Tuple,collections.deque Flat sequence(コンパクトシーケンス):str,bytes,bytearray,memoryview,array.array
    リストリスト
    List:ダイナミック配列、要素が可変で、サイズ(append,resize)リストを変えることができます.
    リスト導出(List Comprehensions)とジェネレータ(Generator)
    リスト導出およびジェネレータは、リストおよび他のシーケンスを作成する迅速な方法であり、概要を記述し、高性能なコードを書くことができます.
    >>> dummy = [x for x in 'ABC']
    >>> dummy
    ['A', 'B', 'C']
    

    mapやfilterでもリストをすばやく作成できますが、パフォーマンスにはメリットはありません.
    (env) MengdeiMac:02-array-seq an$ cat listcomp_speed.py 
    import timeit
    
    TIMES = 10000
    
    SETUP = """
    symbols = '$¢£¥€¤'
    def non_ascii(c):
        return c > 127
    """
    
    def clock(label, cmd):
        res = timeit.repeat(cmd, setup=SETUP, number=TIMES)
        print(label, *('{:.3f}'.format(x) for x in res))
    
    clock('listcomp        :', '[ord(s) for s in symbols if ord(s) > 127]')
    clock('listcomp + func :', '[ord(s) for s in symbols if non_ascii(ord(s))]')
    clock('filter + lambda :', 'list(filter(lambda c: c > 127, map(ord, symbols)))')
    clock('filter + func   :', 'list(filter(non_ascii, map(ord, symbols)))')
    (env) MengdeiMac:02-array-seq an$ python listcomp_speed.py 
    listcomp        : 0.012 0.013 0.013
    listcomp + func : 0.017 0.018 0.032
    filter + lambda : 0.020 0.016 0.025
    filter + func   : 0.015 0.020 0.025
    

    メタグループ、arraysなどのシーケンスを作成する場合は、リストの導出で行うこともできますが、ジェネレータを使用するとメモリをより節約できます.(iterator protocalで要素を生成し、すべて生成してメモリに入れるのではなく)
    タプルTuple
    **タプルは「変更不可リスト」(Immutable List)**タプルだけでなく「属性なしレコード」(Records with no field name)**としても使用できます.
    Tuple as Records
    メタグループを可変リストと見なすだけでは、要素の順序は重要ではありません.メタグループを一連の属性と見なすことができ,属性の数は固定的であり,位置も重要である.
    (name, age)  = ('Jack', 18)
    

    位置によって対応する属性を取得することができ、本にはName Tuple,collectionsも紹介されている.nametupleは、属性名を付与したり、名前や場所で属性にアクセスしたりすることができ、Objectよりも軽量です.
    Tuple as Immutable List
    Tupleはadd,delete,reverseを除いてすべてのListの方法をサポートする.各Pythonプログラマーは、シーケンスがスライスできることを知っています.このようにa[start:stop]、それほど有名ではない知識点があります.
    なぜsliceとrangeには最後の要素が含まれていないのか
  • は、sliceの長さ=stop-start
  • をより容易に得ることができる.
  • シーケンスを2つの部分に分割するのが容易であり、a[:3]およびa[3:]
  •  >>> a = [1,2,3,4,5]
    >>> a[:3]
    [1, 2, 3]
    >>> a[3:]
    [4, 5]
    

    シーケンスの割り当て+=、*=
    +=依存iaddの実装,すなわちinplace addition*=依存imulの実装は可変シーケンス(List)に対してinplaceの動作が良好に実現され,可変シーケンス(Tuple)に対しては実現されなかった.
    >>> l=[1,2,3]
    >>> id(l)
    4322512072
    >>> l *= 2
    >>> l
    [1, 2, 3, 1, 2, 3]
    >>> id(l)
    4322512072
    >>> 
    >>> 
    >>> t=(1,2,3)
    >>> id(t)
    4322621768
    >>> t *= 2
    >>> t
    (1, 2, 3, 1, 2, 3)
    >>> id(t)
    4322550888
    >>> 
    

    すなわち、1つの可変シーケンスに対して、重複する貼り付け操作は非常に非効率であり、多くのメモリ割り当てとコピー操作を伴う.もう一つのcorner caseは、出力がなぜなのか考えてみましょう.異常を投げ出すとtupleも変化し,結論としてtupleに可変のオブジェクトを持たせないで,重畳付与操作は原子操作ではない.
    >>> l=(1,2,[30,50])
    >>> l[2] += [50,60]
    Traceback (most recent call last):
      File "", line 1, in 
    TypeError: 'tuple' object does not support item assignment
    >>> l
    (1, 2, [30, 50, 50, 60])
    >>> 
    

    list.sort vs sort
    2つのソート関数があります.リストに付属する関数、list.sortは、リストをその場でソートします.つまり、新しいコピーは生成されません.内蔵のsort関数、sort、新しいリストを作成し、ソートし、新しいリストを返します.
    もう一つ重要な常識があります.functions or methods that change an object in place should return None to make it clear to the caller that the object itself was changed,and no new object was created.