BeginMan blog

38个python数据类型小技巧

摘自30 Python Language Features and Tricks You May Not Know About, 其中一些知识点我加了释义和参考,enjoy 😊.

1.Unpacking

>>> a, b, c = 1, 2, 3
>>> a, b, c
(1, 2, 3)
>>> a, b, c = [1, 2, 3]
>>> a, b, c
(1, 2, 3)
>>> a, b, c = (2 * i + 1 for i in range(3))
>>> a, b, c
(1, 3, 5)
>>> a, (b, c), d = [1, (2, 3), 4]
>>> a
1
>>> b
2
>>> c
3
>>> d
4

2.Unpacking for swapping variables

>>> a, b = 1, 2
>>> a, b = b, a
>>> a, b
(2, 1)

3. Extended unpacking (Python 3 only)

>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5

4.Negative indexing

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[-1]
10
>>> a[-3]
8

5.List slices (a[start:end])

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[2:8]
[2, 3, 4, 5, 6, 7]

6.List slices with negative indexing

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[-4:-2]
[7, 8]

7.List slices with step (a[start:end:step])

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::2]
[0, 2, 4, 6, 8, 10]
>>> a[::3]
[0, 3, 6, 9]
>>> a[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[2:8:2]
[2, 4, 6]

这里就发觉很奇怪了,切片操作,a[start:end:step], 参数解释如下:

  • start:第一个元素对象,正索引位置默认为0;负索引位置默认为 -len(seq)
  • end: 最后一个元素对象,正索引位置默认为 len(seq)-1;负索引位置默认为 -1
  • step:步长,默认为1,步长值不能为0。

如果索引为正,则从左至右取值,如:

>>> a[1:5:2]
[1, 3]
>>> a[1:5:1]
[1, 2, 3, 4]
>>> a[1:5:]
[1, 2, 3, 4]

如果为负,则从右往左,

>>> a[-10:-6:2]
[1, 3]

其中,三者都可以省略:

>>> a[::]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[1::]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[:3:]
[0, 1, 2]
>>> a[::1]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::2]
[0, 2, 4, 6, 8, 10]
>>> a[-1::]
[10]
>>> a[-3::]
[8, 9, 10]
>>> a[:-1:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-3:]
[0, 1, 2, 3, 4, 5, 6, 7]
>>> a[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> 

更过切片的知识可参考:理解python序列类型的切片

8.List slices with negative step

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[::-2]
[10, 8, 6, 4, 2, 0]

9. List slice assignment

>>> a = [1,2,3,4,5]
>>> a[2:3]
[3]
>>> a[2:3] = [0, 0]
>>> a
[1, 2, 0, 0, 4, 5]
>>> a[2:3] 
[0]
>>> a[2:4]
[0, 0]
>>> a[2:4] = [1,2,3]
>>> a[2:3]
[1]
>>> a[2:4] 
[1, 2]
>>> a
[1, 2, 1, 2, 3, 4, 5]

这里对切片进行赋值,如a[2:3]返回的是引用,我们对它赋值会影响源序列

>>> a[2:4] = ['a', 'b', 'c', 'd']			# 将['a', 'b', 'c', 'd']赋值给a[2:4]引用,插入至原序列a中
>>> a
[1, 2, 'a', 'b','c','d' 5]

同理,也可以删除它们:

>>> a = [1,2,3,4,5]
>>> a[2:4] = []
>>> a
[1, 2, 5]

10. Naming slices (slice(start, end, step))

>>> my = slice(4)
>>> my
slice(None, 4, None)
>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[my]
[0, 1, 2, 3]
>>> slice(5)
slice(None, 5, None)
>>> slice(2)
slice(None, 2, None)
>>> slice(1)
slice(None, 1, None)
>>> slice(-2)
slice(None, -2, None)
>>> slice(3, None)
slice(3, None, None)
>>> slice(1, 3, None)
slice(1, 3, None)

slice(start, end, step) 内置函数,也就是切片的原生。

11. Iterating over list index and value pairs (enumerate)

>>> a = ['Hello', 'world', '!']
>>> for i, x in enumerate(a):
...     print '{}: {}'.format(i, x)
...
0: Hello
1: world
2: !

对于列表,enumerate()返回索引和对应的值.

12. Iterating over dictionary key and value pairs (dict.iteritems)

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> for k, v in m.iteritems():
...     print '{}: {}'.format(k, v)
...
a: 1
c: 3
b: 2
d: 4

对于字典,dict.iteritems()返回键值对。

Note: use dict.items in Python 3.

13. Zipping and unzipping lists and iterables

>>> a = [1,2,3]
>>> b = ['a','b','c']
>>> zip(a, b)
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> s = zip(a, b)
>>> zip(s)
[((1, 'a'),), ((2, 'b'),), ((3, 'c'),)]
>>> zip(*s)			
[(1, 2, 3), ('a', 'b', 'c')]

zip(a, b)进行压缩, zip(*s)则是解压

14. Grouping adjacent list items using zip

>>> a = [1, 2, 3, 4, 5, 6]

>>> # Using iterators
>>> group_adjacent = lambda a, k: zip(*([iter(a)] * k))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]

