7

几个Python“小伎俩”

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MjM5ODkzMzMwMQ%3D%3D&%3Bmid=2650412414&%3Bidx=3&%3Bsn=bebaa8e87f56b852cdfe71a17384f02e
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

写在前面

我又来更新啦~今天一起回顾下Python Cookbook,全书是以 问答对 的形式展开,这是我很久之前看的笔记。Cookbook不算是入门书,更像是一本工具书,既然有基础了那就没必要一个个点去看,建议是需要用到那部分就把那块的知识点技巧翻一遍。下面大噶自己查漏补缺吧!

Chap1 数据结构与算法

从任意长度的可迭代对象中分解元素

*表达式 可以用来将一个含有N个元素的数据结构类型分解成所需的几部分。

例如grades保存了100个成绩数据而我们只关心首末两个成绩,就可以把中间的所有成绩保存到一个列表里面,如下:

first, *middle, last = grades

保存最后N个元素

  • collection.deque(maxlen=N)创建了一个固定长度的队列,当有新记录加入而队列已满时会自动移除最老的那条记录。

  • 若不指定队列的大小,也就得到了一个无界限的队列;

  • deque支持从队列两端添加或弹出元素

from collection import deque
q = deque()
q.append(1)
q.append(2)
q.append(3)
q.appendleft(4)
q.pop()
q.popleft()

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

  • heapq模块中有两个函数:nlargest()和nsmallest()

import heapq

nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums))
out: [42, 37, 23]
print(heapq.nsmallest(3,nums))
out: [-4, 1, 2]
  • 这两个函数还可以接受一个参数key

In [1]: 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'])
cheap
out:
[{'name': 'YHOO', 'price': 16.35, 'shares': 45},
 {'name': 'FB', 'price': 21.09, 'shares': 200},
 {'name': 'HPQ', 'price': 31.75, 'shares': 35}]

让字典保持有序

  • collection模块的OrderedDict会按照元素初始添加的顺序进行操作;

  • 其内部维护了一个双向链表,它会根据元素加入的顺序来排列键的位置。因此OrderedDict的大小是普通字典的2倍多。

字典的计算问题

  • 利用zip()将字典的键与值反转

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

  • collection模块的Counter类

  • 并且Counter类有一个非常方便的most_common(n)方法可以直接得到出现次数最多的前几位

from collections import Counter
words = [一系列单词组成的列表]
word_counts = Counter(words)
top_3 = word_counts.most_common(3)

通过公共键对字典列表排序

  • operator模块的itermgetter函数

from operator import itemgetter

In [26]: 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}
    ...: ]

itemgetter('fname')
Out[31]: <operator.itemgetter at 0x7f01606657d0>

