『Python Cookbook』学習ノート-第1、第2章

22269 ワード

「Python Cookbook」の学習ノートを出して共有し、一時的に第1、第2章を更新します.同期自知乎https://zhuanlan.zhihu.com/p/53457483
Jupyter notebookリンク
1.第1章データ構造とアルゴリズム2.第二章文字列とテキスト
1.第1章データ構造とアルゴリズム
1.1シーケンスを単一変数に分解
反復可能な任意のオブジェクトは、単純な付与操作によって単一の変数に分解できます.
p = (2, 3)
x, y = p
x
2
y
3
data = ['acme', 90, 40, (2018,12,12)]
name, price, shares, date = data
name
'acme'
date
(2018, 12, 12)
name, price, shares, (a,d,c) = data
p = (2, 3)
x, y = p
a
2018
d
12
#       
a, c = data
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

 in ()
      1 #       
----> 2 a, c = data

ValueError: too many values to unpack (expected 2)


オブジェクトが反復できる限り、分解操作を行うことができます.
h, e, l , l, o = 'hello'
h
'h'
l
'l'


数値を破棄するには、使用できない変数名を使用します.
h, _,_,_,_ = 'hello'
h
'h'


1.2任意の長さの反復可能オブジェクトから要素を分解する
解決策:「式」はこの問題を解決します*
#            
def drop_first_last(data):
    fist,*_,last = data
    return avg(middle)
record = ('libo', '[email protected]', '110', '120')
name, email, phone, *others = record
name
'libo'
*others
File "", line 1
    *others
           ^
SyntaxError: can't use starred expression here
others
['120']
record = ('libo', '[email protected]', '110', '120')
*others, phome = record
others
#        
['libo', '[email protected]', '110']


*文は、特に、可変長のタプル・シーケンスを反復する場合に便利です.
record = [('a',1,2), ('b',1,2,3), ('c',1,2,3,4)]

for i, *other in record:
    print(i, other)
a [1, 2]
b [1, 2, 3]
c [1, 2, 3, 4]
#    
record = ['abc',50,123.3,(2018,12,20)]
name, *ignore, (year, *ignore) = record
name
'abc'
year
2018
#       ,  *   
def _sum(items):
    head, *_it = items
    return head + sum(_it) if _it else head
_sum([i for i in range(10)])
45
from collections import deque

def search(lines, pattern, history=5):
    previous_lines = deque(maxlen=history)
    for line in lines:
        if pattern in line:
            yield line, previous_lines
        previous_lines.append(line)


ここのpreviousに注意してください.lines.append(line)はyieldの後ろに置かれ、returnとは異なり実行されます.この文を繰り上げると、python*deque(maxlen=number)が2回printして固定長のキューが作成され、新しいレコードが現れるとキューがいっぱいになり、古いものが押し出され、新しい追加が行われます.appendメソッドあり
with open('untitled.txt', encoding='utf-8') as f:
    for line, previlines in search(f, 'python', 5):
        for pline in previlines:
            print(pline)
        print(line, end='')
        print('-'*10)
12345

678910

python
----------
678910

python

1112

1

2

python
----------
q = deque(maxlen=3)
q.append(1)
q.append(2)
q.append(3)
q.append(4)
q
deque([2, 3, 4])
#    maxlen   ,      
q = deque()
q.append(1)
q.append(2)
q
deque([1, 2])
q.appendleft(10)
q
deque([10, 1, 2])
q.pop()
2
q.popleft()
10
q
deque([1])


1.4最大最小のn個の要素を探す
ある集合の中で最大または最小のn個の要素を探します
# heapq       ——nlargest() nsmallest(),         
import heapq
num = [-1.2,3,3-45,33-4,44,0]
print(heapq.nlargest(3, num))
print(heapq.nsmallest(2, num))
[44, 29, 3]
[-42, -1.2]
#             key,       fun,   fun      
portfolio = [
    {'name':'libo', 'price':10, 'grades':3},
    {'name':'libobo','price':11, 'grades':4}
]
print(heapq.nlargest(2, portfolio, key=lambda s: s['grades']))
print(heapq.nsmallest(1, portfolio, key=lambda s: s['grades']))
[{'name': 'libobo', 'price': 11, 'grades': 4}, {'name': 'libo', 'price': 10, 'grades': 3}]
[{'name': 'libo', 'price': 10, 'grades': 3}]


スタックの方法を使用すると、パフォーマンスをより節約できます.データ量が大きい場合、最小の数を取得すれば、この速度はより速くなります.
import math
nums = [int(math.sin(3*i)*15) for i in range(10)]
nums
[0, 2, -4, 6, -8, 9, -11, 12, -13, 14]
heap = list(nums)
heapq.heapify(heap) # heapify()
heap
[-13, -8, -11, 2, 0, 9, -4, 12, 6, 14]
heapq.heappop(heap)
-13
heapq.heappop(heap)
# secondly
-11


注目すべきは、取得された数と集合要素の数が近い場合、ソートが最善の方法である、sorted(item)[:N]
1.5優先キューの実装
異なる要素を優先順位でソート
class Item():
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'Item({!r})'.format(self.name)
item1 = Item('book')
item1
#       !r        ,    :   Item(book),    
Item('book')
import heapq
class PriorityQueue():
    def __init__(self):
        self.queue = []
        self.index = 0
    def push(self, item, priority):
        heapq.heappush(self.queue, (-priority, self.index, item)) #    heappush()   
        self.index += 1 # index       
    def pop(self):
        return heapq.heappop(self.queue)
q = PriorityQueue()
q.push(Item('foo'), 2)
q.push(Item('fooo'), 2)
q.push(Item('foooo'), 3)
q.push('123', 4)
q.pop()
(-4, 3, '123')
q.pop()
(-3, 2, Item('foooo'))
q.pop()
(-2, 0, Item('foo'))
q.pop()
(-2, 1, Item('fooo'))


(priority,item)形式でも優先度を比較できますが、優先度が同じで問題が発生します.indeの追加も考えられます
a = (1, 'a')
b = (2, 'b')
a < b
True
b < (2, 'c')
True


1.6辞書で複数の値にキーをマッピングする
d = {
    'a':[1,2],
    'b':[3,4]
}
d['c']=[5,6]
d
{'a': [1, 2], 'b': [3, 4], 'c': [5, 6]}
from collections import defaultdict

d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(3)
d
defaultdict(list, {'a': [1, 2], 'b': [3]})
#            
d = {
    'a':[2]
}
pairs = [
    ('a',1),
    (2,2)
]
for key, values in pairs:
    if key not in d:
        d[key] = []
    d[key].append(values)
d
{2: [2], 'a': [2, 1]}


1.7辞書の順序付け
collectionのOrderDictクラスを使用できます.辞書を反復すると、追加順に
from collections import OrderedDict

d = OrderedDict()
d['a'] = 1
d['b'] = 2
d
OrderedDict([('a', 1), ('b', 2)])
import json
json.dumps(d)
'{"a": 1, "b": 2}'


1.8辞書に関する並べ替えの問題
#          
price ={
    'Alibaba':30,
    'Tenxun':20,
    'IBM':21,
    'FB':35
}
min_price = min(zip(price.values(), price.keys()))
min_price
(20, 'Tenxun')
# zip()   
z = zip(['a', 'b', 'c'], [1, 2, 3])
print(list(z))
[('a', 1), ('b', 2), ('c', 3)]


その他の一般的な方法
min(price)
'Alibaba'
max(price)
'Tenxun'
min(price.values())
20
key = min(price, key=lambda x: price[x]) # 'Tenxun'
price[key]
20


1.9 2つの辞書の同じ点(key,value)を探す
a = {
    'a':1,
    'y':2,
    'c':3
}
b = {
    'x':3,
    'y':2,
    'z':1
}
# Find keys in common
a.keys() & b.keys()
{'y'}
a.values() & b.values()
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
----> 1 a.values() & b.values()