>>> # Using slices
>>> from itertools import islice
>>> group_adjacent = lambda a, k: zip(*(islice(a, i, None, k) for i in range(k)))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
>>> group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
>>> group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]

关于zip可参考:Python零碎知识(2):强大的zip

(1).迭代器

内置函数iter()使用可迭代对象(iterable object)作为参数,返回一个iterator对象

>>> x = iter([1, 2, 3])
>>> x
<listiterator object at 0x1004ca850>
>>> x.next()
1
>>> x.next()
2
>>> x.next()
3
>>> x.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

每次调用next()返回下一个元素,如果没有则触发StopIteration异常,下面用类模仿xrang()

class yrange:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()

__iter__方法使对象可迭代,就是通过调用iter方法,返回一个可迭代对象,此对象应该有next方法

>>> y = yrange(3)
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 14, in next
StopIteration

也可以直接用list等内置函数:

>>> list(yrange(5))
[0, 1, 2, 3, 4]
>>> sum(yrange(5))
10

在上述情况下,无论是可迭代和迭代器是同一个对象。注意__iter__不必每次都返回self, 如下:

class zrange:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return zrange_iter(self.n)

class zrange_iter:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        # Iterators are iterables too.
        # Adding this functions to make them so.
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()

如果可迭代和迭代器是同一个对象,他们被消耗在单次迭代:

>>> y = yrange(5)
>>> list(y)
[0, 1, 2, 3, 4]
>>> list(y)
[]
>>> z = zrange(5)
>>> list(z)
[0, 1, 2, 3, 4]
>>> list(z)
[0, 1, 2, 3, 4]

(2). 生成器

生成器简化了创建迭代器:

def yrange(n):
    i = 0
    while i < n:
        yield i
        i += 1

每次yield语句生成一个新值:

>>> y = yrange(3)
>>> y
<generator object yrange at 0x401f30>
>>> y.next()
0
>>> y.next()
1
>>> y.next()
2
>>> y.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

所以生成器也是迭代器

>>> def foo():
...     print "begin"
...     for i in range(3):
...         print "before yield", i
...         yield i
...         print "after yield", i
...     print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0
0
>>> f.next()
after yield 0
before yield 1
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

下面一个算法,使用生成器的方式计算连续n个元素(0~n, n可无穷大)的平方:

def integers():
    i = 1
    while 1:
        yield i
        i += 1


def squares():
    for i in integers():
        yield i * i


def take(n, seq):
    result = []
    seq = iter(seq)
    try:
        for i in range(n):
            result.append(seq.next())
    except StopIteration:
        pass
    return result


print take(5, squares()) # prints [1, 4, 9, 16, 25]

(3).生成器表达式

生成器表达式看起来像list的含义,但返回的是一个备用的生成器而非一个列表.特么这句话”Generator Expressions are generator version of list comprehensions. They look like list comprehensions, but returns a generator back instead of a list.”我也不好理解。。。。

>>> a = (x for x in range(10))
>>> a
<generator object <genexpr> at 0x103279aa0>
>>> sum(a)
45

>>> a = (x for x in range(10))
>>> a.next()
0
>>> a.next()
1
>>> a.next()
2
>>> sum(a)		# 注意缩减啊
42

(4).xrange vs range

  • range: range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列。
  • xrange: 用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。

    xrange(1,5) xrange(1, 5) list(xrange(1,5)) [1, 2, 3, 4]

要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间,这两个基本上都是在循环的时候用

(5). itertools.islice

函数原型:

itertools.islice(iterable, start, stop[, step])

返回的迭代器是返回了输入迭代器根据索引来选取的项,生成项的方式类似于切片返回值,与切片不同,负值不会用于任何start,stop和step, 如果省略了start,迭代将从0开始,如果省略了step,步幅将采用1.

from itertools import *

print 'Stop at 5:'
for i in islice(count(), 5):
    print i,					# 0,1,2,3,4

print 'Start at 5, Stop at 10:'
for i in islice(count(), 5, 10):
    print i,					# 5,6,7,8,9

print 'By tens to 100:'
for i in islice(count(), 0, 100, 10):
    print i,					# 0,10,20,30,40,50,60,70,80,90

再上一个例子中,用了高阶函数islice()实现:

>>> group_adjacent = lambda a, k: zip(*(islice(a, i, None, k) for i in range(k)))
>>> group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]

乍一看会有点吓人,我们拆分来看:

>>> s = (islice(range(10), i, None, 3) for i in range(3))			# 通过for循环用islice创建3个迭代器,组成了生成器
>>> for i in s:
...     print i, list(i)
... 
<itertools.islice object at 0x10329b368> [0, 3, 6, 9]			# islice(range(10), 0, None, 3) --从0索引开始步长为3-> [0, 3, 6, 9]
<itertools.islice object at 0x10329b260> [1, 4, 7]				# islice(range(10), 1, None, 3) -- 从1索引开始步长为3->[1,4,7]
<itertools.islice object at 0x10329b368> [2, 5, 8]				# islice(range(10), 2, None, 3) -- 从2索引开始步长为3->[2,5,8]

那么zip(*s)解压, 由于最短元素个数为3个,则元素9会被过滤掉:

>>> s = (islice(range(10), i, None, 3) for i in range(3))
>>> zip(*s)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]

15. Sliding windows (n-grams) using zip and iterators

>>> from itertools import islice
>>> def n_grams(a, n):
...     z = (islice(a, i, None) for i in range(n))		# 这里步长为1
...     return zip(*z)
...
>>> a = [1, 2, 3, 4, 5, 6]
>>> n_grams(a, 3)
[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]
>>> n_grams(a, 2)
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
>>> n_grams(a, 4)
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)]

16. Inverting a dictionary using zip

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m.items()
[('a', 1), ('c', 3), ('b', 2), ('d', 4)]
>>> zip(m.values(), m.keys())
[(1, 'a'), (3, 'c'), (2, 'b'), (4, 'd')]
>>> mi = dict(zip(m.values(), m.keys()))
>>> mi
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

17. Flattening lists:

这里开始平铺了,共有3种方式:itertools.chain.from_iterable, sum, 和列表推导

>>> a = [[1, 2], [3, 4], [5, 6]]
>>> list(itertools.chain.from_iterable(a))
[1, 2, 3, 4, 5, 6]

>>> sum(a, [])
[1, 2, 3, 4, 5, 6]

>>> [x for l in a for x in l]
[1, 2, 3, 4, 5, 6]

>>> a = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
>>> [x for l1 in a for l2 in l1 for x in l2]
[1, 2, 3, 4, 5, 6, 7, 8]

>>> a = [1, 2, [3, 4], [[5, 6], [7, 8]]]
>>> flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
>>> flatten(a)
[1, 2, 3, 4, 5, 6, 7, 8]

18. Generator expressions

>>> g = (x ** 2 for x in xrange(10))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> sum(x ** 3 for x in xrange(10))
2025
>>> sum(x ** 3 for x in xrange(10) if x % 3 == 1)
408

19. Dictionary comprehensions

>>> m = {x: x ** 2 for x in range(5)}
>>> m
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

>>> m = {x: 'A' + str(x) for x in range(10)}
>>> m
{0: 'A0', 1: 'A1', 2: 'A2', 3: 'A3', 4: 'A4', 5: 'A5', 6: 'A6', 7: 'A7', 8: 'A8', 9: 'A9'}

20. Inverting a dictionary using a dictionary comprehension

>>> m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> m
{'d': 4, 'a': 1, 'b': 2, 'c': 3}
>>> {v: k for k, v in m.items()}
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

21. Named tuples (collections.namedtuple)

>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(x=1.0, y=2.0)
>>> p
Point(x=1.0, y=2.0)
>>> p.x
1.0
>>> p.y
2.0

Python拥有一些内置的数据类型,比如str, int, list, tuple, dict等, collections模块在这些内置数据类型的基础上,提供了几个额外的数据类型:

  • namedtuple(): 生成可以使用名字来访问元素内容的tuple子类
  • deque: 双端队列,可以快速的从另外一侧追加和推出对象
  • Counter: 计数器,主要用来计数
  • OrderedDict: 有序字典
  • defaultdict: 带有默认值的字典

可参考不可不知的Python模块: collections

22. Inheriting from named tuples:

>>> class Point(collections.namedtuple('PointBase', ['x', 'y'])):
...     __slots__ = ()
...     def __add__(self, other):
...             return Point(x=self.x + other.x, y=self.y + other.y)
...
>>> p = Point(x=1.0, y=2.0)
>>> q = Point(x=2.0, y=3.0)
>>> p + q
Point(x=3.0, y=5.0)

23. Sets and set operations

>>> A = {1, 2, 3, 3}
>>> A
set([1, 2, 3])
>>> B = {3, 4, 5, 6, 7}
>>> B
set([3, 4, 5, 6, 7])
>>> A | B
set([1, 2, 3, 4, 5, 6, 7])
>>> A & B
set([3])
>>> A - B
set([1, 2])
>>> B - A
set([4, 5, 6, 7])
>>> A ^ B
set([1, 2, 4, 5, 6, 7])
>>> (A ^ B) == ((A - B) | (B - A))
True

24. Multisets and multiset operations (collections.Counter)

>>> A = collections.Counter([1, 2, 2])
>>> B = collections.Counter([2, 2, 3])
>>> A
Counter({2: 2, 1: 1})
>>> B
Counter({2: 2, 3: 1})
>>> A | B
Counter({2: 2, 1: 1, 3: 1})
>>> A & B
Counter({2: 2})
>>> A + B
Counter({2: 4, 1: 1, 3: 1})
>>> A - B
Counter({1: 1})
>>> B - A
Counter({3: 1})