rows_by_frame = sorted(rows, key=itemgetter('fname'))
Out[30]:
[{'fname': 'Big', 'lname': 'Jones', 'uid': 1004},
 {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
 {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
 {'fname': 'John', 'lname': 'Cleese', 'uid': 1001}]
  • itermgetter()函数还可以接受多个键

rows_by_frame = sorted(rows, key=itemgetter('fname','lname'))

Chap3 数字、日期和时间

对数值进行取整

  • 使用内建的round(value, ndigits)函数

>>> round(1.23, 1)
1.2
>>> round(1.27, 1)
1.3
>>> round(162773, -1)
162770
  • 当某个值恰好等于两个整数间的一半时,取整操作会去到离该值最近的那个偶数上。如1.5和2.5都会取整到2

  • round()中的ndigits可以是负数,在这种情况下会相应地取整到十位、百位。。。

对数值做格式化输出

  • 使用内建的format()函数

>>>x = 123.456
>>>format(x, '0.2f')
123.46

二进制、八进制和十六进制转换

  • 要将一个整数转换为二进制,使用bin()

  • 要将一个整数转换为八进制,使用oct()

  • 要将一个整数转换为十六进制,使用hex()

随机选择

  • random.choice()可以从序列中随机挑选出元素

>>>import random
>>>values = [1,2,3,4,5,6]
>>>random.choice(values)
4
>>>random.choice(values)
2
  • random.shuffle()可以在序列中原地打乱元素的顺序

>>>random.shuffle(values)
>>>values
[2,4,3,1,6,5]
  • random.sample()可以取样出N个元素

>>>random.sample(values, 2)
[6, 2]

时间换算

  • datatime模块可以用来完成不同时间单位间的换算。例如要表示一个时间间隔,可以创建一个timedelta实例

from datetime import timedelta

In [33]: a = timedelta(days=2, hours=6)
In [34]: b = timedelta(hours=4.5)
In [35]: c = a + b
In [36]: c.days
Out[36]: 2
In [37]: c.seconds
Out[37]: 37800
In [38]: c.seconds/3600
Out[38]: 10.5
In [39]: c.total_seconds() / 3600
Out[39]: 58.5

Chap4 迭代器和生成器

手动访问迭代器中的元素

with open('/etc/passwd') as f:
    try:
        while True:
            line = next(f)
            print(line, end='')
    except StopIteration:
        pass

委托迭代

  • 对自定义的容器对象,其内部持有一个列表丶元组或其他的可迭代对象,我们想让自己的新容器能够完成迭代操作。一般来说,我们所要做的就是定义一个__iter__()方法,将迭代请求委托到对象内部持有的容器上。

class Node:
    def __init__(self, value):
        Self._value = vaule
        self._children = []
    def __repr__(self):
        return 'Node({!r})'.format(self._value)
    def __iter__(self):
        return iter(self._children)

在这个例子中,iter()方法将迭代请求转发给对象内部持有的_children属性上。

用生成器创建新的迭代模式

  • 函数中只要出现了yield语句就会将其转变成一个生成器

def frange(start, stop, increment):
    x = start
    while x < stop:
        yield x
        x += increment
  • 注意生成器只在响应迭代操作时才运行

对迭代器做切片操作

  • itertool.islice()可以对迭代器和生成器做切片操作

In [3]: def count(n):
   ...:     while True:
   ...:         yield n
   ...:         n += 1
   ...:
In [5]: c = count(0)
In [6]: c
Out[6]: <generator object count at 0x7f92899b3c80>
----> 1 c[0]
TypeError: 'generator' object has no attribute '__getitem__'

import itertools
In [10]: for x in itertools.islice(c, 10, 20):
    ...:     print(x)
10
11
12
13
14
15
16
17
18
19

跳过可迭代对象中的前一部分元素

  • itertools.dropwhile()函数会 丢弃掉序列中的前面几个元素

    例如,我们需要读取一个文件,文件的开头有一系列注释行并不是我们想要的

from itertools import dropwhile
with open('/etc/passwd') as f:
    for line in dropwhile(lambda line: line,startwith('#'), f):
        print(line, end='')

迭代所有可能的组合

  • 我们想对一些列元素的所有可能组合进行迭代

  • itrtools.permutations()函数接受一个元素集合,将其中所有元素重排列为所有可能的情况,并以元组的形式返回。

In [11]: from itertools import permutations
In [12]: items = ['a', 'b', 'c']
In [13]: for p in permutations(items):
    ...:     print(p)
    ...:
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

#如果想得到较短的所有全排列,可以指定长度
In [14]: for p in permutations(items, 2):
    ...:     print(p)
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')
  • itertools.combinations不考虑元素间的实际顺序,即('a', 'b')和('b', 'a')被认为是相同的组合形式。

  • 若想解除这一限制,可用combinations_with_replacement。

同时迭代多个序列

  • zip()函数可以用来迭代多个序列中的元素

>>>xvalues = [1,5,4,2,10,7]
>>> yvalues = [101,78,37,15,62,99]
>>> for x, y in zip(xvalues, yvalues):
...     print(x, y)
...
1 101
5 78
4 37
2 15
10 62
7 99

在不同的容器中进行迭代

  • 我们需要对许多对象执行相同的操作,但是这些对象包含在不同的容器内,而我们希望可以避免写出嵌套的循环处理,保持代码的可读性。使用itertools.chain()方法可以用来简化这个任务。

from itertools import chain

In [18]: a = [1, 2, 3, 4]
In [19]: b = ['x', 'y', 'z']
In [20]: for x in chain(a, b):
    ...:     print (x)
    ...:
1
2
3
4
x
y
z

合并多个有序序列,再对整个有序序列进行迭代

  • heapq.merge()函数

>>>import heapq
>>>a = [1,4,7,10]
>>>b = [2,5,6,11]
>>>for c in heapq.merge(a,b):
...     print(c)
...
1
2
4
5
6
7
10
11

Chap 5 文件和IO

将输出重定向到文件中

  • 只需要在print()函数加上file关键字参数即可

with open('somefile.txt', 'rt') as f:
    print('Hello World!', file=f)

以不同的分隔符或行结尾符完成打印

>>>print('GKY',1995,5,18, sep='-',end='!!\n')
GKY-1995-5-18!!

在字符串上执行IO操作

  • 使用io.StringIO()和io.ByteIO()来创建类似于文件的对象,这些对象可操作字符串数据。

读写压缩的数据文件

  • gzip和bz2模块可以实现

import gzip
with open('somefile.gz', 'rt') as f:
    text = f.read()

import bz2
with open('somefile.bz2', 'rt') as f:
    text = f.read()
    
import gzip
with open('somefile.gz', 'wt') as f:
    f.write(text)

import bz2
with open('somefile.bz', 'wt') as f:
    f.write(text)

将二进制数据读到可变缓冲区中

  • 我们想将二进制数据直接读取到一个可变缓冲区中,中间不经过任何拷贝环节。例如我们想原地修改数据再将它写回到文件中去。

import os.path
def read_into_buffer(filename):
    buf = bytearray(os.path.getsize(filename))
    with open(filename, 'rb') as f:
        f.readinto(buf)
    return buf

with open('sample.bin', 'wb') as f:
    f.write(b'hello world')

buf = read_into_buffer('sample.bin')
In [16]: buf
Out[16]: bytearray(b'hello world')

序列化Python对象

  • 我们需要将Python对象序列化为字节流,这样就可以将其保存到文件中、存储到数据库中或者通过网络连接进行传输。

  • 序列化数据最常见的做法就是使用pickle模块。

import pickle
data = ...  #some python object
f = open('somefile', 'wb')
pickle.dump(data,f)
  • 要将对象转存为字符串,可以使用

import pickle
data = ...  #some python object
f = open('somefile', 'wb')
pickle.dumps(data,f)
  • 如果要从字节流中重新创建出对象,可以使用pickle.load()或者pickle.loads()

Chap 6 数据编码与处理

读写JSON数据

  • 主要使用JSON模块

  • 两个主要的函数为json.dumps()和json.loads()

  • 如果是文件而不是字符串的话使用json.dump()和json.load()

解析简单的XML文档

  • xml.etree.ElementTree可以从简单的XML文档中提取数据

from urllib.request import urlopen
from xml.etree.ElementTree import parse

u = urlopen('http://planet.python.org/rss20.xml')
doc = parse(u)
In [24]: for item in doc.iterfind('channel/item'):
   ....:     title = item.findtext('title')
   ....:     date = item.findtext('pubDate')
   ....:     link = item.findtext('link')
   ....:     print (title)
   ....:     print(date)
   ....:     print(link)
   ....:     print()
   ....:

以上~

大噶元宵节快乐!

Yj6ruai.gif

本文转载自公众号 NewBeeNLP,作者高开远

推荐阅读

AINLP年度阅读收藏清单

Transformers Assemble(PART I)

站在BERT肩膀上的NLP新秀们(PART I)

站在BERT肩膀上的NLP新秀们(PART II)

站在BERT肩膀上的NLP新秀们(PART III)

大幅减少GPU显存占用:可逆残差网络(The Reversible Residual Network)

鼠年春节,用 GPT-2 自动写对联和对对联

transformer-XL与XLNet笔记

AINLP-DBC GPU 云服务器租用平台建立,价格足够便宜

征稿启示 | 稿费+GPU算力+星球嘉宾一个都不少

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP君微信(id:AINLP2),备注工作/研究方向+加群目的。

qIR3Abr.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK