Chapter-5 Python 模块

重点总结

  • Python 寻找变量的顺序:Python 内置 —> 当前目录 —> 第三方库,可手动添加自定义路径
  • Python 寻找到同名的变量时,先匹先用

1 | 模块 Module

一个模块仅是一个 Python 文件,导入模块时用关键字 import

  • 整体导入
  import module
  • 部分导入

    from filename_without_py import target_file 
    # target_file can be a file, a child package or defined object...
    
  • 引入 + 别名

    别名实际上相当于一个变量, 重新赋值变量时,确保没有使用其引入

    import this_module_has_a_very_long_name as T 
    
  • 导入失败

    >>> import script.zoo
    ModuleNotFoundError: No module named 'script.zoo';'script' is not a package
    
  • 当前模块

    当前模块名称可以通过全局变量 __name__ 获取

    if __name__ == '__main__': 执行当前文件时运行的代码

    if __name__ == '__main__':
        print('aah')
      
    >>> python script.py
    'aah'
    

💻dir()

>>> dir(sys)  # 查看 sys 模块提供的名称
>>> dir() # 查看当前有那些非内置名称

# 可查看内置名称
>>> import builtins
>>> dir(builtins) 
❓ 引入模块时,Python 从那些路径寻找
>>> import sys
>>> for place in sys.path:
    print(place)
C:\python\3.11.2\Scripts\ipython3.exe
C:\python\3.11.2\python311.zip
C:\python\3.11.2\DLLs
C:\python\3.11.2\Lib
C:\python\3.11.2
# 该行空行表示当前目录
C:\python\3.11.2\Lib\site-packages

⚠️同名模块先匹先用,若在标准库路径前有其他路径具有同名模块,则直接使用

可以临时添加模块加载路径

import sys
sys.path.append('/ufs/guido/lib/python')

2 | 包 Packages

多个模块组成包

包的根目录下需创建一个__init__.py 文件(以此区分普通文件夹和 Python 包),该文件可为空

  • __all__

    __init__.py 中定义的列表,可为空,用于定义开放对象,其元素将成为可以被 import 的名称

    避免用户通过类似 from this_pkg import * 这样的操作引入不需要的:

    # 假如用户在文件已经引入了 datetime
    from datetime import datetime
    # 但下列代码可能引入同名模块,导致用户的正确引用被覆盖
    from this_pkg import *
    
  • 包内引用相对引用

    假设目录结构如下:

    Pack/
        childA/
            fileA-1.py
        childB/
            fileB-1.py
            fileB-2.py
    

    在文件 fileB-1.py 中可以:

    from . import fileB-2  # 同级引入
    from .. import childA  # 引入上级
    from ..childA import fileA-1  # 引入上级的某个文件
    
  • 包外引用 __path__

    一个包必须在__init__.py 中定义 __path__,其值必须是包含str类型的 iterable,可以为空,作用类似 sys.path

    适用情况:包内代码使用了其他包的某个文件,不定义则 python 不知道该文件在何处

  • 名称空间包 namespace package

    假设某个包内有 2 个 .py 模块,

    # 初始目录结构
    pkg
     - mod_a.py
     - mod_b.py
     
    from pkg import mod_a, mod_b 
    

    但后期内容增加,希望将两个模块放置在不同目录下,可以这样操作:

    # 调整之后的目录结构
    A
     - pkg
      - mod_a.py
    B  
     - pkg 
      - mod_b.py
      
    from pkg import mod_a, mod_b   
    

3 | .pyc 文件

为快速加载模块,Python 会对其预编译,编译文件放在 __pycache__ 目录中,文件名为 module.version.pyc

⚠️.pyc 文件只是帮助加快加载,并不是比 .py文件加载快,标准库 compileall 可以编译某个路径的文件

version:保证不同 Python 解释器实现生成的预编译文件不会互相覆盖,如mod.cpython-33.pycmod.jpython.pyc

何时重新编译?

Python 对比编译版本与源码的修改日期,已过期则重新编译

4 | 标准库

random

>>> import random
>>> random.choice(range(10))
2

Counter()

计数器,统计列表中某元素的出现次数,

from collections import Counter
c = Counter('letters')

# 逻辑相当于 {l:'letters'.count(l) for l in 'letters'}
>>> c
Counter({'e': 2, 't': 2, 'l': 1, 'r': 1, 's': 1})
>>> c.most_common(2)  # 返回前两名

📋自 3.7, Counter 保留元素插入的顺序

📋 Counter 实例化的对象类似字典,可以直接增删 key 对应的计数

>>> c["a"] += 1
>>> c["a"]
2

itertools 库

该模块包含特殊的迭代器函数