25. Most common elements in an iterable (collections.Counte

>>> A = collections.Counter([1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 6, 7])
>>> A
Counter({3: 4, 1: 2, 2: 2, 4: 1, 5: 1, 6: 1, 7: 1})
>>> A.most_common(1)
[(3, 4)]
>>> A.most_common(3)
[(3, 4), (1, 2), (2, 2)]

26. Double-ended queue (collections.deque)

>>> Q = collections.deque()
>>> Q.append(1)
>>> Q.appendleft(2)
>>> Q.extend([3, 4])
>>> Q.extendleft([5, 6])
>>> Q
deque([6, 5, 2, 1, 3, 4])
>>> Q.pop()
4
>>> Q.popleft()
6
>>> Q
deque([5, 2, 1, 3])
>>> Q.rotate(3)
>>> Q
deque([2, 1, 3, 5])
>>> Q.rotate(-3)
>>> Q
deque([5, 2, 1, 3])

27. Double-ended queue with maximum length (collections.deque)

>>> last_three = collections.deque(maxlen=3)
>>> for i in xrange(10):
...     last_three.append(i)
...     print ', '.join(str(x) for x in last_three)
...
0
0, 1
0, 1, 2
1, 2, 3
2, 3, 4
3, 4, 5
4, 5, 6
5, 6, 7
6, 7, 8
7, 8, 9

28. Ordered dictionaries (collections.OrderedDict)

>>> m = dict((str(x), x) for x in range(10))
>>> print ', '.join(m.keys())
1, 0, 3, 2, 5, 4, 7, 6, 9, 8
>>> m = collections.OrderedDict((str(x), x) for x in range(10))
>>> print ', '.join(m.keys())
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
>>> m = collections.OrderedDict((str(x), x) for x in range(10, 0, -1))
>>> print ', '.join(m.keys())
10, 9, 8, 7, 6, 5, 4, 3, 2, 1

29. Default dictionaries (collections.defaultdict)

>>> m = dict()
>>> m['a']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'a'
>>>
>>> m = collections.defaultdict(int)
>>> m['a']
0
>>> m['b']
0
>>> m = collections.defaultdict(str)
>>> m['a']
''
>>> m['b'] += 'a'
>>> m['b']
'a'
>>> m = collections.defaultdict(lambda: '[default value]')
>>> m['a']
'[default value]'
>>> m['b']
'[default value]'

30. Using default dictionaries to represent simple trees

>>> import json
>>> tree = lambda: collections.defaultdict(tree)
>>> root = tree()
>>> root['menu']['id'] = 'file'
>>> root['menu']['value'] = 'File'
>>> root['menu']['menuitems']['new']['value'] = 'New'
>>> root['menu']['menuitems']['new']['onclick'] = 'new();'
>>> root['menu']['menuitems']['open']['value'] = 'Open'
>>> root['menu']['menuitems']['open']['onclick'] = 'open();'
>>> root['menu']['menuitems']['close']['value'] = 'Close'
>>> root['menu']['menuitems']['close']['onclick'] = 'close();'
>>> print json.dumps(root, sort_keys=True, indent=4, separators=(',', ': '))
{
    "menu": {
        "id": "file",
        "menuitems": {
            "close": {
                "onclick": "close();",
                "value": "Close"
            },
            "new": {
                "onclick": "new();",
                "value": "New"
            },
            "open": {
                "onclick": "open();",
                "value": "Open"
            }
        },
        "value": "File"
    }
}

31. Mapping objects to unique counting numbers (collections.defaultdict)

>>> import itertools, collections
>>> value_to_numeric_map = collections.defaultdict(itertools.count().next)
>>> value_to_numeric_map['a']
0
>>> value_to_numeric_map['b']
1
>>> value_to_numeric_map['c']
2
>>> value_to_numeric_map['a']
0
>>> value_to_numeric_map['b']
1
>>>value
defaultdict(<method-wrapper 'next' of itertools.count object at 0x1094cdb00>,
        {'a': 0, 'b': 1, 'c': 2, 'd': 3})

上面这几部分用的了collections模块,关于此模块参考这里吧:Python collections模块

32. Largest and smallest elements (heapq.nlargest and heapq.nsmallest)

>>>import random, heapq
>>>a = [random.randint(0, 100) for __ in xrange(100)]
>>> heapq.nsmallest(5, a)		# 5个最小元素
[0, 2, 4, 6, 8]
>>> heapq.nlargest(5, a)		# 5个最大元素
[100, 100, 98, 96, 95]

33. Cartesian products (itertools.product)

>>> for p in itertools.product([1, 2, 3], [4, 5]):
(1, 4)
(1, 5)
(2, 4)
(2, 5)
(3, 4)
(3, 5)
>>> for p in itertools.product([0, 1], repeat=4):
...     print ''.join(str(x) for x in p)
...
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

34.Combinations and combinations with replacement (itertools.combinations and itertools.combinations_with_replacement)

In [48]: for i in itertools.combinations([1,2,3,4,5], 3):
   ....:     print i
   ....:     
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 4)
(1, 3, 5)
(1, 4, 5)
(2, 3, 4)
(2, 3, 5)
(2, 4, 5)
(3, 4, 5)


In [51]: for c in itertools.combinations_with_replacement([1,2,3,4,5], 3):
print c
   ....:     
(1, 1, 1)
(1, 1, 2)
(1, 1, 3)
(1, 1, 4)
(1, 1, 5)
(1, 2, 2)
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 3)
(1, 3, 4)
(1, 3, 5)
(1, 4, 4)
(1, 4, 5)
(1, 5, 5)
(2, 2, 2)
(2, 2, 3)
(2, 2, 4)
(2, 2, 5)
(2, 3, 3)
(2, 3, 4)
(2, 3, 5)
(2, 4, 4)
(2, 4, 5)
(2, 5, 5)
(3, 3, 3)
(3, 3, 4)
(3, 3, 5)
(3, 4, 4)
(3, 4, 5)
(3, 5, 5)
(4, 4, 4)
(4, 4, 5)
(4, 5, 5)
(5, 5, 5)

35. Permutations (itertools.permutations)

In [52]: for i in itertools.permutations([1,2,3,4]):
   ....:     print i
   ....:     
(1, 2, 3, 4)
(1, 2, 4, 3)
(1, 3, 2, 4)
(1, 3, 4, 2)
(1, 4, 2, 3)
(1, 4, 3, 2)
(2, 1, 3, 4)
(2, 1, 4, 3)
(2, 3, 1, 4)
(2, 3, 4, 1)
(2, 4, 1, 3)
(2, 4, 3, 1)
(3, 1, 2, 4)
(3, 1, 4, 2)
(3, 2, 1, 4)
(3, 2, 4, 1)
(3, 4, 1, 2)
(3, 4, 2, 1)
(4, 1, 2, 3)
(4, 1, 3, 2)
(4, 2, 1, 3)
(4, 2, 3, 1)
(4, 3, 1, 2)
(4, 3, 2, 1)

