几个Python“小伎俩”

本文首发于微信公众号:NewBeeNLP


写在前面

我又来更新啦~今天一起回顾下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()
    ....:

    以上~

    ©著作权归作者所有,转载或内容合作请联系作者
    • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
      沈念sama阅读 194,524评论 5 460
    • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
      沈念sama阅读 81,869评论 2 371
    • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
      开封第一讲书人阅读 141,813评论 0 320
    • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
      开封第一讲书人阅读 52,210评论 1 263
    • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
      茶点故事阅读 61,085评论 4 355
    • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
      开封第一讲书人阅读 46,117评论 1 272
    • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
      沈念sama阅读 36,533评论 3 381
    • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
      开封第一讲书人阅读 35,219评论 0 253
    • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
      沈念sama阅读 39,487评论 1 290
    • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
      茶点故事阅读 34,582评论 2 309
    • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
      茶点故事阅读 36,362评论 1 326
    • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
      沈念sama阅读 32,218评论 3 312
    • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
      茶点故事阅读 37,589评论 3 299
    • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
      开封第一讲书人阅读 28,899评论 0 17
    • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
      开封第一讲书人阅读 30,176评论 1 250
    • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
      沈念sama阅读 41,503评论 2 341
    • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
      茶点故事阅读 40,707评论 2 335

    推荐阅读更多精彩内容