跳转至

python 语法笔记

Quote

笔记、示例来自廖雪峰的python教程

补漏一些不熟悉的语法

函数

函数的参数

可变参数

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n
    return sum

在参数前面加上*,参数numbers接收到的是一个tuple,调用该函数时可以传入任意个参数(包括0个参数)

>>> calc(1, 2)
5
>>> calc()
0

如果已经有一个listtuple,该怎么传进去呢?

nums = [1, 2, 3]
calc(*nums)

*nums代表把nums这个list的所有元素作为可变参数传进去

关键字参数

上面的可变参数允许传入0个或任意个参数,在调用时自动组装成一个tuple

而关键字参数运行传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

调用该函数时可以只传入必选参数

>>> person('Michael', 30)
name: Michael age: 30 other: {}

也可以传入任意个数的关键字参数

>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
也可以组装出一个dict再传进去,在前面加上**,类似前面的*numbers

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

**extra表示将extra传入**kwkw将获得一个dict,这个dict是extra的一份拷贝

命名关键字参数

传入关键字参数后,我们想在函数内检查是否有cityjob参数

def person(name, age, **kw):
    if 'city' in kw:
        # 有city参数
        pass
    if 'job' in kw:
        # 有job参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。

调用方式:

>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

参数组合

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

高级特性

列表生成式

>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

还可以两层循环

>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

用例: 列出当前目录下所有的文件和目录名

>>> import os
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录

if...else 的用法

>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

函数式编程

高阶函数

map

>>> def f(x):
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

filter

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

实例:埃氏素数筛

# 先构造一个从3开始的奇数序列
def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n
# 这是一个生成器,且是一个无限序列

# 然后定义一个筛选函数
def _not_divisible(n):
    return lambda x: x % n > 0

def primes():
    yield 2
    it = _odd_iter()
    while True:
        n = next(it)
        yield n
        it = filter(_not_divisible(n), it)

# 打印100以内的素数
for n in primes():
    if n < 100:
        print(n)
    else:
        break
注意不会死循环,filter是惰性计算,只是创建新的迭代器,不会立即把无限序列算完

装饰器

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
    return wrapper

@log
def now():
    print('2026-3-16')

# >>> now()
# call now():
# 2024-6-1

@log放到now()定义处相当于:

now = log(now)

模块化编程

__init__.py的作用

特性:在import一个package后,就会执行这个package里的__init__.py

所以应该往__init__.py里放什么东西?

  1. 包的初始化

  2. 管理包的接口

# moduleA.py
x = 'moduleA is imported!'
# __init__.py
from .moduleA import x  # 相对导入
相对导入
|- pkg/
|  |- subPkg1/
|  |  |- moduleA.py
|  |  |- moduleB.py
|  |- subPkg2/
|     |- moduleX.py
|- start.py
# start.py
from pkg.subPkg1 import moduleA

# pkg/subPkg1/moduleA.py
import moduleB

运行start.py会报错,因为 import 模块的搜寻路径是根据sys.path的列表来的,而sys.path内是当前工作目录,标准库和第三方库的路径,如果运行start.py就会找不到moduleB

解决办法:

A: 把路径append到sys.path

import sys
from pathlib import Path
sys.path.append(
    str(Path('./pkg/subPkg1').absolute())
)

但这样不优雅,且文件夹名改变后就要修改

B: 改为相对导入

# moduleA
from . import moduleB
from ..subPkg2 import moduleX
相对导入必须以from开头

但相对导入也有坑

直接运行相对导入的文件时(也就是这里的moduleA.py),不是以sys.path为搜寻路径,而是从module的__package__中寻找,而当一个文件作为最开始运行的脚步,他的__package__None

所以一个文件使用了相对导入后,就不该直接运行改文件,而是在包外面新建一个文件来启动

然后就可以在main.py里 import x 了

from packageA import x

有时可以写from packageA import **代表所有,这个所有并非指所有变量,而是通过对应__init__.py里的__all__定义的

from . import

__all__ = ['x', 'moduleA']
  1. 包的信息

__version__, __author__这类