TypeError: unsupported operand type(s) for &: 'dict_values' and 'dict_values'
a.keys() | b.keys()
{'a', 'c', 'x', 'y', 'z'}
a.keys() - b.keys()
{'a', 'c'}
# find (key, value) pairs in item
a.items() & b.items()
{('y', 2)}
a.items() | b.items()
{('a', 1), ('c', 3), ('x', 3), ('y', 2), ('z', 1)}
list(a.items())
[('a', 1), ('y', 2), ('c', 3)]


1.10繰り返しをシーケンス全体から除去し、エレメント間の迅速な変更を維持
#             (hashable)
def dedupe(items):
    seen = set()
    for item in items:
        if item not in seen:
            yield item
            seen.add(item)
a = [i*i for i in range(-5,5)]
list(dedupe(a))
[25, 16, 9, 4, 1, 0]
a.append([1,2])
a
[25, 16, 9, 4, 1, 0, 1, 4, 9, 16, [1, 2]]
[1,2] in a
True
list(dedupe(a))
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
----> 1 list(dedupe(a))

 in dedupe(items)
      3     seen = set()
      4     for item in items:
----> 5         if item not in seen:
      6             yield item
      7             seen.add(item)

TypeError: unhashable type: 'list'
set(a) #        
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
----> 1 set(a) #        

TypeError: unhashable type: 'list'