36. Chaining iterables (itertools.chain)

>>> a = [1, 2, 3, 4]
>>> for p in itertools.chain(itertools.combinations(a, 2), itertools.combinations(a, 3)):
...     print p
...
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
(1, 2, 3)
(1, 2, 4)
(1, 3, 4)
(2, 3, 4)
>>> for subset in itertools.chain.from_iterable(itertools.combinations(a, n) for n in range(len(a) + 1))
...     print subset
...
()
(1,)
(2,)
(3,)
(4,)
(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
(1, 2, 3)
(1, 2, 4)
(1, 3, 4)
(2, 3, 4)
(1, 2, 3, 4)

37. Grouping rows by a given key (itertools.groupby)

>>> from operator import itemgetter
>>> import itertools
>>> with open('contactlenses.csv', 'r') as infile:
...     data = [line.strip().split(',') for line in infile]
...
>>> data = data[1:]
>>> def print_data(rows):
...     print '\n'.join('\t'.join('{: <16}'.format(s) for s in row) for row in rows)
...

>>> print_data(data)
young               myope                   no                      reduced                 none
young               myope                   no                      normal                  soft
young               myope                   yes                     reduced                 none
young               myope                   yes                     normal                  hard
young               hypermetrope            no                      reduced                 none
young               hypermetrope            no                      normal                  soft
young               hypermetrope            yes                     reduced                 none
young               hypermetrope            yes                     normal                  hard
pre-presbyopic      myope                   no                      reduced                 none
pre-presbyopic      myope                   no                      normal                  soft
pre-presbyopic      myope                   yes                     reduced                 none
pre-presbyopic      myope                   yes                     normal                  hard
pre-presbyopic      hypermetrope            no                      reduced                 none
pre-presbyopic      hypermetrope            no                      normal                  soft
pre-presbyopic      hypermetrope            yes                     reduced                 none
pre-presbyopic      hypermetrope            yes                     normal                  none
presbyopic          myope                   no                      reduced                 none
presbyopic          myope                   no                      normal                  none
presbyopic          myope                   yes                     reduced                 none
presbyopic          myope                   yes                     normal                  hard
presbyopic          hypermetrope            no                      reduced                 none
presbyopic          hypermetrope            no                      normal                  soft
presbyopic          hypermetrope            yes                     reduced                 none
presbyopic          hypermetrope            yes                     normal                  none

>>> data.sort(key=itemgetter(-1))
>>> for value, group in itertools.groupby(data, lambda r: r[-1]):
...     print '-----------'
...     print 'Group: ' + value
...     print_data(group)
...
-----------
Group: hard
young               myope                   yes                     normal                  hard
young               hypermetrope            yes                     normal                  hard
pre-presbyopic      myope                   yes                     normal                  hard
presbyopic          myope                   yes                     normal                  hard
-----------
Group: none
young               myope                   no                      reduced                 none
young               myope                   yes                     reduced                 none
young               hypermetrope            no                      reduced                 none
young               hypermetrope            yes                     reduced                 none
pre-presbyopic      myope                   no                      reduced                 none
pre-presbyopic      myope                   yes                     reduced                 none
pre-presbyopic      hypermetrope            no                      reduced                 none
pre-presbyopic      hypermetrope            yes                     reduced                 none
pre-presbyopic      hypermetrope            yes                     normal                  none
presbyopic          myope                   no                      reduced                 none
presbyopic          myope                   no                      normal                  none
presbyopic          myope                   yes                     reduced                 none
presbyopic          hypermetrope            no                      reduced                 none
presbyopic          hypermetrope            yes                     reduced                 none
presbyopic          hypermetrope            yes                     normal                  none
-----------
Group: soft
young               myope                   no                      normal                  soft
young               hypermetrope            no                      normal                  soft
pre-presbyopic      myope                   no                      normal                  soft
pre-presbyopic      hypermetrope            no                      normal                  soft
presbyopic          hypermetrope            no                      normal                  soft

这几个都是itertools模块的内容,可参考PYTHON-进阶-ITERTOOLS模块小结