re 模块

正则

  • 最短匹配模式(非贪婪)
  • 多行匹配(re.compile(r'/\*(.*?)\*/', re.DOTALL) 或者 (?:.|\n) 指定一个非捕获组)

查找

如果想匹配的是字面字符串,那么通常只需要调用基本字符串方法就行, 比如 str.find() , str.endswith() , str.startswith()

复杂的匹配需要使用正则表达式和 re 模块。

>>> text1 = '11/27/2012'
>>> text2 = 'Nov 27, 2012'
>>>
>>> import re
>>> # Simple matching: \d+ means match one or more digits
>>> if re.match(r'\d+/\d+/\d+', text1):
... print('yes')
... else:
... print('no')
...

如果想使用同一个模式去做多次匹配,你应该先将模式字符串预编译为模式对象。比如:

>>> datepat = re.compile(r'\d+/\d+/\d+')
>>> if datepat.match(text1):
... print('yes')
... else:
... print('no')
...

match() 总是从字符串开始去匹配,如果你想查找字符串任意部分的模式出现位置, 使用 findall() 方法去代替。

在定义正则式的时候,通常会利用括号去捕获分组。

>>> datepat = re.compile(r'(\d+)/(\d+)/(\d+)')
>>> text
'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> datepat.findall(text)
[('11', '27', '2012'), ('3', '13', '2013')]
>>> for month, day, year in datepat.findall(text):
... print('{}-{}-{}'.format(year, month, day))
...
2012-11-27
2013-3-13
>>>

findall() 方法会搜索文本并以列表形式返回所有的匹配。 如果想以迭代方式返回匹配,可以使用 finditer() 方法来代替,比如:

>>> for m in datepat.finditer(text):
... print(m.groups())
...
('11', '27', '2012')
('3', '13', '2013')
>>>

替换

对于简单的字面模式,直接使用 str.replace() 方法即可,对于复杂的模式,使用 re 模块中的 sub() 函数。

>>> text = 'UPPER PYTHON, lower python, Mixed Python'
>>> re.findall('python', text, flags=re.IGNORECASE)
['PYTHON', 'python', 'Python']
>>> re.sub('python', 'snake', text, flags=re.IGNORECASE)
'UPPER snake, lower snake, Mixed snake'
>>>

上面例子有个缺陷,替换字符串并不会自动跟被匹配字符串的大小写保持一致。

def matchcase(word):
    def replace(m):
        text = m.group()
        if text.isupper():
            return word.upper()
        elif text.islower():
            return word.lower()
        elif text[0].isupper():
            return word.capitalize()
        else:
            return word
    return replace

>>> re.sub('python', matchcase('snake'), text, flags=re.IGNORECASE)
'UPPER SNAKE, lower snake, Mixed Snake'
>>>

matchcase('snake') 返回了一个回调函数(参数必须是 match 对象),前面一节提到过, sub() 函数除了接受替换字符串外,还能接受一个回调函数。

对于一般的忽略大小写的匹配操作,简单的传递一个 re.IGNORECASE 标志参数就已经足够了。但是需要注意的是,这个对于某些需要大小写转换的 Unicode 匹配可能还不够。

在正则式中使用 Unicode

默认情况下re模块已经对一些 Unicode 字符类有了基本的支持。 比如,\\d 已经匹配任意的 unicode 数字字符了。

>>> import re
>>> num = re.compile('\d+')
>>> # ASCII digits
>>> num.match('123')
<_sre.SRE_Match object at 0x1007d9ed0>
>>> # Arabic digits
>>> num.match('\u0661\u0662\u0663')
<_sre.SRE_Match object at 0x101234030>
>>>

混合使用Unicode和正则表达式,最好还是使用增强的第三方库,比如: regex

字符串令牌解析

有一个字符串,想从左至右将其解析为一个令牌流。

对于复杂的语法,最好是选择某个解析工具比如 PyParsing 或者是 PLY。

unicodedata 模块

标准化对于任何需要以一致的方式处理Unicode文本的程序都是非常重要的,当处理来自用户输入的字符串而你很难去控制编码的时候尤其如此。