import itertools 
# 以下循环均省去print(i)   
>>> for i in itertools.chain([1,2],['a','b']): # 按单个元素迭代所有可迭代的入参    
1 2 a b

>>> for i in itertools.cycle([1,2]): # 无限循环迭代
1 2 1 2 1 2 1 2 ......   

>>> for i in itertools.accumulate([1,2,3,4]): # 计算累加值
1 3 6 10

def func(x,y):
    return a*b
>>> for i in itertools.accumulate([1,2,3,4], func): # 自插函数,计算累乘值
1 2 6 24 

itemgetter()

可以取出设定的字段值,方便排序

rows = [ {"name": "C"},{"name": "A"}, {"name": "D"}, {"name": "B"}]
>>> sorted(rows, key=itemgetter("name"))
[{'name': 'A'}, {'name': 'B'}, {'name': 'C'}, {'name': 'D'}]

📋lambda 语句也可实现,但 itemgetter() 性能更好 😊

>>> sorted(rows, key=lambda x: x['name'])

attrgetter()

可取出对象属性值,比 lambda 性能更好

from operator import attrgetter
# class User:
#	id: int  
sorted(users, key=attrgetter("user_id"))

groupby()

传一个可迭代对象,返回一个分组值,及其对应的组数据

from itertools import groupby
rows = [
    {"gender": "m","name": "John"},
    {"gender": "f", "name": "My"}, 
    {"gender": "m","name": "Bill"}, 
    {"gender": "f", "name": "Cherry"}
]
rows.sort(key=itemgetter("gender"))
for v, items in groupby(rows, key=itemgetter('gender')):
    print(v)
    for i in items:
        print(i)
# result: 
f
{'gender': 'f', 'name': 'My'}
{'gender': 'f', 'name': 'Cherry'}
m
{'gender': 'm', 'name': 'John'}
{'gender': 'm', 'name': 'Bill'}

⚠️ 用前必须将可迭代入参排好顺序

compress(data, selctors)

传入一个可迭代对象,根据第二个参数布尔序列判断是否输出值

from itertools import compress
l = ['a', 'b', 'c']
bool_list = [True, False, True]
>>> list(compress(l, bool_list))
['a', 'c']

os

提供系统交换接口

import os

>>> os.getuid()    # 用户 ID, Unix 系统可用
501
>>> os.getgid()    # 组 ID, Unix 系统可用
20
>>> os.uname()     # 获取系统信息,Unix 系统可用
posix.uname_result(sysname='Linux', 
                   nodename='Dellana', release='5.15.90.1-microsoft-standard-WSL2', 
                   version='#1 SMP Fri Jan 27 02:56:13 UTC 2023', machine='x86_64')

argparse

模块提供一种更复杂的机制处理命令行参数

🧲示例:可提取一个或多个文件名,并可选择要显示的行数

当在通过 python top.py --lines=5 alpha.txt beta.txt 在命令行运行时,该脚本会将 args.lines 设为 5 并将 args.filenames 设为 ['alpha.txt', 'beta.txt']

import argparse

parser = argparse.ArgumentParser(
    prog='top',
    description='Show top lines from each file')
parser.add_argument('filenames', nargs='+')
parser.add_argument('-l', '--lines', type=int, default=10)
args = parser.parse_args()
print(args)

数学相关

math 模块提供对浮点数学的底层C库函数的访问:

>>> import math
>>> math.cos(math.pi / 4)
0.70710678118654757
>>> math.log(1024, 2)
10.0

random模块提供了进行随机选择的工具:

>>> import random
>>> random.choice(['apple', 'pear', 'banana'])
'apple'
>>> random.sample(range(100), 10)   # sampling without replacement
[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
>>> random.random()    # random float
0.17970987693706186
>>> random.randrange(6)    # random integer chosen from range(6)
4

statistics模块计算数值数据的基本统计属性(均值,中位数,方差等):

>>> import statistics
>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> statistics.mean(data)
1.6071428571428572
>>> statistics.median(data)
1.25
>>> statistics.variance(data)
1.3720238095238095

SciPy 有许多其他模块用于数值计算

压缩相关

常见的数据存档和压缩格式由模块直接支持,包括:zlib, gzip, bz2, lzma, zipfile,tarfile:

import zlib
s = b'witch which has which witches wrist watch'
len(s)
41
t = zlib.compress(s)
len(t)
37
zlib.decompress(t)
b'witch which has which witches wrist watch'
zlib.crc32(s)
226805979

发送邮件

import smtplib
server = smtplib.SMTP('localhost')
server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
"""To: jcaesar@example.org
From: soothsayer@example.org

Beware the Ides of March.
""")
server.quit()

解释器输出

pprint()

打印输出时增加可读性

from pprint import pprint
pprint(vars)
reprlib

返回字符串

import reprlib
reprlib.repr(set('supercalifragilisticexpialidocious'))
"{'a', 'c', 'd', 'e', 'f', 'g', ...}"
textwrap

折叠文本,与 HTML 中的类似

import textwrap
doc = """The wrap() method is just like fill() except that it returns
a list of strings instead of one big string with newlines to separate
the wrapped lines."""

>>> print(textwrap.fill(doc, width=40))
The wrap() method is just like fill()
except that it returns a list of strings
instead of one big string with newlines
to separate the wrapped lines.
locale

可处理与特定地域文化相关的数据格式

import locale
>>> locale.setlocale(locale.LC_ALL, 'English_United States.1252')
'English_United States.1252'
>>> locale.localeconv()  # 获取本地配置


x = 1234567.8
>>> locale.format("%d", x, grouping=True)
'1,234,567'
>>> locale.format_string("%s%.*f", (conv['currency_symbol'],
                     conv['frac_digits'], x), grouping=True)
'$1,234,567.80'

模板

string 模块的类 Template 可渲染模板,通过占位符 $ 指定位置填充实际数据,$$ 将转为 $

from string import Template
# 1.定义模板
t = Template('${village}folk send $$10 to $cause.')
# 2.替换值,入参传字典也可以
t.substitute(village='Nottingham', cause='the ditch fund')
'Nottinghamfolk send $10 to the ditch fund.'

# 替换外来值
t.safe_substitute(d)

二进制

struct 模块提供了 pack() 和 unpack() 函数,用于处理不定长度的二进制记录格式

多线程

可使用 threading , 提供多个同步操作原语,包括线程锁、事件、条件变量和信号量,但是仍可能导致一些难以复现的问题。因此,实现多任务协作的首选方法将所有对资源的请求集中到一个线程中,然后使用 queue 模块向该线程供应来自其他线程的请求。 应用程序使用 Queue对象进行线程间通信和协调,更易于设计,更易读,更可靠

日志

日志系统可以直接通过 Python 配置,也可以通过用户配置文件加载

import logging
logging.debug('Debugging information')
logging.info('Informational message')
logging.warning('Warning:config file %s not found', 'server.conf')
logging.error('Error occurred')
logging.critical('Critical error -- shutting down')

弱引用

weakref 模块提供的工具可以不必创建引用就能跟踪对象

import weakref, gc
class A:
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return str(self.value)

a = A(10)                   # create a reference
d = weakref.WeakValueDictionary()
d['primary'] = a            # does not create a reference
>>> d['primary']                # fetch the object if it is still alive
10
>>> del a                       # remove the one reference
>>> gc.collect()                # run garbage collection right away
0
>>> d['primary']                # entry was automatically removed
KeyError: 'primary'

Decimal

适合金融相关的计算场景,采用十进制浮点数,Python 基础类型float 采用二进制浮点数

from decimal import *
Decimal('1.00') % Decimal('.10')
Decimal('0.00')
1.00 % 0.10
0.09999999999999995

Unicodedata

一个完整的数据库(许多结构化文本文件),不仅包括码点与字符名称之间的映射表,还包括各个字符的元数据,以及字符之间的关系

💻 返回字符的名称 name()

没有则 ValueError

import unicodedata

>>> unicodedata.name('😂')
'FACE WITH TEARS OF JOY'
# 可传一个unicode字符码
>>> unicodedata.name('\u00e9')
'LATIN SMALL LETTER E WITH ACUTE'

💻返回名称对应的字符 lookip()

接受不区分大小写的标准名称,返回一个unicode字符

>>> unicodedata.lookup('LATIN CAPITAL LETTER A') 
"A"
>>> unicodedata.lookup('LATIN SMALL LETTER E WITH ACUTE')
'é'

len()计算字符串中unicode字符的个数,而不是字节数

>>> len('\U0001f47b')   
1

chr()ord() 对 unicode 字符同样起作用

>>> chr(233)
'é'
>>> chr(0xe9)
'é'
>>> chr(0x1fc6)
'ῆ'

字符集统一化

>>> eacute1 = 'é'                              # UTF-8,直接粘贴
>>> eacute2 = '\u00e9'                         # Unicode代码点
>>> eacute3 = '\N{LATIN SMALL LETTER E WITH ACUTE}' # Unicode名称
>>> eacute4 = chr(233)                         # 十进制字节值
>>> eacute5 = chr(0xe9)                        # 十六进制字节值
>>> eacute1, eacute2, eacute3, eacute4, eacute5
('é', 'é', 'é', 'é', 'é')
>>> eacute1 == eacute2 == eacute3 == eacute4 == eacute5
True