1.ハッシュ(hashable)と非変性(immutable)
オブジェクトが自分のライフサイクルでハッシュ値(hash value)を変更できない場合、これらのデータ構造にはハッシュ値が内蔵されており、各ハッシュ可能なオブジェクトにはhashメソッドが内蔵されているため、ハッシュ可能なオブジェクトはハッシュ値で比較することができます.辞書のキー値とset関数のパラメータとしても使用できます.すべてのpython内の変更できないすべてのオブジェクト(imutable objects)は、文字列、メタグループ、すなわち辞書のような変更可能なコンテナ、リストのハッシュ不可(unhashable)などのハッシュ可能である.我々ユーザが定義したクラスのインスタンスオブジェクトは、デフォルトではハッシュ可能(hashable)であり、いずれも一意であり、hash値、すなわちid()である.
  • ハッシュ
  • これは、大体のデータを小さなデータに変換するプロセスであり、一定の時間の複雑さでクエリーできるように、数値であってもよいので、ハッシュは効率的なアルゴリズムとデータ構造にとって重要です.
  • 可変性
  • これは、いくつかのオブジェクトが作成された後、いくつかの方法によって変更されないことを意味し、特にハッシュオブジェクトのハッシュ値を変更することができる任意の方法を指す.
  • 連絡:
  • ハッシュキーは必ず変更できないので、対応するハッシュ値も変更されません.変更を許可すると、ハッシュ・テーブルなどのデータ構造の格納場所も変更されるため、ハッシュの概念に反し、効率が大幅に低下します.
    #     ,      
    def dedupe(items, key=None):
        seen = set()
        for item in items:
            val = item if key==None else key(item)
            if val not in seen:
                yield item
            seen.add(val)
    b = [{'x':1,'y':2}, {'x':4,'y':2}, {'x':1,'y':3}, {'x':1,'y':2}]
    de = dedupe(b, key=lambda x:(x['x'],x['y']))
    list(de) #       {'x':1,'y':2}
    [{'x': 1, 'y': 2}, {'x': 4, 'y': 2}, {'x': 1, 'y': 3}]
    
    

    実は集合を用いる方法で実現できるが、関数を定義する目的は、f=open('aa.txt')for line in dedupe(f):......
    set(b)
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
     in ()
    ----> 1 set(b)
    
    TypeError: unhashable type: 'dict'
    
    

    1.10スライスの名前付け
    item = [i for i in range(10)]
    a = slice(2, 4)
    item[a]
    [2, 3]
    del item[a]
    item
    [0, 1, 4, 5, 6, 7, 8, 9]
    a.start
    2
    a.stop
    4
    
    

    1.11シーケンスで最も多く発生する要素を探し出す
    collectionsモジュールのCounerクラスは,この問題のために設計されている.そのmost_common()メソッドは迅速に答えを出すことができます.
    word = [str(int(math.sin(i)*5)) for i in range(20)]
    word
    ['0',
     '4',
     '4',
     '0',
     '-3',
     '-4',
     '-1',
     '3',
     '4',
     '2',
     '-2',
     '-4',
     '-2',
     '2',
     '4',
     '3',
     '-1',
     '-4',
     '-3',
     '0']
    from collections import Counter
    
    word_counter = Counter(word)
    top_3 = word_counter.most_common(3)
    top_3
    [('4', 4), ('0', 3), ('-4', 3)]
    
    

    パラメータとしてCounterオブジェクトに任意のハッシュ可能なシーケンスを提供することができる.最下位実装では、Counterは要素と回数の間にマッピングされた辞書です.
    word_counter['2']
    2
    #      counter,      ‘’
    for w in word:
        word_counter[w] += 1
    word_counter['2']
    4
    #     update  
    word.append('2')
    word_counter.update(word)
    word_counter['2'] #4+2+1
    7
    #   counter           
    
    

    1.13共通キーによる辞書リストの並べ替え
    #                。 ,           。       key-vlue      ,          
    
    rows = [
        {'fname':'li5', 'lname':'bo', 'uid':101},
        {'fname':'li2', 'lname':'bo2', 'uid':102},
        {'fname':'li3', 'lname':'bo3', 'uid':103},
        {'fname':'li4', 'lname':'bo4', 'uid':104},
    ]
    from operator import itemgetter
    
    rows_by_fname = sorted(rows, key=itemgetter('fname'))
    rows_by_fname
    [{'fname': 'li2', 'lname': 'bo2', 'uid': 102},
     {'fname': 'li3', 'lname': 'bo3', 'uid': 103},
     {'fname': 'li4', 'lname': 'bo4', 'uid': 104},
     {'fname': 'li5', 'lname': 'bo', 'uid': 101}]
    
    

    この例では、rowsは呼び出し可能なオブジェクト(callable)としてキーパラメータkeyを受け入れるsorted関数に渡される.callableはrowsから入力として個別の要素を選択し、ソートのための基準値を返します.
    #       ,      
    rows_by_fname = sorted(rows, key=lambda x: x['fname'])
    rows_by_fname
    [{'fname': 'li2', 'lname': 'bo2', 'uid': 102},
     {'fname': 'li3', 'lname': 'bo3', 'uid': 103},
     {'fname': 'li4', 'lname': 'bo4', 'uid': 104},
     {'fname': 'li5', 'lname': 'bo', 'uid': 101}]
    #       
    price
    {'Alibaba': 30, 'FB': 35, 'IBM': 21, 'Tenxun': 20}
    sorted(price, key=lambda k:price[k])
    ['Tenxun', 'IBM', 'Alibaba', 'FB']
    
    

    1.14未生成の比較操作対象の並べ替えをサポートする
    #       sorted  key         
    
    class User():
        def __init__(self,id):
            self.id = id
        def __repr__(self):
            return 'U{}'.format(self.id)
    uses = [User(40), User(20), User(30)]
    uses
    [U40, U20, U30]
    sorted(uses, key=lambda x:x.id)
    [U20, U30, U40]
    
    

    from operator import itemgetterにはoperatorが対応する.attrgetter()
    import operator
    sorted(uses, key=operator.attrgetter('id'))
    [U20, U30, U40]
    
    

    この方法があるのは,匿名関数を自分で構築するよりも性能が高いからである.
    1.15日付などのフィールドに基づいてデータをグループ化する
    rows = [
        {'address':'555N 444W', 'date':'2018/12/27'},
        {'address':'555N 44W', 'date':'2018/12/2'},
        {'address':'555N 4W', 'date':'2018/1/27'},
        {'address':'55N 444W', 'date':'2018/1/27'},
        {'address':'5N 444W', 'date':'2018/2/27'},
        {'address':'5N 444W', 'date':'2018/12/7'},
        {'address':'55N 44W', 'date':'2018/12/27'},
    
    ]
    from operator import itemgetter
    from itertools import groupby
    
    rows.sort(key=itemgetter('date'))
    rows
    [{'address': '555N 4W', 'date': '2018/1/27'},
     {'address': '55N 444W', 'date': '2018/1/27'},
     {'address': '555N 44W', 'date': '2018/12/2'},
     {'address': '555N 444W', 'date': '2018/12/27'},
     {'address': '55N 44W', 'date': '2018/12/27'},
     {'address': '5N 444W', 'date': '2018/12/7'},
     {'address': '5N 444W', 'date': '2018/2/27'}]
    for date, item in groupby(rows, key=itemgetter('date')):
        print(date)
        for i in item:
            print(' ', i)
    2018/1/27
      {'address': '555N 4W', 'date': '2018/1/27'}
      {'address': '55N 444W', 'date': '2018/1/27'}
    2018/12/2
      {'address': '555N 44W', 'date': '2018/12/2'}
    2018/12/27
      {'address': '555N 444W', 'date': '2018/12/27'}
      {'address': '55N 44W', 'date': '2018/12/27'}
    2018/12/7
      {'address': '5N 444W', 'date': '2018/12/7'}
    2018/2/27
      {'address': '5N 444W', 'date': '2018/2/27'}
    
    

    事前に並べ替えておかないと、次のような結果になります.
    rows = [
        {'address':'555N 444W', 'date':'2018/12/27'},
        {'address':'555N 44W', 'date':'2018/12/2'},
        {'address':'555N 4W', 'date':'2018/1/27'},
        {'address':'55N 444W', 'date':'2018/1/27'},
        {'address':'5N 444W', 'date':'2018/2/27'},
        {'address':'5N 444W', 'date':'2018/12/7'},
        {'address':'55N 44W', 'date':'2018/12/27'},
    
    ]
    for date, item in groupby(rows, key=itemgetter('date')):
        print(date)
        for i in item:
            print(' ', i)
    2018/12/27
      {'address': '555N 444W', 'date': '2018/12/27'}
    2018/12/2
      {'address': '555N 44W', 'date': '2018/12/2'}
    2018/1/27
      {'address': '555N 4W', 'date': '2018/1/27'}
      {'address': '55N 444W', 'date': '2018/1/27'}
    2018/2/27
      {'address': '5N 444W', 'date': '2018/2/27'}
    2018/12/7
      {'address': '5N 444W', 'date': '2018/12/7'}
    2018/12/27
      {'address': '55N 44W', 'date': '2018/12/27'}
    
    

    groupby()の使い方がわかります.group()は前後のkeyに基づいて辞書のような反復器を生成し、前後の同じkeyがパッケージされ、対応するvalueはサブ反復器である.
    ディスカッション:日付順のソートを実現するには、dateをkeyとして、辞書をvalueとして、多値対応の辞書を構築することもできます.
    dic = {
    
    }
    
    for row in rows:
        dic[row['date']] = []
    for row in rows:
        dic[row['date']].append(row)
    for key in dic:
        print(key)
        print(dic[key])
    2018/12/27
    [{'address': '555N 444W', 'date': '2018/12/27'}, {'address': '55N 44W', 'date': '2018/12/27'}]
    2018/12/2
    [{'address': '555N 44W', 'date': '2018/12/2'}]
    2018/1/27
    [{'address': '555N 4W', 'date': '2018/1/27'}, {'address': '55N 444W', 'date': '2018/1/27'}]
    2018/2/27
    [{'address': '5N 444W', 'date': '2018/2/27'}]
    2018/12/7
    [{'address': '5N 444W', 'date': '2018/12/7'}]
    #                     defaultdict()    
    
    from collections import defaultdict
    
    row_by_date = defaultdict(list)
    for row in rows:
        row_by_date[row['date']].append(row)
    for key in row_by_date:
        print(key)
        print(row_by_date[key])
    2018/12/27
    [{'address': '555N 444W', 'date': '2018/12/27'}, {'address': '55N 44W', 'date': '2018/12/27'}]
    2018/12/2
    [{'address': '555N 44W', 'date': '2018/12/2'}]
    2018/1/27
    [{'address': '555N 4W', 'date': '2018/1/27'}, {'address': '55N 444W', 'date': '2018/1/27'}]
    2018/2/27
    [{'address': '5N 444W', 'date': '2018/2/27'}]
    2018/12/7
    [{'address': '5N 444W', 'date': '2018/12/7'}]
    
    

    1.16条件に従ってリスト要素をフィルタする
    #      
    mylist = [i for i in range(10)]
    [n for n in mylist if n>5]
    [6, 7, 8, 9]
    
    

    リストデータが多い場合、膨大な結果がメモリを占有します.ジェネレータ式を使用して結果を反復的にフィルタできます
    pos = (n for n in mylist if n>5 )
    pos
     at 0x0000024B2F8D4780>
    for x in pos:
        print(x)
    6
    7
    8
    9
    pos = [n for n in mylist if n>5]
    pos
    [6, 7, 8, 9]
    
    

    フィルタ条件が複雑な場合、簡単な式で表すことができない場合は、組み込み関数filter()を使用することを記録することができます.
    values = [1,2,3,'3','#','na']
    
    def is_int(n):
        try:
            int(n)
            return True
        except:
            return False
    newlist = list(filter(is_int, values))
    print(newlist)
    [1, 2, 3, '3']
    #        
    clip_neg = [n if n > 5 else 0 for n in mylist]
    clip_neg
    [0, 0, 0, 0, 0, 0, 6, 7, 8, 9]
    # compress   
    from itertools import compress
    list(compress(['a','b','c'], [True, False, True]))
    ['a', 'c']
    
    

    1.17ディクショナリからサブセットを抽出する(ディクショナリフィルタ)
    price = {
        'Aliba': 10,
        'Ten':20,
        'Fb':10.2,
        'IBM': 11.2,
        'BB':42
    }
    pl = {key:value for key,value in price.items() if value>15}
    pl
    {'BB': 42, 'Ten': 20}
    #      
    pos = ((key,value) for key,value in price.items())
    pos
     at 0x0000024B2F8D4048>
    dict(pos)
    {'Aliba': 10, 'BB': 42, 'Fb': 10.2, 'IBM': 11.2, 'Ten': 20}
    
    

    1.19データの変換と換算を同時に行う
    num = [i for i in range(10)]
    sum(x*x for x in num)
    285
    #              ,        ,     
    s = sum((x*x for x in num))
    
    

    1.20複数のマッピングを単一のマッピングにマージ
    a = {'x':1,'y':2}
    b = {'x':2,'z':3}
    from collections import ChainMap
    c = ChainMap(a,b)
    c
    ChainMap({'x': 1, 'y': 2}, {'x': 2, 'z': 3})
    c['x']
    1
    list(c.keys())
    ['z', 'x', 'y']
    
    

    update()スキームは同様の効果を奏する
    a.update(b)
    a
    {'x': 2, 'y': 2, 'z': 3}
    #   ChainMap             
    
    merged = ChainMap(a,b)
    merged
    ChainMap({'x': 2, 'y': 2, 'z': 3}, {'x': 2, 'z': 3})
    merged['z']
    3
    a['z']=100
    merged['z']
    100
    a['z']
    100
    b['z']
    3
    #   ChainMap              ,    a
    merged['z']=106
    a['z']
    106
    b['z']
    3
    
    

    23:38に編集