In [1]:
# 多行结果输出支持
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

In [88]:
import numpy as np

In [2]:
def fun(a, b, c, d):
    print(a, b, c, d)

In [3]:
x = [1, 2, 3, 4]
  • 不可以直接传入

In [9]:
fun(x)


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-c60f18e0c328> in <module>()
----> 1 fun(x)

TypeError: fun() missing 3 required positional arguments: 'b', 'c', and 'd'
  • 但是可以通过 unpacking 的方式传入参数
  • * 可以用来打乱 list 或者 tuple, set

In [15]:
fun(*x)


1 2 3 4
  • range 的例子,这个一定会经常用到

In [19]:
list(range(1, 5))


Out[19]:
[1, 2, 3, 4]

In [24]:
st_end = (1, 5)
st_end1 = [1, 5]

In [25]:
list(range(*st_end))
list(range(*st_end1))


Out[25]:
[1, 2, 3, 4]
Out[25]:
[1, 2, 3, 4]
  • 当我们不知道需要传多少个参数的时候,我们可以使用 Packing来打包参数
  • When we don’t know how many arguments need to be passed to a python function, we can use Packing to pack all arguments in a tuple.

In [41]:
# A Python program to demonstrate use
# of packing

# This function uses packing to sum
# unknown number of arguments
# 变长参数
def mySum(*args):
    sum = 0
    for i in range(len(args)):
        sum = sum + args[i]
    return sum

In [ ]:
mySum(1, 2, 3, 4, 6, 7, 9)
mySum(1, 7, 9)
  • list and tuple 都是可以 packing的

In [50]:
mySum(*st_end1)
mySum(*st_end)


Out[50]:
6
Out[50]:
6
  • ** 用于对 字典进行unpacking

In [51]:
# A sample program to demonstrate unpacking of
# dictionary items using **
def fun(a, b, c):
    print(a, b, c)

In [56]:
# A call with unpacking of dictionary
d = {'a':2, 'b':4, 'c':10}
fun(*d)
fun(**d)


a b c
2 4 10
  • 连接两个list

In [59]:
list1 = [2, 4, 5]
list2 = [3, 6, 2]
[*list1, *list2]


Out[59]:
[2, 4, 5, 3, 6, 2]
  • 连接两个字典
  • 出现同样的关键字会使用最后的那个值

In [60]:
dict1 = {'a': 2, 'b': 4}
dict2 = {'s': 3, 'b': 1, 'd': 5}
{**dict1, **dict2}


Out[60]:
{'a': 2, 'b': 1, 'd': 5, 's': 3}
  • 集合set 的连接

In [65]:
set1 = set(list1)
set2 = set(list2)
set1
set2


Out[65]:
{2, 4, 5}
Out[65]:
{2, 3, 6}

In [66]:
{*set1, *set2}