如果 eacute 拼接其他字符集,尽管显示不变,但底层不再一样,可以用 unicodedata.normalize() 进行统一

eacute_normalized = unicodedata.normalize('NFC', "e\u0301")
Unicode 排序

第三方库 pyuca

>>> import pyuca
>>> coll = pyuca.Collator()
>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted_fruits = sorted(fruits, key=coll.sort_key)
>>> sorted_fruits
['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

html

🆕 Python 3.4 引入了一种方便转换 Unicode 字符的方法

import html
>>> html.unescape("è")  # 'è'

# 有专门的 mapping
from html.entities import html5
>>> html5["egrave"]  # 'è'

根据序号转为名称

char = '\u00e9'
dec_value = ord(char)
>>> html.entities.codepoint2name[dec_value]
'eacute'

struct

通过 struct,可以在二进制数据和 Python 数据结构之间任意转换

import struct
valid_png_header = b'\x89PNG\r\n\x1a\n'
data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR' + \
...    b'\x00\x00\x00\x9a\x00\x00\x00\x8d\x08\x02\x00\x00\x00\xc0'

# unpack() 将字节序列组合成 Python 数据类型
>>> if data[:8] == valid_png_header:
...    width, height = struct.unpack('>LL', data[16:24])
...    print('Valid PNG, width', width, 'height', height)
... else:
...    print('Not a valid PNG')
...
Valid PNG, width 154 height 141

# Python 数据转成字节,用 pack() 
>>> struct.pack('>L', 154)
b'\x00\x00\x00\x9a'
  • > 表示整数采用大端(big-endian)格式存储,小端<
  • 每个 L 指定一个长度为 4 字节的无符号长整数,可以写 LL,也可写 2L

其他格式说明符

说明符描述字节数
x跳过一字节1
b符号字节1
B无符号字节1
h有符号短整数2
H无符号短整数2
i有符号整数4
I无符号整数4
l有符号长整数4
L无符号长整数4
Q无符号长长(long long)整数8
f单精度浮点数4
d双精度浮点数8
p数量和字符1+ 数量
s字符数量

🧲示例>16x2L6x 表示,大端跳过 16 字节,读取 8 字节内容,跳过最后 6 字节

binascii

该标准库提供二进制数据和各种字符串描述(十六进制、base 64、uuencode 等)之间转换的函数

>>> import binascii
>>> valid_png_header = b'\x89PNG\r\n\x1a\n'
# 以 16 进制值序列的形式转换
>>> binascii.hexlify(valid_png_header)
b'89504e470d0a1a0a'

# 反向转换
>>> binascii.unhexlify(b'89504e470d0a1a0a')
b'\x89PNG\r\n\x1a\n'

re

使用需要定义一个模式 pattern 和一个 sourcepattern 可以是普通字符,也可以是正则表达式

  • re.match()

    ⚠️ 只检查源是否以 pattern 开头!!

    import re
    result = re.match('To', 'Today') # match()
    >>> result.pos
    0
    >>> result.span()
    (0, 2)
    
    re.findall(pat, source) # 查找全部
    re.split('n', source) # 按照 pattern 切分字符串,返回列表
    re.sub('a', 'b', source) # 将所有 a 替换成 b
    
  • re.complie()

    将 pattern 预编译, 提高匹配速度

    to_pattern = re.compile('To') 
    result2 = to_pattern.match('Too busy right now')
    >>> result2.group()  # 查看匹配内容
    'To'
    
  • re.search()

    在任意位置查找 pattern,找到返回一个 Match对象,找不到则返回 None,只找第一个,区分大小写

    res = re.search('to', 'Too busy, too easy')
    >>> res
    <re.Match object; span=(10, 12), match='to'>
    
  • re.findall()

    找到所有,返回列表

    res = re.findall('to', 'too busy, too easy')
    >>> res
    ['to', 'to']
    
  • re.split() 分割

    res = re.split('to', 'too busy, too easy')
    >>> res
    ['', 'o busy, ', 'o easy']
    
  • re.sub() 替换

    res = re.sub('to', "TO",'too busy, too easy')
    >>> res
    'TOo busy, TOo easy'
    

string

内置了 100 个可打印的 ASCII 字符

可用于测试正则表达式import string
>>> printable = string.printable
>>> printable
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

可用于测试正则表达式

可用于测试正则表达式# 找出所有数字
>>> re.findall('\d', printable)
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

# 找出所有空白字符
>>> re.findall('\s', printable)
[' ', '\t', '\n', '\r', '\x0b', '\x0c']

END

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值