>>> s1 = 'Spicy Jalape\u00f1o'
>>> s2 = 'Spicy Jalapen\u0303o'
>>> s1
'Spicy Jalapeño'
>>> s2
'Spicy Jalapeño'
>>> s1 == s2
False
>>> len(s1)
14
>>> len(s2)
15
>>>

>>> import unicodedata
>>> t1 = unicodedata.normalize('NFC', s1)
>>> t2 = unicodedata.normalize('NFC', s2)
>>> t1 == t2
True
>>> print(ascii(t1))
'Spicy Jalape\xf1o'
>>> t3 = unicodedata.normalize('NFD', s1)
>>> t4 = unicodedata.normalize('NFD', s2)
>>> t3 == t4
True
>>> print(ascii(t3))
'Spicy Jalapen\u0303o'
>>>

>>> t1 = unicodedata.normalize('NFD', s1)
>>> ''.join(c for c in t1 if not unicodedata.combining(c))
'Spicy Jalapeno'
>>>

collections 模块

deque()

在迭代操作或者其他操作的时候,只保留最后有限几个元素的历史记录。deque(maxlen=N)构造函数会新建一个固定大小的队列。当新的元素加入并且这个队列已满的时候, 最老的元素会自动被移除掉。

在队列两端插入或删除元素时间复杂度都是O(1),区别于列表,在列表的开头插入或删除元素的时间复杂度为O(N)

在写查询元素的代码时,通常会使用包含yield表达式的生成器函数,这样可以将搜索过程代码和使用搜索结果代码解耦。

from collections import deque


def my_search(lines, pattern, history=5):
    previous_lines = deque(maxlen=history)
    for line in lines:
        if pattern in line:
            yield line, previous_lines
        previous_lines.append(line)

# Example use on a file
if __name__ == '__main__':
    with open(r'../../cookbook/somefile.txt') as f:
        for line, prevlines in my_search(f, 'python', 5):
            for pline in prevlines:
                print(pline, end='')
            print(line, end='')
            print('-' * 20)

defaultdict()

一个字典就是一个键对应一个单值的映射。如果你想要一个键映射多个值,就需要将这多个值放到另外的容器中,比如列表或者集合里面。

可以很方便的使用collections模块中的defaultdict来构造这样的字典。

from collections import defaultdict

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

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

需要注意的是, defaultdict 会自动为将要访问的键(就算目前字典中并不存在这样的键)创建映射实体。

OrderedDict()

创建一个字典,并且在迭代或序列化这个字典的时候能够控制元素的顺序。

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

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])

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

需要注意的是,一个 OrderedDict 的大小是一个普通字典的两倍,因为它内部维护着另外一个链表。 所以如果你要构建一个需要大量 OrderedDict 实例的数据结构的时候(比如读取 100,000 行 CSV 数据到一个 OrderedDict 列表中去), 那么你就得仔细权衡一下是否使用 OrderedDict 带来的好处要大过额外内存消耗的影响。

Counter()

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

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)
print(top_three)
# Outputs [('eyes', 8), ('the', 5), ('look', 4)]

namedtuple()

collections.namedtuple()这个函数实际上是一个返回 Python 中标准元组类型子类的一个工厂方法。

传递一个类型名和你需要的字段给它,然后它就会返回一个类,可以初始化这个类,为定义的字段传递值等。

>>> from collections import namedtuple
>>> Subscriber = namedtuple('Subscriber', ['addr', 'joined'])
>>> sub = Subscriber('jonesy@example.com', '2012-10-19')
>>> sub
Subscriber(addr='jonesy@example.com', joined='2012-10-19')
>>> sub.addr
'jonesy@example.com'
>>> sub.joined
'2012-10-19'
>>>

尽管 namedtuple 的实例看起来像一个普通的类实例,但是它跟元组类型是可交换的,支持所有的普通元组操作,比如索引和解压。 比如:

>>> len(sub)
2
>>> addr, joined = sub
>>> addr
'jonesy@example.com'
>>> joined
'2012-10-19'
>>>

命名元组的一个主要用途是将代码从下标操作中解脱出来。 因此,在数据库调用中返回了一个很大的元组列表的情况下,使用命名元组增删就会很方便。

def compute_cost(records):
    total = 0.0
    for rec in records:
        total += rec[1] * rec[2]
    return total

下标操作通常会让代码表意不清晰,并且非常依赖记录的结构。 下面是使用命名元组的版本:

from collections import namedtuple

Stock = namedtuple('Stock', ['name', 'shares', 'price'])
def compute_cost(records):
    total = 0.0
    for rec in records:
        s = Stock(*rec)
        total += s.shares * s.price
    return total

命名元组另一个用途就是作为字典的替代,因为字典存储需要更多的内存空间。 如果需要构建一个非常大的包含字典的数据结构,那么使用命名元组会更加高效。 但是需要注意的是,不像字典那样,一个命名元组是不可更改的。比如:

>>> s = Stock('ACME', 100, 123.45)
>>> s
Stock(name='ACME', shares=100, price=123.45)
>>> s.shares = 75
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>>

如果真的需要改变属性的值,那么可以使用命名元组实例的 _replace() 方法, 它会创建一个全新的命名元组并将对应的字段用新的值取代。比如:

>>> s = s._replace(shares=75)
>>> s
Stock(name='ACME', shares=75, price=123.45)
>>>

ChainMap

现在有多个字典或者映射,将它们从逻辑上合并为一个单一的映射后执行某些操作,比如查找值或者检查某些键是否存在。

a = {'x': 1, 'z': 3 }
b = {'y': 2, 'z': 4 }
from collections import ChainMap
c = ChainMap(a,b)
print(c['x']) # Outputs 1 (from a)
print(c['y']) # Outputs 2 (from b)
print(c['z']) # Outputs 3 (from a)

一个 ChainMap 接受多个字典并将它们在逻辑上变为一个字典。 然后,这些字典并不是真的合并在一起了, ChainMap 类只是在内部创建了一个容纳这些字典的列表 并重新定义了一些常见的字典操作来遍历这个列表。大部分字典操作都是可以正常使用。

update() 不同的是:使用 update 如果原字典做了更新,这种改变不会反应到新的合并字典中去。ChainMap 使用原来的字典,它自己不创建新的字典,所以会随之改变。

heapq 模块

从一个集合中获得最大或者最小的 N 个元素列表。

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

import heapq
nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]

在底层实现里面,首先会先将集合数据进行堆排序后放入一个列表中。

堆数据结构最重要的特征是heap[0]永远是最小的元素。并且剩余的元素可以很容易的通过调用heapq.heappop()方法得到, 该方法会先将第一个元素弹出来,然后用下一个最小的元素来取代被弹出元素(这种操作时间复杂度仅仅是O(log N)N是堆大小)。

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

当要查找的元素个数相对比较小的时候,函数 nlargest()nsmallest() 是很合适的。 如果你仅仅想查找唯一的最小或最大(N=1)的元素的话,那么使用 min()max() 函数会更快些。 类似的,如果 N 的大小和集合大小接近的时候,通常先排序这个集合然后再使用切片操作会更快点 ( sorted(items)[:N] 或者是 sorted(items)[-N:] )。 需要在正确场合使用函数 nlargest()nsmallest() 才能发挥它们的优势 (如果 N 快接近集合大小了,那么使用排序操作会更好些)。

顺序迭代合并后的排序迭代对象

有一系列排序序列,想将它们合并后得到一个排序序列并在上面迭代遍历。

>>> 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

heapq.merge() 需要所有输入序列必须是排过序的。 特别的,它并不会预先读取所有数据到堆栈中或者预先排序,也不会对输入做任何的排序检测。它仅仅是检查所有序列的开始部分并返回最小的那个,这个过程一直会持续直到所有输入序列中的元素都被遍历完。

operator 模块

itemgetter()

根据某个或某几个字典字段来排序这个列表,通过使用operator模块的itemgetter函数,可以非常容易的排序这样的数据结构:

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}
]

from operator import itemgetter
rows_by_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))

itemgetter()有时候也可以用lambda表达式代替,比如:

rows_by_fname = sorted(rows, key=lambda r: r['fname'])
rows_by_lfname = sorted(rows, key=lambda r: (r['lname'],r['fname']))

使用itemgetter()方式会运行的稍微快点。因此,如果你对性能要求比较高的话就使用itemgetter()方式。

>>> min(rows, key=itemgetter('uid'))
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}
>>> max(rows, key=itemgetter('uid'))
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
>>>

attrgetter()

排序类型相同的对象,但是他们不支持原生的比较操作。

内置的 sorted() 函数有一个关键字参数 key ,可以传入一个 callable 对象给它, 这个 callable 对象对每个传入的对象返回一个值,这个值会被 sorted 用来排序这些对象。 比如,如果你在应用程序里面有一个 User 实例序列,并且你希望通过他们的 user_id 属性进行排序, 你可以提供一个以 User 实例作为输入并输出对应 user_id 值的 callable 对象。

class User:
    def __init__(self, user_id):
        self.user_id = user_id

    def __repr__(self):
        return 'User({})'.format(self.user_id)


sorted(users, key=lambda u: u.user_id)      # [User(3), User(23), User(99)]

from operator import attrgetter
sorted(users, key=attrgetter('user_id'))    # [User(3), User(23), User(99)]

itertools 模块

islice()

用于 迭代器和生成器 上的切片操作。

dropwhile()

用于丢弃一个可迭代对象前面的元素。

permutations()/combinations()/combinations_with_replacement()

排列组合的迭代(迭代遍历一个集合中元素的所有可能的排列或组合)。

zip_longest()

同时迭代多个序列,并且对其长度。

chain()

合并多个集合,创建迭代器。

groupby()

有一个字典或者实例的序列,然后你想根据某个特定的字段比如 date 来分组迭代访问。

itertools.groupby() 函数对于这样的数据分组操作非常实用。

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'},
]

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

compress()

它以一个 iterable 对象和一个相对应的 Boolean 选择器序列作为输入参数。 然后输出 iterable 对象中对应选择器为 True 的元素。 当需要用另外一个相关联的序列来过滤某个序列的时候,这个函数是非常有用的。 比如:

addresses = [
    '5412 N CLARK',
    '5148 N CLARK',
    '5800 E 58TH',
    '2122 N CLARK',
    '5645 N RAVENSWOOD',
    '1060 W ADDISON',
    '4801 N BROADWAY',
    '1039 W GRANVILLE',
]
counts = [ 0, 3, 10, 4, 1, 7, 6, 1]

将那些对应 count 值大于5的地址全部输出,可以这样做:

>>> from itertools import compress
>>> more5 = [n > 5 for n in counts]
>>> more5
[False, False, True, False, False, True, True, False]
>>> list(compress(addresses, more5))
['5800 E 58TH', '1060 W ADDISON', '4801 N BROADWAY']
>>>

这里的关键点在于先创建一个 Boolean 序列,指示哪些元素符合条件。 然后 compress() 函数根据这个序列去选择输出对应位置为 True 的元素。

filter() 函数类似, compress() 也是返回的一个迭代器。因此,如果你需要得到一个列表, 那么你需要使用 list() 来将结果转换为列表类型。

logging 模块

time 模块

import time

# 当前时间戳(秒)
ts = time.time()
print("当前时间戳:", ts, type(ts))  
# 输出示例: 1681440000.123456 <class 'float'>

# 时间戳 -> 本地时间元组
local = time.localtime(ts)
print("本地时间元组:", local, type(local))  
# time.struct_time(tm_year=..., ...) <class 'time.struct_time'>

# 时间元组 -> 字符串
str_time = time.strftime("%Y-%m-%d %H:%M:%S", local)
print("格式化时间字符串:", str_time, type(str_time))  
# '2025-04-10 15:30:45' <class 'str'>