Out[66]:
{2, 3, 4, 5, 6}
  • 文档的格式
    • 第一行不要提及任何名字,只是对整体的一个描述
    • 第二行必须是空行
    • 第三行以后开始具体描述,并且改行决定了文档的缩进程度,通常和"""对齐即可

In [69]:
def my_function():
    """Do nothing, but document it.

    No, really, it doesn't do anything.
    """
    pass

print(my_function.__doc__)


Do nothing, but document it.

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

In [73]:
aq = 'jiji'
a, c, d, f=aq
a,c, d,f


Out[73]:
('j', 'i', 'j', 'i')

In [77]:
aq = '你好我哈'
a, c, d, f=aq
a,c, d,f


Out[77]:
('你', '好', '我', '哈')
  • 利用占位符 _ 可以丢弃掉一些不想使用的数值

In [78]:
data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
_, shares, price, _ = data

In [79]:
shares, price


Out[79]:
(50, 91.1)
  • 可以使用 * 进行赋值操作

In [89]:
# 去掉最高分和最低分计算平均成绩
def drop_first_last(grades):
    first, *middle, last = grades
    return np.mean(middle)

In [94]:
arg = [89, 78, 98, 36, 68, 74, 81]
drop_first_last(arg)
drop_first_last([89, 78, 98, 36, 68, 74, 81])


Out[94]:
70.8
Out[94]:
70.8

In [95]:
# 去掉最高分和最低分计算平均成绩
def drop_first_last1(*grades):
    first, *middle, last = grades
    return np.mean(middle)

In [100]:
drop_first_last1(*arg)
drop_first_last1(89, 78, 98, 36, 68, 74, 81)


Out[100]:
70.8
Out[100]:
70.8
  • 不定数量赋值操作
  • 结果使用 list 类型进行保存

In [106]:
record = ('Dave', 'dave@example.com', '773-555-1212', '847-555-1212')
record1 = ('Dae', 'dae@example.com', '773-555-1212')
for t in [record, record1]:
    name, email, *phone_numbers = t
    print(name, email, phone_numbers)


Dave dave@example.com ['773-555-1212', '847-555-1212']
Dae dae@example.com ['773-555-1212']

In [108]:
*trailing, current = [10, 8, 7, 1, 9, 5, 10, 3]
trailing
current


Out[108]:
[10, 8, 7, 1, 9, 5, 10]
Out[108]:
3

In [109]:
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)


foo 1 2
bar hello
foo 3 4
  • 字符串分割

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


Out[112]:
'nobody'
Out[112]:
'/var/empty'
Out[112]:
'/usr/bin/false'
Out[112]:
['*', '-2', '-2', 'Unprivileged User']
  • 丢弃

In [115]:
record = ('ACME', 50, 123.45, (12, 18, 2012))
name, *_, (*_, year) = record
name
year


Out[115]:
'ACME'
Out[115]:
2012
  • 使用 deque(maxlen=N) 构造函数会新建一个固定大小的队列。当新的元素加入并且这个队列已满 的时候, 最老的元素会自动被移除掉。

In [122]:
from collections import deque
q = deque(maxlen=3)
q.append(1)
q.append(2)
q.append(3)
q


Out[122]:
deque([1, 2, 3])

In [123]:
q.append(4)
q


Out[123]:
deque([2, 3, 4])

In [128]:
deque([2, 3, 4], maxlen=3)
q.append(5)
q


Out[128]:
deque([2, 3, 4])
Out[128]:
deque([9, 9, 5])

In [129]:
q.appendleft(9)
q


Out[129]:
deque([9, 9, 9])

In [130]:
q.pop()


Out[130]:
9

In [131]:
q


Out[131]:
deque([9, 9])

In [132]:
q.popleft()
q


Out[132]:
9
Out[132]:
deque([9])

In [133]:
q.appendleft(5)
q


Out[133]:
deque([5, 9])

In [134]:
q.append(4)
q


Out[134]:
deque([5, 9, 4])
  • 在队列两端插入或删除元素时间复杂度都是 O(1) ,区别于列表,在列表的开头插入或删除元素 的时间复杂度为 O(N)

查找最大或最小的 N 个元素

  • heapq 模块有两个函数:nlargest() 和 nsmallest() 可以完美解决这个问题

In [136]:
import heapq
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
heapq.nlargest(3, nums)
heapq.nsmallest(3, nums)


Out[136]:
[42, 37, 23]
Out[136]:
[-4, 1, 2]

In [137]:
portfolio = [
    {'name': 'IBM', 'shares': 100, 'price': 91.1},
    {'name': 'AAPL', 'shares': 50, 'price': 543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
# 使用price 进行比较

In [138]:
cheap
expensive


Out[138]:
[{'name': 'YHOO', 'price': 16.35, 'shares': 45},
 {'name': 'FB', 'price': 21.09, 'shares': 200},
 {'name': 'HPQ', 'price': 31.75, 'shares': 35}]
Out[138]:
[{'name': 'AAPL', 'price': 543.22, 'shares': 50},
 {'name': 'ACME', 'price': 115.65, 'shares': 75},
 {'name': 'IBM', 'price': 91.1, 'shares': 100}]

堆排序


In [139]:
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
import heapq
heap = list(nums)
heapq.heapify(heap)
heap


Out[139]:
[-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]

多值字典

  • 使用list, tuple, set等其他容器实现
  • 选择使用列表还是集合取决于你的实际需求。如果你想保持元素的插入顺序就应该使用列表, 如果想去掉重复元素就使用集合(并且不关心元素的顺序问题)

In [144]:
d = {
    'a' : [1, 3, 3],
    'b' : (4, 5)
}
e = {
    'a' : {1, 2, 3},
    'b' : {4, 5}
}

In [148]:
d['a']


Out[148]:
[1, 3, 3]

In [151]:
from collections import defaultdict

d = defaultdict(list)
d['a'].append(1)
d['a'].append(2)
d['b'].append(4)
d


Out[151]:
defaultdict(list, {'a': [1, 2], 'b': [4]})

In [152]:
d = defaultdict(set)
d['a'].add(1)
d['a'].add(2)
d['b'].add(4)

In [155]:
d
d.values()


Out[155]:
defaultdict(set, {'a': {1, 2}, 'b': {4}})
Out[155]:
dict_values([{1, 2}, {4}])

字典排序

  • 为了能控制一个字典中元素的顺序,你可以使用 collections 模块中的 OrderedDict 类。 在迭代操作的时候它会保持元素被插入时的顺序

In [156]:
import json

In [157]:
from collections import OrderedDict

d = OrderedDict()
d['foo'] = 1
d['bar'] = 2
d['spam'] = 3
d['grok'] = 4
# Outputs "foo 1", "bar 2", "spam 3", "grok 4"
for key in d:
    print(key, d[key])


foo 1
bar 2
spam 3
grok 4

In [158]:
json.dumps(d)


Out[158]:
'{"foo": 1, "bar": 2, "spam": 3, "grok": 4}'

字典的运算

  • 为了对字典值执行计算操作,通常需要使用 zip() 函数先将键和值反转过来
  • 可以使用 zip() 和 sorted() 函数来排列字典数据
  • 在一个字典上执行普通的数学运算,你会发现它们仅仅作用于键,而不是值

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

In [162]:
prices.values()
prices.keys()


Out[162]:
dict_values([45.23, 612.78, 205.55, 37.2, 10.75])
Out[162]:
dict_keys(['ACME', 'AAPL', 'IBM', 'HPQ', 'FB'])

In [161]:
min(zip(prices.values(), prices.keys()))
max(zip(prices.values(), prices.keys()))


Out[161]:
(10.75, 'FB')
Out[161]:
(612.78, 'AAPL')

In [163]:
sorted(zip(prices.values(), prices.keys()))


Out[163]:
[(10.75, 'FB'),
 (37.2, 'HPQ'),
 (45.23, 'ACME'),
 (205.55, 'IBM'),
 (612.78, 'AAPL')]

In [167]:
list(zip(prices.values(), prices.keys()))


Out[167]:
[(45.23, 'ACME'),
 (612.78, 'AAPL'),
 (205.55, 'IBM'),
 (37.2, 'HPQ'),
 (10.75, 'FB')]

In [165]:
sorted(prices.items())


Out[165]:
[('AAPL', 612.78),
 ('ACME', 45.23),
 ('FB', 10.75),
 ('HPQ', 37.2),
 ('IBM', 205.55)]

查找两字典的相同点

  • 为了寻找两个字典的相同点,可以简单的在两字典的 keys() 或者 items() 方法返回结果上执行集合操作
  • 一个字典就是一个键集合与值集合的映射关系。 字典的 keys() 方法返回一个展现键集合的键视图对象。 键视图的一个很少被了解的特性就是它们也支持集合操作,比如集合并、交、差运算。 所以,如果你想对集合的键执行一些普通的集合操作,可以直接使用键视图对象而不用先将它们转换成一个 set
  • 字典的 items() 方法返回一个包含 (键,值) 对的元素视图对象。 这个对象同样也支持集合操作,并且可以被用来查找两个字典有哪些相同的键值对
  • 尽管字典的 values() 方法也是类似,但是它并不支持这里介绍的集合操作。 某种程度上是因为值视图不能保证所有的值互不相同,这样会导致某些集合操作会出现问题。 不过,如果你硬要在值上面执行这些集合操作的话,你可以先将值集合转换成 set,然后再执行集合运算就行了

In [168]:
a = {
    'x' : 1,
    'y' : 2,
    'z' : 3
}

b = {
    'w' : 10,
    'x' : 11,
    'y' : 2
}

In [177]:
# Find keys in common
a.keys() & b.keys() # 交集

# Find keys in a that are not in b
a.keys() - b.keys() # 补集

# Find (key,value) pairs in common
a.items() & b.items()


Out[177]:
{'x', 'y'}
Out[177]:
{'z'}
Out[177]:
{('y', 2)}

In [178]:
# Make a new dictionary with certain keys removed
c = {key:a[key] for key in a.keys() - {'z', 'w'}}
c


Out[178]:
{'x': 1, 'y': 2}

In [179]:
a.keys()|b.keys() # 并集


Out[179]:
{'w', 'x', 'y', 'z'}

In [180]:
a.keys()^b.keys() # 对称差集


Out[180]:
{'w', 'z'}

命名切片

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

In [182]:
a = np.array([1, 3, 4, 6, 7, 1, 2, 8])

In [183]:
a


Out[183]:
array([1, 3, 4, 6, 7, 1, 2, 8])

In [185]:
s1 = slice(2, 6)
a[s1]
a[2:6]


Out[185]:
array([4, 6, 7, 1])
Out[185]:
array([4, 6, 7, 1])

In [193]:
b = [1, 5, 7, 9, 0, 2, 1, 45, 5]
s2 = slice(2, 6, 2)
b[s2]
del b[s2]
b
s2.start, s2.stop, s2.step


Out[193]:
[7, 0]
Out[193]:
[1, 5, 9, 2, 1, 45, 5]
Out[193]:
(2, 6, 2)

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

  • collections.Counter 类就是专门为这类问题而设计的, 它甚至有一个有用的 most_common() 方法直接给了你答案
  • 毫无疑问, Counter 对象在几乎所有需要制表或者计数数据的场合是非常有用的工具。 在解决这类问题的时候你应该优先选择它,而不是手动的利用字典去实现

In [200]:
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_counts = Counter(words)
# 出现频率最高的3个单词
top_three = word_counts.most_common(3)
top_three
word_counts
sorted(zip(word_counts.values(), word_counts.keys()), reverse=True)


Out[200]:
[('eyes', 8), ('the', 5), ('look', 4)]
Out[200]:
Counter({'around': 2,
         "don't": 1,
         'eyes': 8,
         'into': 3,
         'look': 4,
         'my': 3,
         'not': 1,
         'the': 5,
         'under': 1,
         "you're": 1})
Out[200]:
[(8, 'eyes'),
 (5, 'the'),
 (4, 'look'),
 (3, 'my'),
 (3, 'into'),
 (2, 'around'),
 (1, "you're"),
 (1, 'under'),
 (1, 'not'),
 (1, "don't")]

In [207]:
word_counts
# 增加1
word_counts.update(words)
word_counts


Out[207]:
Counter({'around': 8,
         "don't": 4,
         'eyes': 32,
         'into': 12,
         'look': 16,
         'my': 12,
         'not': 4,
         'the': 20,
         'under': 4,
         "you're": 4})
Out[207]:
Counter({'around': 10,
         "don't": 5,
         'eyes': 40,
         'into': 15,
         'look': 20,
         'my': 15,
         'not': 5,
         'the': 25,
         'under': 5,
         "you're": 5})

通过某个关键字排序一个字典列表

  • 通过使用 operator 模块的 itemgetter 函数,可以非常容易的排序这样的数据结构

In [208]:
rows = [
    {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
    {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
    {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
    {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]

In [210]:
from operator import itemgetter
rows_by_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))
print(rows_by_fname)


[{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}]

In [211]:
print(rows_by_uid)


[{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}]

In [212]:
# itemgetter() 函数也支持多个 keys
rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))
print(rows_by_lfname)


[{'fname': 'David', 'lname': 'Beazley', 'uid': 1002}, {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}, {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}, {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003}]

In [213]:
min(rows, key=itemgetter('uid'))


Out[213]:
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}

通过某个字段将记录分组


In [214]:
rows = [
    {'address': '5412 N CLARK', 'date': '07/01/2012'},
    {'address': '5148 N CLARK', 'date': '07/04/2012'},
    {'address': '5800 E 58TH', 'date': '07/02/2012'},
    {'address': '2122 N CLARK', 'date': '07/03/2012'},
    {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
    {'address': '1060 W ADDISON', 'date': '07/02/2012'},
    {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
    {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]

In [215]:
from operator import itemgetter
from itertools import groupby

# Sort by the desired field first
rows.sort(key=itemgetter('date'))
# Iterate in groups
for date, items in groupby(rows, key=itemgetter('date')):
    print(date)
    for i in items:
        print(' ', i)


07/01/2012
  {'address': '5412 N CLARK', 'date': '07/01/2012'}
  {'address': '4801 N BROADWAY', 'date': '07/01/2012'}
07/02/2012
  {'address': '5800 E 58TH', 'date': '07/02/2012'}
  {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}
  {'address': '1060 W ADDISON', 'date': '07/02/2012'}
07/03/2012
  {'address': '2122 N CLARK', 'date': '07/03/2012'}
07/04/2012
  {'address': '5148 N CLARK', 'date': '07/04/2012'}
  {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}

过滤序列元素

  • 最简单的过滤序列元素的方法就是使用列表推导
  • 使用列表推导的一个潜在缺陷就是如果输入非常大的时候会产生一个非常大的结果集,占用大量内存。 如果你对内存比较敏感,那么你可以使用生成器表达式迭代产生过滤的元素
  • 有时候,过滤规则比较复杂,不能简单的在列表推导或者生成器表达式中表达出来。 比如,假设过滤的时候需要处理一些异常或者其他复杂情况。这时候你可以将过滤代码放到一个函数中, 然后使用内建的 filter() 函数

In [217]:
# 列表表达式
mylist = [1, 4, -5, 10, -7, 2, 3, -1]
[n for n in mylist if n > 0]
[n for n in mylist if n < 0]


Out[217]:
[1, 4, 10, 2, 3]
Out[217]:
[-5, -7, -1]

In [220]:
# 生成器
pos = (n for n in mylist if n > 0)
pos


Out[220]:
<generator object <genexpr> at 0x7f96e345c200>

In [221]:
next(pos)


Out[221]:
1

In [222]:
next(pos)


Out[222]:
4

In [223]:
list(pos)


Out[223]:
[10, 2, 3]

In [225]:
# 使用 filter 函数实现
values = ['1', '2', '-3', '-', '4', 'N/A', '5']
def is_int(val):
    try:
        x = int(val)
        return True
    except ValueError:
        return False
list(filter(is_int, values))


Out[225]:
['1', '2', '-3', '4', '5']

In [229]:
# 替换
[n if n > 0 else 0 for n in mylist]


Out[229]:
[1, 4, 0, 10, 0, 2, 3, 0]

从字典中提取子集

  • 字典推导式
  • 大多数情况下字典推导能做到的,通过创建一个元组序列然后把它传给 dict() 函数也能实现

In [233]:
prices = {
    'ACME': 45.23,
    'AAPL': 612.78,
    'IBM': 205.55,
    'HPQ': 37.20,
    'FB': 10.75
}
# Make a dictionary of all prices over 200
{key: value for key, value in prices.items() if value > 200}
# Make a dictionary of tech stocks
tech_names = {'AAPL', 'IBM', 'HPQ', 'MSFT'}
{key: value for key, value in prices.items() if key in tech_names}


Out[233]:
{'AAPL': 612.78, 'IBM': 205.55}
Out[233]:
{'AAPL': 612.78, 'HPQ': 37.2, 'IBM': 205.55}

In [231]:
dict((key, value) for key, value in prices.items() if value > 200)


Out[231]:
{'AAPL': 612.78, 'IBM': 205.55}

当生成器表达式作为一个单独参数传递给函数时候的巧妙语法(你并不需要多加一个括号

  • 尽可能的使用生成器作为参数,会更加的省内存

In [236]:
nums = [1, 2, 3, 4, 5]
sum((x * x for x in nums)) # 显示的传递一个生成器表达式对象
sum(x * x for x in nums) # 更加优雅的实现方式,省略了括号


Out[236]:
55
Out[236]:
55

In [ ]: