4つの非常に有用なpython内蔵データ構造(array,defaultdict,named tuple,counter)


array
Pythonは内蔵リストを用いて配列を実現するだけでなく,C言語のような指定型の原生配列arrayもサポートする.明らかに、listは様々なタイプのオブジェクトを格納することができ、arrayは指定されたオリジナルタイプのみを格納するため、データ量が大きい場合、オリジナルarrayはlistよりもメモリ占有量が小さい.またarrayはC言語のように定義時にサイズを制限するのではなく、listがサポートする様々な一般的な関数をサポートしています.それに比べてPythonのarrayはC++のvectorに似ています.
from array import array
l = list(range(100))
a = array.fromlist(l)

print(l.__sizeof__(), a.__sizeof__())

現在arrayには2つの制限があります.まず、整数、小数、unicode文字のみをサポートし、C++のvectorのように複数のデータ型をサポートすることはできません.また、現在はタイプを指定するのが面倒で、int、floatなどの簡単な方法ではなく、タイプに対応するアルファベットの略語を使用して指定する必要があります.
a = array('i')
a.append(1)
a.append(4)

Type code
C Type
Python Type
Minimum size in bytes 'b'
signed char
int
1 'B'
unsigned char
int
1 'u'
wchar_t
Unicode character
2 'h'
signed short
int
2 'H'
unsigned short
int
2 'i'
signed int
int
2 'I'
unsigned int
int
2 'l'
signed long
int
4 'L'
unsigned long
int
4
詳細については、以下を参照してください.https://docs.python.org/3.8/library/array.html
defaultdict
C++のmapは新しいkeyに対してvalue typeのデフォルトコンストラクション関数を自動的に使用して値を構築しますが、Pythonのデフォルトdictは存在しないkeyへのアクセスに対して例外を放出します(割り当てを除きます).これはPythonがvalueのタイプを知らないので、デフォルトでは構築できません.defaultdictでは、構築時にタイプを指定し、必要に応じてvalueを自動的に初期化する必要があります.これにより、簡単なコードを使用して多くの機能を実現することができます.
次のコードでは,defaultdictとoriginal dictを用いて学生を姓の頭文字でグループ化する機能と,分類カウントする機能を比較した.
import collections
students = ['Zhang San', 'Li Si', 'Zhou liu', 'Chen qi', 'Cheng ba']
# using defaultdict
dd = collections.defaultdict(list)
for s in students:
	key = s[0]
	dd[key].append(s)
print(dd)
# using original dict (method 1)
od = {
     }
for s in students:
	key = s[0]
	if key not in do:
		od[key] = []
	od[key].append(s)
print(od)

scores = ['A', 'B', 'C', 'A', 'A', 'B', 'C', 'B', 'A', 'A']
# using defaultdict
dd = collections.defaultdict(int)
for s in scores :
	dd[s] += 1
print(dd)
# using original dict (method 2)
od = collections.defaultdict(int)
for s in scores :
	if s not in do:
		do[s] = 1
	else:
		do[s] += 1
print(od)

Named Tuple
プログラミングの実践では、地理的座標の緯度、色のRGB値、矩形ボックスの左上と右下座標などの簡単なデータ構造を統合するために、関連するデータのセットを作成する必要があります.複雑なパラメータのセットは、ウィンドウを構築するなどです.実際には、通常3つの方法があります.
  • は、このようなデータ構造ごとにclassを作成する.利点は、名前を直接使用してデータ・メンバーにアクセスでき、複雑なアクセス・ロジックとデータ・オペレーションをサポートできることです.欠点は、対応するクラスと必要な関数を記述し、ファイルと参照関係を管理する必要があることです.
  • はtupleを使用します.利点は、作成が簡単で、メモリの使用効率が高いことです.欠点は、下付き文字のみでアクセスでき、読みやすさが悪く、エラーが発生しやすいことです.
  • はdictを使用し、strを属性の名前として使用します.利点は、比較的簡単に作成され、変数の名前が保持されていることです.欠点は,文字列を用いて名前を表すのが面倒で,各構造が名前としての文字列を保存し,空間を浪費することである.

  • collectionsのnametupleは、名前を持つ簡単なタイプを直接構築することができ、手書きのようなclassの効果を簡単に実現することができます.collections.nametupleはfactory functionであり、このタイプの特定のオブジェクトではなくタイプを作成するのに役立ちます.タイプを作成すると、各プロパティの名前を指定できます.その後、.を使用してアクセスできます.また、下付きアクセスもサポートされます.同時にNamed Tupleは、内部の数値をdictに変換するための_asdict関数もサポートしています.
    # class
    class Rect:
    	def __init__(self, x1, y1, x2, y2):
    		self.x1 = x1
    		self.y1 = y1
    		self.x2 = x2
    		self.y2 = y2
    		
    def area_class(r):
    	w = r.x2 - r.x1
    	h = r.y2 - r.y1
    	return w*h
    
    r1 = Rect(1,3,5,5)
    # <__main__.rect object="" at=""/>
    # to show its content, we need to implement __repr__(self) or __str__(self)
    print(area_class(r1))
    
    # tuple
    def area_tuple(r):
    	w = r[2]-r[0]
    	h = r[3]-r[1]
    	return w*h
    
    r2 = (1,3,5,5)
    print(r2)
    # (1, 3, 5, 5)
    print(area_tuple(r2))
    
    # dict
    def area_dict(r):
    	w = r["x2"] - r["x1"]
    	h = r["y2"] - r["y1"]
    	return w*h
    
    r3 = {
         "x1":1, "y1":3, "x2":5, "y2":5}
    print(r3)
    # {'x1': 1, 'y1': 3, 'x2': 5, 'y2': 5}
    print(area_tuple(r3))
    
    # named tuple
    import collections
    Rectangle = collections.namedtuple("Rectangle", ["x1", "y1", "x2", "y2"])
    
    def area_namedtuple(r):
    	w = r.x2 - r.x1
    	y = r.y2 - r.y1
    	return w*h
    
    r4 = Rectangle(1,3,5,5)
    print(r4)
    # Rectangle(x1=1, y1=3, x2=5, y2=5)
    x1,y2,x2,y2 = r4
    print(x1,y2,x2,y2)
    # 1 3 5 5
    print(area_namedtuple(r4))
    print(area_class(r4)) # work with "." grammar
    print(area_tuple(r4)) # work with index
    print(area_dict(r4._asdict())) # work with dict
    

    Counter
    名前の通り、Counterは要素をカウントするために使用され、collectionsというパッケージにも使用されています.Pythonの公式ドキュメントによると、dictタイプのサブクラスです.構築時にiterableのタイプを入力します.リスト、range、dict、defaultdictなどのmappingのタイプを入力します.するとCounterはその中の要素をカウントします.特殊なのは、Counterが負の数に対して特殊な処理をしていないことです.つまり、特殊な操作の下でテストを負にすることができます.後で例があります.
    c = Counter()                           # a new, empty counter
    c = Counter('gallahad')                 # a new counter from an iterable
    print(c)
    # Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
    c = Counter({
         'red': 4, 'blue': 2})      # a new counter from a mapping
    print(c)
    # Counter({'red': 4, 'blue': 2})
    c = Counter(cats=4, dogs=8)             # a new counter from keyword args
    print(c)
    # Counter({'dogs': 8, 'cats': 4})
    

    基本的なカウント機能に加えて、一般的な関連機能もサポートされています.例:
  • は、周波数に従って並べ替えられる(most_common([n])).ここで、nはオプション入力であり、前のn個の最も頻繁な要素とそれらの周波数を返すことを示す.デフォルトでは、すべての要素が返されます.
  • は、要素自体を周波数出力する(elements()).エレメント自体を返しますが、エレメントの順序は元ではなく、同じエレメントが連続的に出力されます.異なる要素間では、OrderedDictおよび3.7以降のdictによって提供される特性が、それらの出現順に出力される.
  • の2つのCounterは減算された(substract(c)).1番目のcounterから2番目のcounterで対応する要素が現れる回数を減算できます.2番目のcoutnerにのみ表示される要素の場合、デフォルトでは1番目のcounterに0回表示されます.
  • c = Counter(a=4, b=2, c=0, d=-2)
    sorted(c.elements())
    # ['a', 'a', 'a', 'a', 'b', 'b']
    Counter('abracadabra').most_common(3)
    # [('a', 5), ('b', 2), ('r', 2)]
    
    c1 = Counter(a=4, b=2, d=-2)
    c2 = Counter(a=1, b=2, c=3, d=4)
    c1.subtract(c2)
    c1
    # Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
    

    詳細については、公式ドキュメントを参照してください.https://docs.python.org/3/library/collections.html