# 字符串 -> 时间元组
parsed = time.strptime("2025-01-01 12:00:00", "%Y-%m-%d %H:%M:%S")
print("解析后的时间元组:", parsed, type(parsed))  
# time.struct_time(tm_year=2025, ...) <class 'time.struct_time'>

# 时间元组 -> 时间戳
ts_from_struct = time.mktime(parsed)
print("从结构体得到时间戳:", ts_from_struct, type(ts_from_struct))  
# 1735723200.0 <class 'float'>

datetime 模块

from datetime import datetime, timedelta

# 当前时间
now = datetime.now()
print("当前 datetime:", now, type(now))  
# 2025-04-10 15:30:45.123456 <class 'datetime.datetime'>

# 自定义时间
d = datetime(2024, 12, 31, 23, 59, 59)
print("自定义 datetime:", d, type(d))  
# 2024-12-31 23:59:59 <class 'datetime.datetime'>

# datetime -> 字符串
formatted = d.strftime("%Y/%m/%d %H:%M")
print("格式化:", formatted, type(formatted))  
# '2024/12/31 23:59' <class 'str'>

# 字符串 -> datetime
parsed_dt = datetime.strptime("2025-03-15 08:30", "%Y-%m-%d %H:%M")
print("字符串解析为 datetime:", parsed_dt, type(parsed_dt))  
# 2025-03-15 08:30:00 <class 'datetime.datetime'>

# 获取时间差
delta = now - parsed_dt
print("时间差:", delta, type(delta))  
# datetime.timedelta(days=..., seconds=...) <class 'datetime.timedelta'>

print("差多少天:", delta.days, type(delta.days))  
# 26 <class 'int'>
print("差多少秒:", delta.total_seconds(), type(delta.total_seconds()))  
# 2276400.0 <class 'float'>

# 加减时间
future = now + timedelta(days=7)
print("7天后:", future, type(future))  
# <class 'datetime.datetime'>
past = now - timedelta(hours=3)
print("3小时前:", past, type(past))  
# <class 'datetime.datetime'>

# datetime -> 时间戳
timestamp = now.timestamp()
print("当前 datetime 转时间戳:", timestamp, type(timestamp))  
# 1681440000.0 <class 'float'>

# 时间戳 -> datetime
from_ts = datetime.fromtimestamp(timestamp)
print("时间戳转 datetime:", from_ts, type(from_ts))  
# <class 'datetime.datetime'>

# ISO 格式时间(标准格式)
iso_str = now.isoformat()
print("ISO 格式:", iso_str, type(iso_str))  
# '2025-04-10T15:30:45.123456' <class 'str'>

# 获取当前 UTC 时间
utc_now = datetime.utcnow()
print("当前 UTC 时间:", utc_now, type(utc_now))  
# <class 'datetime.datetime'>

# 替换 datetime 中的部分值
new_time = now.replace(hour=0, minute=0)
print("替换后的时间:", new_time, type(new_time))  
# <class 'datetime.datetime'>
内容 示例输出类型
时间戳 <class 'float'>
时间元组 <class 'time.struct_time'>
格式化字符串时间 <class 'str'>
datetime 对象 <class 'datetime.datetime'>
时间差对象 <class 'datetime.timedelta'>
total_seconds() <class 'float'>
days 属性 <class 'int'>

time 和 datetime 对比

功能 time 模块 datetime 模块
时间戳操作 time.time() datetime.timestamp()
字符串格式化 strftime / strptime ✅ 同样支持
时间差计算 ❌(需手动计算) datetime - datetime
加减时间 ❌(需要封装) ✅ 使用 timedelta
可读性 & OOP 较底层 更直观、更强大

pytz 模块

对几乎所有涉及到时区的问题,都应该使用 pytz 模块。这个包提供了Olson时区数据库, 它是时区信息的事实上的标准,在很多语言和操作系统里面都可以找到。

pytz 模块一个主要用途是将 datetime 库创建的简单日期对象本地化。 比如,下面如何表示一个芝加哥时间的示例:

