Skip to content

第2章 基础语法

学习目标

完成本章学习后,读者将能够:

  • 掌握Python注释的编写规范与文档字符串(Docstring)标准
  • 理解Python变量模型的本质——引用语义与对象模型
  • 熟练运用f-string等格式化方法进行专业级输出
  • 理解Python缩进机制的设计原理与工程意义
  • 遵循PEP 8规范编写符合行业标准的代码

2.1 注释与文档

2.1.1 单行注释

Python使用#标记单行注释,解释器会忽略从#到行末的所有内容:

python
# 计算圆的面积
radius = 5
area = 3.14159 * radius ** 2  # 面积公式:S = πr²

2.1.2 多行注释与文档字符串

Python没有真正的多行注释语法。三引号字符串("""''')在模块/类/函数级别会成为文档字符串(Docstring),而非注释:

python
"""
模块级文档字符串

描述整个模块的功能、作者、版本等信息。
可通过 __doc__ 属性或 help() 函数访问。
"""

def calculate_bmi(weight: float, height: float) -> float:
    """计算身体质量指数(BMI)

    使用国际标准公式:BMI = 体重(kg) / 身高(m)²

    Args:
        weight: 体重,单位千克(kg)
        height: 身高,单位米(m)

    Returns:
        BMI值,保留一位小数

    Raises:
        ValueError: 当身高或体重为非正数时

    Examples:
        >>> calculate_bmi(70, 1.75)
        22.9
    """
    if weight <= 0 or height <= 0:
        raise ValueError("体重和身高必须为正数")
    return round(weight / height ** 2, 1)

学术注记:Python文档字符串遵循PEP 257规范。主流格式包括Google风格、NumPy风格和Sphinx/reST风格。上述示例采用Google风格,因其可读性最佳,且可被Sphinx自动解析生成API文档。

2.1.3 注释最佳实践

python
# 好的注释:解释"为什么"(Why),而非"是什么"(What)
# 使用二分查找替代线性搜索,将时间复杂度从O(n)降至O(log n)
def binary_search(arr: list, target: int) -> int:
    pass

# 不好的注释:重复代码内容
x = 10  # 设置x等于10

# 好的注释:解释不明显的业务逻辑
# 边界条件:空列表或单元素列表天然有序,直接返回
if len(arr) <= 1:
    return arr

# 特殊标记注释(IDE和工具可识别)
# TODO: 添加并发安全机制
# FIXME: 当输入为空列表时返回值不正确
# NOTE: 此函数会修改传入列表的原地顺序
# HACK: 临时绕过第三方库的编码问题,待升级后移除
# XXX: 此处逻辑复杂,需要仔细审查

2.2 变量与对象模型

2.2.1 Python的对象模型

理解Python变量的关键在于:Python中变量是对象的引用(标签),而非存储值的容器

C语言模型:                Python模型:
┌──────┐                  name ──┐
│ name │──→ 42             age  ──┤
└──────┘                          │
┌─────┐                    ┌──────┴──────┐
│ age  │──→ 25             │  int(42)    │
└─────┘                    │  id: 1407... │
                           │  value: 42  │
                           │  type: int  │
                           └─────────────┘
                           ┌─────────────┐
                           │  int(25)    │
                           │  id: 1407... │
                           │  value: 25  │
                           │  type: int  │
                           └─────────────┘
python
x = 42
print(id(x))       # 对象的内存地址(标识)
print(type(x))     # 对象的类型
print(x)           # 对象的值

a = 42
b = 42
print(a is b)      # True - 小整数缓存(-5到256)

a = 257
b = 257
print(a is b)      # 可能False - 超出缓存范围
print(a == b)      # True  - 值相等

学术注记:CPython对小整数(-5到256)和短字符串进行**驻留(Interning)**优化,相同值的对象共享同一内存地址。这是解释器实现细节,不应在业务逻辑中依赖is进行值比较。

2.2.2 可变与不可变对象

这是Python对象模型中最重要的区分:

类型可变性示例赋值行为
int, float, bool不可变x = 1修改时创建新对象
str不可变s = "hello"修改时创建新对象
tuple不可变t = (1, 2)内容不可修改
list可变l = [1, 2]原地修改
dict可变d = {"a": 1}原地修改
set可变s = {1, 2}原地修改
python
# 不可变对象:修改创建新对象
x = 42
print(id(x))       # 例如 1407357984
x = x + 1
print(id(x))       # 不同!新对象

# 可变对象:原地修改
my_list = [1, 2, 3]
print(id(my_list)) # 例如 2305843014
my_list.append(4)
print(id(my_list)) # 相同!原地修改

# 引用语义导致的陷阱
a = [1, 2, 3]
b = a              # b和a指向同一个列表对象
b.append(4)
print(a)           # [1, 2, 3, 4] - a也被修改了!

# 正确的复制方式
c = a.copy()       # 浅拷贝
c = a[:]           # 浅拷贝(切片语法)
import copy
d = copy.deepcopy(a)  # 深拷贝

2.2.3 变量命名规则

语法规则(必须遵守):

  • 只能包含字母、数字、下划线
  • 不能以数字开头
  • 不能使用Python关键字
  • 区分大小写
python
# 合法命名
name = "Alice"
user_name = "Bob"
_user = "David"
user2 = "Eve"
π = 3.14159          # Unicode标识符(不推荐)

# 非法命名
2user = "Alice"      # 不能以数字开头
user-name = "Bob"    # 不能包含连字符
class = "Math"       # 不能使用关键字

PEP 8命名约定:

python
# 变量和函数:snake_case
user_name = "Alice"
def calculate_total_price():
    pass

# 常量:UPPER_SNAKE_CASE
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30

# 类名:PascalCase
class UserProfile:
    pass

# 私有属性:单下划线前缀(约定)
class MyClass:
    _internal_state = None     # 提示:内部使用,但不强制

# 名称修饰:双下划线前缀
class MyClass:
    __private = 42             # 实际存储为 _MyClass__private

# 魔术方法:双下划线包围
class MyClass:
    def __init__(self):
        pass
    def __repr__(self):
        pass

2.2.4 赋值语法

python
# 基本赋值
x = 10

# 多重赋值
x, y, z = 1, 2, 3

# 链式赋值
x = y = z = 0

# 交换变量(Pythonic方式)
x, y = 10, 20
x, y = y, x

# 解包赋值
numbers = [1, 2, 3]
a, b, c = numbers

# 扩展解包(Python 3.0+)
first, *rest = [1, 2, 3, 4, 5]
print(first, rest)    # 1 [2, 3, 4, 5]

*head, last = [1, 2, 3, 4, 5]
print(head, last)     # [1, 2, 3, 4] 5

first, *middle, last = [1, 2, 3, 4, 5]
print(first, middle, last)  # 1 [2, 3, 4] 5

# 海象运算符(Python 3.8+,赋值表达式)
if (n := len(data)) > 10:
    print(f"数据过长:{n}条记录")

# 等价于
n = len(data)
if n > 10:
    print(f"数据过长:{n}条记录")

2.2.5 变量删除

python
x = 10
del x

data = {"a": 1, "b": 2, "c": 3}
del data["b"]
print(data)  # {'a': 1, 'c': 3}

2.2.6 Python内存管理机制

理解Python的内存管理对于编写高效代码至关重要:

引用计数(Reference Counting)

Python主要使用引用计数来管理内存,每个对象维护一个引用计数器:

python
import sys

a = [1, 2, 3]
print(sys.getrefcount(a) - 1)  # 1 (减去getrefcount自身的临时引用)

b = a
print(sys.getrefcount(a) - 1)  # 2

c = a
print(sys.getrefcount(a) - 1)  # 3

del b
print(sys.getrefcount(a) - 1)  # 2

import gc
gc.collect()

垃圾回收(Garbage Collection)

引用计数无法处理循环引用,Python使用分代垃圾回收器处理这类情况:

python
import gc

gc.disable()

class Node:
    def __init__(self):
        self.ref = None

a = Node()
b = Node()
a.ref = b
b.ref = a

del a
del b

gc.enable()
collected = gc.collect()
print(f"回收了 {collected} 个对象")

内存池机制

CPython使用内存池(PyMalloc)优化小对象的分配:

python
import sys

sizes = [sys.getsizeof(i) for i in range(100)]
print(f"整数对象大小: {sizes[0]} 字节")
print(f"列表对象大小: {sys.getsizeof([])} 字节")
print(f"字典对象大小: {sys.getsizeof({})} 字节")

学术注记:Python 3.12引入了PEP 684——每子解释器GIL,允许每个子解释器拥有独立的GIL。Python 3.13的自由线程模式(PEP 703)则完全移除了GIL,这对内存管理策略产生了深远影响。

2.2.7 对象内省(Introspection)

Python提供了丰富的内省能力,这是动态语言的核心特性之一:

python
class Person:
    species = "Homo sapiens"

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def greet(self) -> str:
        return f"Hello, I'm {self.name}"

p = Person("Alice", 30)

print(type(p))           # <class '__main__.Person'>
print(p.__class__)       # <class '__main__.Person'>
print(p.__dict__)        # {'name': 'Alice', 'age': 30}
print(p.__class__.__bases__)  # (<class 'object'>,)

print(hasattr(p, 'name'))     # True
print(hasattr(p, 'gender'))   # False
print(getattr(p, 'name'))     # Alice
print(getattr(p, 'gender', 'N/A'))  # N/A (默认值)

setattr(p, 'gender', 'Female')
print(p.gender)          # Female

delattr(p, 'gender')
print(hasattr(p, 'gender'))  # False

print(dir(p))

使用inspect模块进行深度内省:

python
import inspect

print(inspect.getmembers(p))
print(inspect.getsource(Person.greet))
print(inspect.signature(Person.__init__))
print(inspect.getmro(Person))

2.2.8 类型注解与静态类型检查

Python 3.5+引入了类型注解(Type Hints),支持静态类型检查:

python
from typing import Optional, Union, List, Dict, Callable, TypeVar, Generic

T = TypeVar('T')

def process_data(
    items: List[T],
    key: Optional[str] = None,
    transform: Optional[Callable[[T], T]] = None,
) -> Dict[str, List[T]]:
    """处理数据列表

    Args:
        items: 输入数据列表
        key: 可选的分组键
        transform: 可选的转换函数

    Returns:
        分组后的字典
    """
    result: Dict[str, List[T]] = {}
    if transform:
        items = [transform(item) for item in items]
    result[key or "default"] = items
    return result

class Container(Generic[T]):
    def __init__(self, value: T):
        self.value = value

    def get(self) -> T:
        return self.value

container: Container[int] = Container(42)

def parse_value(s: str) -> Union[int, float, str]:
    try:
        return int(s)
    except ValueError:
        try:
            return float(s)
        except ValueError:
            return s

result: Union[int, float, str] = parse_value("42")

类型检查工具:

bash
pip install mypy
mypy your_script.py
python
from typing import TypedDict, Literal, Final, Protocol

class PersonDict(TypedDict):
    name: str
    age: int
    gender: Literal['male', 'female', 'other']

MAX_SIZE: Final[int] = 100

class Drawable(Protocol):
    def draw(self) -> None: ...

class Circle:
    def draw(self) -> None:
        print("Drawing circle")

def render(shape: Drawable) -> None:
    shape.draw()

工程实践:类型注解不改变Python的运行时行为,但能显著提升代码可维护性和IDE支持。推荐在所有公共API中使用类型注解,并配置mypy进行静态检查。


2.3 输入与输出

2.3.1 print函数详解

python
# 基本输出
print("Hello, World!")

# 多参数输出(默认空格分隔)
print("Hello", "World", "Python")  # Hello World Python

# 自定义分隔符
print("2024", "01", "15", sep="/")  # 2024/01/15
print("A", "B", "C", sep="-")       # A-B-C

# 自定义结束符
print("Loading", end="")
for i in range(3):
    print(".", end="", flush=True)
print(" Done!")

# 进度条效果
import time
for i in range(101):
    print(f"\r进度: {i}%", end="", flush=True)
    time.sleep(0.02)
print()

# 输出到标准错误流
import sys
print("错误信息", file=sys.stderr)

2.3.2 字符串格式化

f-string(Python 3.6+,推荐方案)

f-string是Python最现代、最高效的字符串格式化方式:

python
name = "Alice"
age = 25

# 基本用法
print(f"姓名:{name},年龄:{age}")

# 表达式求值
print(f"2的10次方:{2 ** 10}")
print(f"姓名大写:{name.upper()}")

# 浮点数格式化
pi = 3.14159265
print(f"π ≈ {pi:.2f}")        # π ≈ 3.14
print(f"π ≈ {pi:.6f}")        # π ≈ 3.141593

# 整数格式化
num = 42
print(f"二进制:{num:b}")      # 二进制:101010
print(f"八进制:{num:o}")      # 八进制:52
print(f"十六进制:{num:x}")    # 十六进制:2a
print(f"十六进制:{num:X}")    # 十六进制:2A

# 对齐与填充
name = "Alice"
print(f"|{name:>10}|")        # 右对齐:|     Alice|
print(f"|{name:<10}|")        # 左对齐:|Alice     |
print(f"|{name:^10}|")        # 居中:  |  Alice   |
print(f"|{name:*^10}|")       # 居中填充:|**Alice***|

# 千分位分隔
value = 1234567.89
print(f"金额:{value:,.2f}")  # 金额:1,234,567.89

# 科学计数法
print(f"科学计数:{value:e}")  # 科学计数:1.234568e+06

# 百分比
ratio = 0.856
print(f"完成率:{ratio:.1%}")  # 完成率:85.6%

# 日期格式化
from datetime import datetime
now = datetime.now()
print(f"当前时间:{now:%Y-%m-%d %H:%M:%S}")

# 嵌套格式化(Python 3.12+)
width = 10
precision = 2
print(f"{pi:{width}.{precision}f}")  #       3.14

# 调试输出(Python 3.8+)
x = 42
print(f"{x = }")               # x = 42
print(f"{x = :08b}")           # x = 00101010

format方法

python
print("姓名:{},年龄:{}".format(name, age))
print("姓名:{0},年龄:{1},重复:{0}".format(name, age))
print("姓名:{name},年龄:{age}".format(name="Alice", age=25))

data = {"name": "Alice", "age": 25}
print("姓名:{name},年龄:{age}".format(**data))

%格式化(旧式,仅作了解)

python
print("姓名:%s,年龄:%d" % ("Alice", 25))
print("π ≈ %.2f" % 3.14159)

工程实践:新项目统一使用f-string。format方法仅在需要动态模板时使用。%格式化已不推荐。

2.3.3 input函数

python
# 基本输入
name = input("请输入你的名字:")

# 类型转换
age = int(input("请输入你的年龄:"))
height = float(input("请输入你的身高(米):"))

# 安全输入模式
def safe_int_input(prompt: str, min_val: int = 0, max_val: int = 150) -> int:
    """安全获取整数输入"""
    while True:
        try:
            value = int(input(prompt))
            if min_val <= value <= max_val:
                return value
            print(f"请输入{min_val}{max_val}之间的整数")
        except ValueError:
            print("请输入有效的整数")

age = safe_int_input("请输入你的年龄:", 0, 150)

# 密码输入(不回显)
import getpass
password = getpass.getpass("请输入密码:")

2.4 缩进与代码块

2.4.1 缩进机制

Python使用缩进(Indentation)而非花括号来表示代码块的层次关系,这是Python最具争议也最具特色的设计决策:

python
if True:
    print("缩进4个空格")    # 属于if代码块
    if True:
        print("缩进8个空格")  # 属于嵌套if代码块
print("回到顶层")           # 不属于任何代码块

设计哲学:Guido van Rossum选择缩进语法的原因是——代码被阅读的次数远多于编写的次数,强制缩进消除了代码格式不一致的问题,使Python代码天然具有良好的可读性。这一设计在大型项目中显著降低了代码审查的认知负担。

2.4.2 缩进规则

python
# 规则1:统一使用4个空格(PEP 8)
if True:
    print("4个空格")

# 规则2:同一代码块缩进必须一致
if True:
    print("第1行")
    print("第2行")     # 正确:与上一行缩进一致

# 规则3:续行使用额外缩进
total = (first_variable
         + second_variable
         + third_variable)

# 规则4:悬挂缩进
def long_function_name(
        var_one, var_two,
        var_three, var_four):
    print(var_one)

2.4.3 常见缩进错误

python
# 错误1:缩进不一致 → IndentationError
if True:
    print("4个空格")
      print("6个空格")    # IndentationError

# 错误2:缺少缩进 → IndentationError
if True:
print("没有缩进")         # IndentationError

# 错误3:混用Tab和空格 → TabError
if True:
    print("空格")
	print("Tab")          # TabError

# 错误4:不必要的缩进
print("顶层代码")
    print("多余缩进")     # IndentationError

工程实践:在编辑器中配置"将Tab转换为空格",杜绝Tab和空格混用问题。VS Code默认已启用此设置。


2.5 运算符

2.5.1 算术运算符

python
print(10 + 3)    # 13    加法
print(10 - 3)    # 7     减法
print(10 * 3)    # 30    乘法
print(10 / 3)    # 3.3333...  除法(返回float)
print(10 // 3)   # 3     整除(向下取整)
print(10 % 3)    # 1     取模(余数)
print(10 ** 3)   # 1000  幂运算

# 注意整除的负数行为
print(-7 // 2)   # -4    向负无穷方向取整
print(7 // -2)   # -4

# divmod同时获取商和余数
quotient, remainder = divmod(10, 3)
print(quotient, remainder)  # 3 1

2.5.2 比较运算符

python
print(10 == 10)   # True   等于
print(10 != 5)    # True   不等于
print(10 > 5)     # True   大于
print(10 < 5)     # False  小于
print(10 >= 10)   # True   大于等于
print(10 <= 5)    # False  小于等于

# Python支持链式比较
x = 5
print(1 < x < 10)     # True,等价于 1 < x and x < 10
print(1 < x > 3)      # True,等价于 1 < x and x > 3

# is vs ==
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)    # True  - 值相等
print(a is b)    # False - 不是同一对象

c = a
print(a is c)    # True  - 同一对象

2.5.3 逻辑运算符

python
print(True and False)    # False
print(True or False)     # True
print(not True)          # False

# 短路求值
x = 0
if x != 0 and 10 / x > 1:   # 不会执行除法
    pass

# 返回实际值(非布尔值)
print(1 and 2)     # 2    - and返回最后一个为真的值
print(0 and 2)     # 0    - and返回第一个为假的值
print(1 or 2)      # 1    - or返回第一个为真的值
print(0 or 2)      # 2    - or返回最后一个为假的值
print(0 or "" or None)  # None

# 实用模式
name = input("姓名:") or "匿名用户"

2.5.4 位运算符

python
print(0b1010 & 0b1100)   # 0b1000   按位与
print(0b1010 | 0b1100)   # 0b1110   按位或
print(0b1010 ^ 0b1100)   # 0b0110   按位异或
print(~0b1010)            # -11      按位取反
print(0b1010 << 2)        # 0b101000 左移
print(0b1010 >> 2)        # 0b10     右移

# 实用:权限标志
READ = 0b0001
WRITE = 0b0010
EXECUTE = 0b0100

permission = READ | WRITE
print(bool(permission & READ))    # True  - 有读权限
print(bool(permission & EXECUTE)) # False - 无执行权限

2.5.5 运算符优先级

从高到低:

优先级运算符说明
1**幂运算
2+x, -x, ~x一元运算符
3*, /, //, %乘除
4+, -加减
5<<, >>位移
6&按位与
7^按位异或
8|按位或
9==, !=, <, >, <=, >=, is, in比较
10not逻辑非
11and逻辑与
12or逻辑或
13if-else条件表达式

工程实践:不要依赖运算符优先级,复杂表达式使用括号明确意图。2 + 3 * 4 应写为 2 + (3 * 4)

2.5.6 成员运算符与身份运算符

python
fruits = ["apple", "banana", "cherry"]

print("apple" in fruits)       # True
print("orange" not in fruits)  # True

text = "Hello, Python"
print("Python" in text)        # True
print("python" in text)        # False (区分大小写)

scores = {"Alice": 95, "Bob": 87}
print("Alice" in scores)       # True (检查键)
print(95 in scores)            # False (不检查值)

a = 256
b = 256
print(a is b)                  # True (小整数缓存)

a = 257
b = 257
print(a is b)                  # False (超出缓存范围)
print(a == b)                  # True (值相等)

x = None
print(x is None)               # True (推荐用is检查None)
print(x == None)               # True (不推荐)

最佳实践is用于身份检查(是否同一对象),==用于值比较。检查None时应使用x is None而非x == None

2.5.7 运算符重载

Python允许通过魔术方法重载运算符,实现自定义类型的行为:

python
import math

class Vector2D:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return f"Vector2D({self.x}, {self.y})"

    def __add__(self, other: "Vector2D") -> "Vector2D":
        return Vector2D(self.x + other.x, self.y + other.y)

    def __sub__(self, other: "Vector2D") -> "Vector2D":
        return Vector2D(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar: float) -> "Vector2D":
        return Vector2D(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar: float) -> "Vector2D":
        return self.__mul__(scalar)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Vector2D):
            return NotImplemented
        return self.x == other.x and self.y == other.y

    def __abs__(self) -> float:
        return math.sqrt(self.x ** 2 + self.y ** 2)

    def __bool__(self) -> bool:
        return bool(self.x or self.y)

v1 = Vector2D(3, 4)
v2 = Vector2D(1, 2)

print(v1 + v2)        # Vector2D(4, 6)
print(v1 - v2)        # Vector2D(2, 2)
print(v1 * 2)         # Vector2D(6, 8)
print(2 * v1)         # Vector2D(6, 8)
print(abs(v1))        # 5.0
print(v1 == v2)       # False
print(bool(v1))       # True

常用运算符魔术方法对照表:

运算符魔术方法说明
+__add__, __radd__加法
-__sub__, __rsub__减法
*__mul__, __rmul__乘法
/__truediv__, __rtruediv__真除法
//__floordiv__, __rfloordiv__整除
%__mod__, __rmod__取模
**__pow__, __rpow__幂运算
==__eq__相等比较
!=__ne__不等比较
<__lt__小于
<=__le__小于等于
>__gt__大于
>=__ge__大于等于
[]__getitem__, __setitem__索引访问
len()__len__长度
bool()__bool__布尔转换
str()__str__字符串表示
repr()__repr__官方字符串表示

2.6 语句与表达式

2.6.1 语句(Statement)

语句是执行操作的指令,不产生值:

python
x = 10                    # 赋值语句
if x > 0: print("正数")   # 条件语句
import math               # 导入语句
def greet(): pass         # 函数定义语句
return 42                 # 返回语句

2.6.2 表达式(Expression)

表达式是计算后产生值的代码:

python
42                        # 字面量表达式
x + y                     # 算术表达式
"Hello" + " World"        # 字符串表达式
x > 0                     # 比较表达式
func()                    # 函数调用表达式
[x for x in range(10)]    # 列表推导式表达式

2.6.3 条件表达式(三元运算符)

python
# 语法:value_if_true if condition else value_if_false
status = "成年" if age >= 18 else "未成年"

# 等价于
if age >= 18:
    status = "成年"
else:
    status = "未成年"

# 嵌套使用(可读性差,不推荐)
result = "A" if score >= 90 else "B" if score >= 80 else "C"

2.7 前沿技术动态

2.7.1 模式匹配(PEP 634)

Python 3.10引入结构化模式匹配,提供更强大的条件分支:

python
def process_command(command: tuple[str, ...]) -> str:
    match command:
        case ["quit"]:
            return "Exiting..."
        case ["load", filename]:
            return f"Loading {filename}"
        case ["save", filename, *options]:
            opts = ", ".join(options)
            return f"Saving {filename} with options: {opts}"
        case ["move", source, destination] if source != destination:
            return f"Moving {source} to {destination}"
        case ["copy", source, destination]:
            return f"Copying {source} to {destination}"
        case _:
            return "Unknown command"

print(process_command(("save", "data.txt", "--force", "--backup")))

2.7.2 海象运算符(PEP 572)

Python 3.8引入赋值表达式,简化代码:

python
# 传统写法
text = input("Enter text: ")
if len(text) > 10:
    print(f"Text too long: {len(text)} characters")

# 海象运算符写法
if (n := len(text := input("Enter text: "))) > 10:
    print(f"Text too long: {n} characters")

# 在列表推导中使用
data = [1, 2, 3, 4, 5]
filtered = [y for x in data if (y := x * 2) > 4]

2.7.3 类型系统演进

Python类型系统持续演进,提供更精确的类型表达:

python
from typing import TypeAlias, Self, TypeGuard

# 类型别名(Python 3.12+)
type Vector = list[float]

# Self类型(Python 3.11+)
class Builder:
    def set_name(self, name: str) -> Self:
        self.name = name
        return self

# TypeGuard用于类型收窄
def is_string_list(val: list[object]) -> TypeGuard[list[str]]:
    return all(isinstance(x, str) for x in val)

2.7.4 参数语法增强

python
# 位置参数分隔符(Python 3.8+)
def greet(name, /, *, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Alice")                    # 正确
greet("Alice", greeting="Hi")     # 正确
greet(name="Alice")               # 错误:name是位置参数

# 参数解包增强
def func(a, b, c):
    return a + b + c

args = [1, 2, 3]
kwargs = {"a": 1, "b": 2, "c": 3}
result = func(*args)              # 列表解包
result = func(**kwargs)           # 字典解包

2.8 本章小结

本章系统介绍了Python基础语法的核心要素:

  1. 注释与文档:遵循PEP 257编写标准Docstring,注释解释"为什么"而非"是什么"
  2. 对象模型:变量是对象的引用,理解可变/不可变对象的区别是避免bug的关键
  3. 内存管理:引用计数是主要机制,分代GC处理循环引用,内存池优化小对象分配
  4. 类型系统:类型注解提升代码可维护性,mypy实现静态类型检查
  5. 格式化输出:f-string是现代Python的首选格式化方案
  6. 缩进机制:4个空格缩进是Python的语法要求,也是代码可读性的保障
  7. 运算符体系:掌握算术、比较、逻辑、位运算符及其优先级,理解运算符重载
  8. 编码规范:PEP 8是Python社区的共同约定,配合Ruff等工具自动执行

2.8.1 知识图谱

Python基础语法
├── 注释与文档
│   ├── 单行注释 (#)
│   ├── 多行注释 (""" """)
│   └── Docstring (PEP 257)
├── 变量与对象
│   ├── 对象模型 (id, type, value)
│   ├── 引用语义
│   ├── 可变/不可变对象
│   ├── 内存管理 (引用计数 + GC)
│   └── 类型注解 (Type Hints)
├── 输入输出
│   ├── print() 函数
│   ├── f-string 格式化
│   └── input() 函数
├── 缩进与代码块
│   ├── 4空格缩进
│   └── 代码块层次
└── 运算符
    ├── 算术运算符
    ├── 比较运算符
    ├── 逻辑运算符
    ├── 位运算符
    ├── 成员/身份运算符
    └── 运算符重载

2.8.2 最佳实践清单

场景推荐做法避免做法
字符串格式化f-string% 格式化
值比较==is
None检查x is Nonex == None
布尔检查if x:if x == True:
空容器检查if not lst:if len(lst) == 0:
复制列表lst.copy()lst[:]new = old
类型注解公共API必加完全不加
注释解释"为什么"重复代码内容

2.9 练习题

基础题

  1. 编写程序,使用f-string格式化输出你的姓名、年龄和BMI值,要求BMI保留1位小数。

  2. 编写程序,输入两个数,输出它们的和、差、积、商(整除和浮点除法)。

  3. 使用Google风格Docstring为一个函数编写文档,包含Args、Returns、Raises和Examples。

进阶题

  1. 解释以下代码的输出结果,并说明原因:

    python
    a = [1, 2, 3]
    b = a
    b.append(4)
    print(a)
  2. 使用位运算实现一个简单的权限系统,支持添加、移除和检查权限。

  3. 编写一个函数,使用海象运算符简化循环中的条件判断。

项目实践

  1. 个人名片生成器:编写一个Python程序,要求:
    • 使用input获取用户姓名、年龄、职业、邮箱
    • 使用f-string格式化输出一张美观的文本名片
    • 包含完整的类型注解和Docstring
    • 处理输入为空的情况(使用or提供默认值)

思考题

  1. Python使用缩进而非花括号表示代码块,这一设计决策的利弊是什么?从软件工程角度分析其对大型项目的影响。

  2. 为什么Python中a = b对于列表会导致"修改b影响a"的问题?这与C++的引用、Java的对象变量有何异同?

  3. is==的区别是什么?在什么场景下应该使用is?为什么a is b对小整数返回True而对大整数可能返回False?

2.10 延伸阅读

2.10.1 Python语言规范

2.10.2 内存管理与对象模型

2.10.3 类型系统

2.10.4 代码风格工具


下一章:第3章 数据类型

青少年创意编程 - 高中Python组 - 江苏省宿城中等专业学校