天天看点

Python CookBook 笔记 第一章:数据结构和算法

解压序列赋给多个值

* 任何的序列(或者是可迭代对象) 可以通过一个简单的赋值语句解压并赋值给多个变量。唯一的前提就是变量的数量必须跟序列元素的数量是一样的。

这种解压赋值可以用在任何可迭代对象上面,而不仅仅是列表或者元组。包括字符串,文件对象,迭代器和生成器。

data = [ 'ACME', 50, 91.1, (2012, 12, 21) 
name, shares, price, date = data
name, shares, price, (year, month, day) = data
           

* 只想解压一部分,丢弃其他的值。对于这种情况Python 并没有提供特殊的语法。但是你可以使用任意变量名去占位,到时候丢掉这些变量就行了。

data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
_, shares, _, (year, month, day) = data
           

解压可迭代对象赋值给多个变量

record = ('Dave', '[email protected]', '773-555-1212', '847-555-1212')
name, email, *phone_numbers = record
           
>>> name
'Dave'
>>> email
'[email protected]'
>>> phone_numbers
['773-555-1212', '847-555-1212']
           

    phone numbers 变量永远都是列表类型,不管解压的电话号码数量是多少(包括0 个)。

    * 例1:星号表达式在迭代元素为可变长元组的序列时是很有用的

records = [
('foo', 1, 2),
('bar', 'hello'),
('foo', 3, 4),
]
def do_foo(x, y):
    print('foo', x, y)
def do_bar(s):
    print('bar', s)
for tag, *args in records:
    if tag == 'foo':
        do_foo(*args)
    elif tag == 'bar':
        do_bar(*args)
           

    * 例2:星号解压语法在字符串操作的时候也会很有用,比如字符串的分割。

line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
unname, *fields, homedir, sh = line.split(':')
           
>>> uname
'nobody'
>>> homedir
'/var/empty'
>>> sh
'/usr/bin/false'
           

    * 例3:想解压一些元素后丢弃它们,你不能简单就使用* ,但是你可以使用一个普通的废弃名称,比如_或者ign 。

record = ('ACME', 50, 123.45, (12, 18, 2012))
name, *_, (*_, year) = record
           

    * 例4:分割语法去巧妙的实现递归算法

items = [1,10,7,4,5,9]

def sum(items):
    head, *tail = items
    # return head+sum(tail) if tail else head
    if tail:#如果tail为真,返回head+sum(tail)
        x = head+sum(tail)
        return x
    else:#否则,返回head
        return head

x = sum(items)
           

保留最后N 个元素

字典排序

*  使用collections 模块中的OrderedDict 类。

from collections import OrderedDict

def ordered_dict():
    d = OrderedDict()
    d['foo'] = 1
    d['bar'] = 2
    d['spam'] = 3
           

OrderedDict 内部维护着一个根据键插入顺序排序的双向链表。每次当一个新的元素插入进来的时候,它会被放到链表的尾部。对于一个已经存在的键的重复赋值不会改变键的顺序。

一个OrderedDict 的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。

字典运算

在一个字典上执行普通的数学运算,你会发现它们仅仅作用于键,而不是值。

prices = {
'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
}
           

* 使用zip() 函数先将键和值反转过来。

min_price = min(zip(prices.values(),prices.keys()))
max_price = max(zip(prices.values(),prices.keys()))
prices_sorted = sorted(zip(prices.values(), prices.keys()))
           

zip() 函数创建的是一个只能访问一次的迭代器。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

prices_and_names = zip(prices.values(), prices.keys())
print(min(prices_and_names)) # OK
print(max(prices_and_names)) # ValueError: max() arg is an empty sequence
           

* 在min() 和max() 函数中提供key 函数参数来获取最小值或最大值对应的键的信息。(比zip复杂)

min(prices, key=lambda k: prices[k]) # Returns 'FB'
max(prices, key=lambda k: prices[k]) # Returns 'AAPL'
           

查找两字典的相同点

a = {'
x' : 1,
'y' : 2,
'z' : 3
}
b = {'
w' : 10,
'x' : 11,
'y' : 2
}
           
# Find keys in common
a.keys() & b.keys() # { 'x', 'y' }
# Find keys in a that are not in b
a.keys() - b.keys() # { 'z' }
# Find (key,value) pairs in common
a.items() & b.items() # { ('y', 2) }
           

例: 修改或者过滤字典元素。

# Make a new dictionary with certain keys removed
c = {key:a[key] for key in a.keys() - {'z', 'w'}}
# c is {'x': 1, 'y': 2}
           

命名切片

* slice() 函数创建了一个切片对象,可以被用在任何切片允许使用的地方。

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]
           

如果你有一个切片对象s,你可以分别调用它的s.start , s.stop , s.step 属性来获取更多的信息。

>>> s = slice(5, 50, 2)
>>> s.start
5
>>> s.stop
50
>>> s.step
2
           

* 调用切片的indices(size) 方法将它映射到一个确定大小的序列上,这个方法返回一个三元组(start, stop, step) ,所有值都会被合适的缩小以满足边界限制,从而使用的时候避免出现IndexError 异常。

>>> s = 'HelloWorld'
>>> a.indices(len(s))
(5, 10, 2)
>>> for i in range(*a.indices(len(s))):
	print(s[i])
>>> W
>>> r
>>> d
           

序列中出现次数最多的元素

* collections.Counter 类就是专门为这类问题而设计的,它甚至有一个有用的most common() 方法直接给你答案。

words = [
'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around', 'the',
'eyes', "don't", 'look', 'around', 'the', 'eyes', 'look', 'into',
'my', 'eyes', "you're", 'under'
]

from collections import Counter

word_count = Counter(words)
top_three = word_count.most_common(3)

print(top_three)
           
[('eyes', 8), ('the', 5), ('look', 4)]
           

    * 作为输入, Counter 对象可以接受任意的hashable 序列对象。在底层实现上,一个Counter 对象就是一个字典,将元素映射到它出现的次数上。

>>> word_counts['not']
1
>>> word_counts['eyes']
8
           

    * 如果你想手动增加计数,可以简单的用加法。

>>> morewords = ['why','are','you','not','looking','in','my','eyes']
>>> for word in morewords:
... word_counts[word] += 1
...
>>> word_counts['eyes']
9
           

    或者你可以使用update() 方法。

>>> word_counts.update(morewords)
           

    * Counter 实例一个鲜为人知的特性是它们可以很容易的跟数学运算操作相结合。

>>> a = Counter(words)
>>> b = Counter(morewords)
>>> a
Counter({'eyes': 8, 'the': 5, 'look': 4, 'into': 3, 'my': 3, 'around': 2,
"you're": 1, "don't": 1, 'under': 1, 'not': 1})
>>> b
Counter({'eyes': 1, 'looking': 1, 'are': 1, 'in': 1, 'not': 1, 'you': 1,
'my': 1, 'why': 1})
>>> # Combine counts
>>> c = a + b
>>> c
Counter({'eyes': 9, 'the': 5, 'look': 4, 'my': 4, 'into': 3, 'not': 2,
'around': 2, "you're": 1, "don't": 1, 'in': 1, 'why': 1,
'looking': 1, 'are': 1, 'under': 1, 'you': 1})
>>> # Subtract counts
>>> d = a - b
>>> d
Counter({'eyes': 7, 'the': 5, 'look':