>>> from datetime import datetime
>>> from pytz import timezone
>>> d = datetime(2012, 12, 21, 9, 30, 0)
>>> print(d)
2012-12-21 09:30:00
>>>

>>> # Localize the date for Chicago
>>> central = timezone('US/Central')
>>> loc_d = central.localize(d)
>>> print(loc_d)
2012-12-21 09:30:00-06:00
>>>

os 模块

八进制

>>> import os
>>> os.chmod('script.py', 0755)
    File "<stdin>", line 1
        os.chmod('script.py', 0755)
                            ^
SyntaxError: invalid token
>>>

# 需确保八进制数的前缀是 0o 
>>> os.chmod('script.py', 0o755)
>>>

operator 模块

math 模块

数学函数

import math
print(f"math.ceil(3.2) = {math.ceil(3.2)}")  # 输出: math.ceil(3.2) = 4 (向上取整)
print(f"math.floor(3.8) = {math.floor(3.8)}")  # 输出: math.floor(3.8) = 3 (向下取整)
print(f"math.trunc(-3.7) = {math.trunc(-3.7)}")  # 输出: math.trunc(-3.7) = -3 (截断小数部分)

decimal 模块

精确计算浮点数(并能容忍一定的性能损耗)。

decimal 模块主要用在涉及到金融的领域。 在这类程序中,哪怕是一点小小的误差在计算过程中蔓延都是不允许的。 因此, decimal 模块为解决这类问题提供了方法。 当Python和数据库打交道的时候也通常会遇到 Decimal 对象,并且,通常也是在处理金融数据的时候。

>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> print(a + b)
6.3
>>> (a + b) == Decimal('6.3')
True

fractions 模块

fractions 模块可以被用来执行包含分数的数学运算。比如:

>>> from fractions import Fraction
>>> a = Fraction(5, 4)
>>> b = Fraction(7, 16)
>>> print(a + b)
27/16
>>> print(a * b)
35/64

>>> # Getting numerator/denominator
>>> c = a * b
>>> c.numerator
35
>>> c.denominator
64

>>> # Converting to a float
>>> float(c)
0.546875

>>> # Limiting the denominator of a value
>>> print(c.limit_denominator(8))
4/7

>>> # Converting a float to a fraction
>>> x = 3.75
>>> y = Fraction(*x.as_integer_ratio())
>>> y
Fraction(15, 4)
>>>

random 模块

random模块有大量的函数用来产生随机数和随机选择元素。 比如,要想从一个序列中随机的抽取一个元素,可以使用 random.choice() :

>>> import random
>>> values = [1, 2, 3, 4, 5, 6]
>>> random.choice(values)
2
>>> random.choice(values)
3
>>> random.choice(values)
1
>>> random.choice(values)
4
>>> random.choice(values)
6
>>>

为了提取出N个不同元素的样本用来做进一步的操作,可以使用 random.sample()

>>> random.sample(values, 2)
[6, 2]
>>> random.sample(values, 2)
[4, 3]
>>> random.sample(values, 3)
[4, 3, 1]
>>> random.sample(values, 3)
[5, 4, 1]
>>>

打乱序列中元素的顺序,可以使用 random.shuffle()

>>> random.shuffle(values)
>>> values
[2, 4, 6, 5, 3, 1]
>>> random.shuffle(values)
>>> values
[3, 5, 2, 1, 6, 4]
>>>

生成随机整数,请使用 random.randint() :

>>> random.randint(0,10)
2
>>> random.randint(0,10)
5
>>> random.randint(0,10)
0
>>> random.randint(0,10)
7
>>> random.randint(0,10)
10
>>> random.randint(0,10)
3
>>>

为了生成0到1范围内均匀分布的浮点数,使用 random.random()

>>> random.random()
0.9406677561675867
>>> random.random()
0.133129581343897
>>> random.random()
0.4144991136919316
>>>

如果要获取N位随机位(二进制)的整数,使用 random.getrandbits() :

>>> random.getrandbits(200)
335837000776573622800628485064121869519521710558559406913275
>>>

struct 模块

hashlib 模块

statistics 模块