这篇文章总结一些平时容易被忽略的 Python 知识

Primitives, Variables and Expressions

PYTHON
print(f"{year:>3d} {principal:0.2f}")
Click to expand and view more

Arithmetic Operators

round(x, [n]): 该函数采用 Banker’s Rounding 银行家舍入法,也叫 四舍六入五成双,当要舍弃的数字正好是 5 时

这样做的目的是减少舍入误差的累积,在统计学和金融计算中更为公平。

PYTHON
# 常规四舍五入(Python实际行为是银行家舍入)
print(round(1.5))   # 2  (1是奇数,5进位)
print(round(2.5))   # 2  (2是偶数,5舍去)
print(round(3.5))   # 4  (3是奇数,5进位)
print(round(4.5))   # 4  (4是偶数,5舍去)

# 更复杂的例子
print(round(1.25, 1))  # 1.2 (2是偶数,5舍去)
print(round(1.35, 1))  # 1.4 (3是奇数,5进位)
print(round(1.251, 1)) # 1.3 (因为后面还有1,不是正好5,正常进位)
Click to expand and view more

银行家舍入法是 IEEE 754 标准推荐的方式,Python、R、NumPy 等都采用这种舍入方式,能有效减少大量数据计算时的统计偏差。


Python 二进制运算符会将整数视为 2’s complement binary representation 二进制补码,并且符号位会在左侧无限扩展。 此外,Python 不会截断二进制,也不会溢出。

Text Strings

Immediately adjacent string literals 紧邻的字符串 会被拼接成单个字符串:

PYTHON
print(
'Content-type: text/html\n'
'\n'
'<h1> Hello World </h1>\n'
'Clock <a href="http://www.python.org">here</a>\n'
)
Click to expand and view more

str()repr() 都输出一个字符串,但是并不相同

PYTHON
s = "hello\nworld"
print(str(s))
# hello
# world

print(repr(s))
# 'hello\nworld'
Click to expand and view more

File Input and Output

一行一行读取文件

PYTHON
with open('data.text') as file:
    for line in file:
        print(line, end='') # end='' omits to printing extra newline
Click to expand and view more

如果不使用 with 语句,代码应该这样写

PYTHON
file = open('data.text')
for line in file:
    print(line, end='') # omits to printing extra newline
file.close()
Click to expand and view more

但这样很容易忘记关闭文件,因此更加推荐上面写法

如果要读取整个文件,使用 read() 方法

PYTHON
with open('data.text') as file:
    data = file.read()
Click to expand and view more

如果说要读取一个大文件,需要给 read() 方法一个参数

PYTHON
with open('data.text') as file:
    while (chunk := file.read(10000)):
        print(chunk, end='')
Click to expand and view more

这里使用 := 运算符来获取并返回读取的值,以便在文件结束时(会读取到空字符)退出循环。

注意使用海象运算符的时候,应该总是将表达式使用括号括起来。

每次调用 file.read(10000) 后,文件内部的“读取指针”会自动向前移动 10000 个字符的位置。

如果不使用海象运算符,代码会更加冗余

PYTHON
with open('data.text') as file:
    while True:
        chunk = file.read(10000)
        if not chunk:
            break
        print(chunk, end='')
Click to expand and view more

如果要将程序内容输出到文件中,在 print() 中指定

PYTHON
with open('outupt.text', 'wt') as out:
    print('...\n', file=out)
Click to expand and view more

此外,还可以使用 write() 方法写入字符串到文件中

PYTHON
out.write('...\n')
Click to expand and view more

默认文件都使用 UTF-8 编码,如果使用不同的编码,使用 encoding 参数指定:

PYTHON
with open('data.text', encoding='latin-1') as file:
    data = file.read()
Click to expand and view more

Lists

初始化空列表有两种方式:

PYTHON
names = []
names = list()
Click to expand and view more

一般和使用 [] 是更加地道的方法,list() 更多用于类型转换:

PYTHON
letters = list('Dave')
# ['D', 'a', 'v', 'e']
Click to expand and view more

Tuples

tuple /'tʌpl/ 初始化 0 个和 1 个元素的方法

PYTHON
a = ()
b = (item, ) # 注意这里的尾逗号
Click to expand and view more

元组元素可以和数组一样使用索引获取,但更加常见的方法是解包

PYTHON
host, port = addresss
Click to expand and view more

由于元组的不可变性,最好将其视为有不同部分的不可变类型,而不是列表那种独立类型的集和。

Lists vs Tuples

结构体定义

C
// listobject.h
typedef struct {
  PyObject_VAR_HEAD
  PyObject **ob_item;   // 指向元素数组的指针
  Py_ssize_t allocated; // 已分配的槽位数量
} PyListObject;

// tupleobject.h
typedef struct {
  PyObject_VAR_HEAD
  PyObject *ob_item[1]; // 元素直接内联存储 (flexible array)
} PyTupleObject;
Click to expand and view more
方面listtuple
内存分配策略动态增长一次性分配,大小固定
内存占用较大 (有预留空间)较小 (紧凑存储)
创建速度较慢较快 (小 tuple 从缓存复用)
哈希支持不可哈希可哈希 (元素都可哈希时)

可以看到,tuple 对缓存会更加友好,更加符号局部性原理,命中率更高,且固定长度可优化,因此访问速度会比 list 更快。

Dict

PYTHON
prices = {
    'GOOG': 490.1,
    'APPL': 123.5,
    'IBM': 91.5,
    'MSFT': 52.13
}
Click to expand and view more

获取一个元素可以这样

PYTHON
if 'IBM' in prices:
    p = prices['IBM']
else:
    p = 0.0
Click to expand and view more

使用 get() 方法编写更加紧凑的版本

PYTHON
p = prices.get('IBM', 0.0)
Click to expand and view more

字典初始化也是两种方法,同样一般使用大括号初始化空字典

PYTHON
prices = {}
prices = dict()
Click to expand and view more

一般 dict() 更多用于处理键值对数据

PYTHON
pairs = [('IBM', 125), ('ACME', 50), ('PHP', 40)]
a = dict(paris)
Click to expand and view more

可以使用列表获取字典的键

PYTHON
syms = list(prices)
# ['APPL', 'MSFT', 'IBM', 'GOOG']
Click to expand and view more

或者使用 keys() 方法

PYTHON
syms = prices.keys()
Click to expand and view more

区别在于,keys() 方法会返回一个 key view,为字典键的引用(即字典修改也会改变)

PYTHON
d = {'x': 2, 'y': 3}
k = d.keys()
# dict_keys(['x', 'y'])
d['z'] = 4
k
# dict_keys(['x', 'y', 'z'])
Click to expand and view more

如果要获取值,使用 dict.values()。 如果想获取键值对,使用 doct.items()

这样遍历字典

PYTHON
from sym, price in prices.items():
    print(f'{sym} = {prices})
Click to expand and view more

Script Writing

任何 py 文件都可以作为脚本运行,或者作为通过 import 导入的库。 为了更好地支持导入,脚本代码通常放在一个模块名 __name__ 进行条件检查的语句块里。 __name__ 是一个内置变量,其始终包括当前模块的名称。

如果要传递命令行参数,可以这样,例如给一个脚本传递一个文件名称

PYTHON
def main(argv):
    if len(argv) == 1:
        filename = input('Enter filename: ')
    elif len(argv) == 2:
        filename = argv[1]
    else:
        raise SystemExit(f'usage: {argv[0]} [filename]')
    portfolio = read_portfolio(filename)
    for name, shares, price in portfolio:
        print(f'{name:>10s'} {shares:10d} {price:10.2f})

if __name__ == '__main__':
    import sys
    main(sys.argv)
Click to expand and view more

对于简单的程序而言,sys.argv 已经足够了,如果有更加复杂的需求,使用 argparse 库。

Packages

在更大的项目中,一般会将代码组织成一个包。 包是一个模块的层次化集合,类似这样。

TEXT
tutorial/
    __init__.py
    readport.py
    pcost.py
    stack.py
    ...
Click to expand and view more

目录应该有一个 __init__.py 文件,这样就可以导入该包里面的

Structuring an Application

将大型代码库组织成包结构是一种标准做法,在进行操作时,需要为顶级目录选择一个唯一的包名。 包目录的主要目的是管理导入语句和编程时使用的模块命名空间。

除了源代码外,可能还有测试、示例、脚本和文档等附加内容。 这些附加材料存放在与源码不同的目录中。 常见的做法是为项目创建一个顶层的总目录,并将所有工作内容至于其下。

TEXT
tutorial-project/
    tutorial/
        __init__.py
        readport.py
        pcost.py
        stack.py
        ...
    tests/
        test_stack.py
        test_pcost.py
        ...
    examples/
        sample.py
        ...
    doc/
        tutorial.txt
        ...
Click to expand and view more

Managing Third Party Packages

使用下面命令去 PyPI 安装第三方软件包

BASH
python3 -m pip install
Click to expand and view more

安装的包会在 site-packages 目录下面,可以使用 sys.path 看到。 在 Unix 机器上,目录可能在 /usr/local/bin/python3.10/site-packages。 如果想知道某个模块具体的位置,可以在导入后使用 __file__ 变量查看:

PYTHON
import asyncio
asyncio.__file__
# '/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/asyncio/__init__.py'
Click to expand and view more

实际中,更加常见的是使用虚拟环境

PYTHON
python3 -m venv myproject
Click to expand and view more

这样将创建一个项目目录叫 myproject,在该目录中将会创建下面的目录结构

TEXT
myproject
├── bin/                # Linux/Mac: 可执行文件目录
│   ├── python          # Python解释器
│   ├── pip             # 包管理工具
│   └── activate        # 激活脚本
├── lib/                # Python库文件
│   └── python3.x/      # 具体Python版本
│       └── site-packages/  # 安装的第三方包
└── pyvenv.cfg         # 环境配置文件
Click to expand and view more

这里不建议直接使用 pip 安装包,而是这样

PYTHON
./myproject/bin/python3 -m pip install somepackage
Click to expand and view more

Start searching

Enter keywords to search articles

↑↓
ESC
⌘K Shortcut