python从入门到精通-第2章: 语法基础 — 从"看起来一样"到"完全不同"

1 阅读27分钟

第2章: 语法基础 — 从"看起来一样"到"完全不同"

Java/Kotlin 开发者看 Python 代码,第一反应往往是"这不就是去掉分号和类型声明吗?"。 错。大量语法元素看起来像但行为完全不同。本章逐个拆解,每个知识点都给出 JVM 对比、Python demo、核心差异和陷阱。


2.1 缩进即语法: 没有大括号

Java/Kotlin 对比

// Java: {} 定义代码块,缩进只是风格
public class Example {
    public void greet(String name) {
        if (name != null) {
            System.out.println("Hello, " + name);
            if (name.length() > 5) {
                System.out.println("Long name!");
            }
        } else {
            System.out.println("Hello, stranger");
        }
    }
}
// 缩进不一致不影响编译,但 IDE 会格式化
// Kotlin: 同样用 {} 定义代码块
fun greet(name: String?) {
    if (name != null) {
        println("Hello, $name")
        if (name.length > 5) {
            println("Long name!")
        }
    } else {
        println("Hello, stranger")
    }
}

Python 实现

# Python: 缩进就是语法的一部分,没有 {}
# 缩进规则: 4个空格(PEP 8 标准),不要用 Tab

def greet(name):
    if name is not None:
        print(f"Hello, {name}")
        if len(name) > 5:
            print("Long name!")
    else:
        print("Hello, stranger")

# === 缩进错误示例 ===

# 错误1: 缩进不一致(IndentationError)
# def bad():
#     print("line 1")
#       print("line 2")  # IndentationError: unexpected indent

# 错误2: 混用 Tab 和空格(TabError)
# def worse():
#     print("space")     # 4 spaces
# 	print("tab")         # Tab — Python 3 不允许混用!

# === 多行语句 ===

# 方式1: 反斜杠续行(不推荐,容易出错)
long_string = "This is a very long string " \
              "that spans multiple lines"

# 方式2: 括号隐式续行(推荐)
result = (1 + 2 + 3 +
          4 + 5 + 6)

names = [
    "Alice",
    "Bob",
    "Charlie",
]

data = {
    "name": "Alice",
    "age": 30,
    "email": "alice@example.com",
}

# 方式3: 函数参数自动续行
result = some_function(
    arg1="value1",
    arg2="value2",
    arg3="value3",
)

# === pass 语句: 空代码块占位 ===
# Java/Kotlin 用空 {},Python 用 pass
class EmptyClass:
    pass  # 什么都不做,占位用

def todo_function():
    pass  # TODO: 待实现

if condition:
    pass  # 占位

核心差异

维度Java/KotlinPython
代码块界定{}缩进(4空格)
缩进作用纯风格语法的一部分
空代码块{}pass
多行语句自动(; 分隔)\ 或括号隐式续行
混用 Tab/空格无影响Python 3 报错

常见陷阱

# 陷阱1: 复制粘贴代码时缩进混乱
# 从不同来源复制的代码可能用不同的缩进风格
# 解决: 用编辑器的"转换为空格"功能,或用 ruff format

# 陷阱2: 在 REPL 中写多行代码
# REPL 中需要额外的空行来结束代码块
# >>> def greet(name):
# ...     print(f"Hello, {name}")
# ...     # 这里需要一个空行来结束
# ...

# 陷阱3: 反斜杠后面有空格
# total = 1 + 2 + \    # 错误!反斜杠后面不能有空格
#       3 + 4

# 陷阱4: if/else 在同一行(虽然合法但不推荐)
x = 10
if x > 5: print("big")  # 合法但只适合单行简单逻辑
else: print("small")     # 这样写可读性差

何时使用

  • 缩进: 永远用 4 空格,配置编辑器 Tab 键转 4 空格
  • 括号续行: 列表、字典、函数调用、长表达式——优先用括号续行
  • 反斜杠: 只在没有括号可用时(如 assert 语句)才用
  • pass: 定义接口、抽象基类、TODO 占位

2.2 变量与赋值: 名字绑定 vs 变量声明

Java/Kotlin 对比

// Java: 变量声明 = 类型 + 名字 + 内存分配
String name = "Alice";       // name 是 String 类型,不能改
int count = 42;              // count 是 int 类型,不能改
// name = 123;               // 编译错误!

// final 变量
final String GREETING = "Hello";  // 常量,编译器强制不可变

// 多重赋值不存在,只能逐个声明
int a = 1, b = 2, c = 3;    // 这是声明多个同类型变量,不是元组解包
// Kotlin: val 不可变,var 可变,但类型不变
val name = "Alice"           // 推断为 String,不可变引用
var count = 42               // 推断为 Int,可变引用
// count = "hello"           // 编译错误!类型不变

val a = 1; val b = 2; val c = 3  // 逐个声明
// val (x, y) = Pair(1, 2)       // 解构声明(有限支持)

Python 实现

# Python: = 是名字绑定(name binding),不是变量声明
# 变量只是贴在对象上的标签,可以随时贴到别的对象上

# === 基础赋值 ===
name = "Alice"     # name 绑定到 str 对象
name = 42          # name 重新绑定到 int 对象 — 完全合法!
name = [1, 2, 3]   # name 又绑定到 list 对象

# 没有"声明"这个概念,赋值即声明
# 不需要 String name = "Alice" 这样的类型声明

# === 多重赋值 ===
a = b = c = 0          # 链式赋值: a, b, c 都绑定到同一个 int 对象 0
print(a, b, c)          # 0 0 0

a = b = c = []          # 陷阱!a, b, c 绑定到同一个 list 对象
a.append(1)
print(b)                # [1] — b 也变了!因为它们指向同一个对象

# === 解包赋值 ===
x, y, z = 1, 2, 3       # 元组解包
print(x, y, z)           # 1 2 3

# 交换变量(不需要临时变量)
x, y = y, x
print(x, y)              # 2 1

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

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

# 字典解包
data = {"name": "Alice", "age": 30}
# Python 3.9+ 可以直接解包
# for key, value := data.items():  # 不行,这不是海象运算符的用法
for key, value in data.items():
    print(f"{key}: {value}")

# === 增量赋值 ===
count = 0
count += 1     # count = count + 1
count *= 2     # count = count * 2
count //= 3    # count = count // 3(整除)
count **= 2    # count = count ** 2(幂运算)

# === 常量约定 ===
# Python 没有真正的常量,全大写是约定
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
API_BASE_URL = "https://api.example.com"

# 但 Python 不会阻止你修改"常量"
MAX_RETRIES = 5  # 合法,但违反约定
# 工具(如 mypy、pylint)可以检测对全大写变量的重新赋值

核心差异

维度Java/KotlinPython
赋值语义内存分配 + 类型绑定名字绑定(标签贴到对象上)
类型约束编译器强制无(运行时检查)
重新赋值不同类型编译错误完全合法
多重赋值不支持(或有限解构)链式赋值、元组解包、扩展解包
变量交换需要临时变量a, b = b, a
常量final / val + 编译器强制全大写约定(无强制)

常见陷阱

# 陷阱1: 链式赋值共享可变对象
a = b = []
a.append(1)
print(b)  # [1] — a 和 b 是同一个对象!

# 正确: 分别创建
a = []
b = []

# 陷阱2: 以为赋值创建了副本
original = [1, 2, 3]
copy = original          # 不是副本!是同一个对象的另一个标签
copy.append(4)
print(original)          # [1, 2, 3, 4] — original 也变了!

# 正确: 用 copy() 或切片
copy = original.copy()
copy = original[:]
copy = list(original)

# 陷阱3: 解包数量不匹配
# x, y = [1, 2, 3]       # ValueError: too many values to unpack
# x, y, z = [1, 2]       # ValueError: not enough values to unpack

# 解决: 用 * 捕获剩余
x, y, *rest = [1, 2, 3, 4, 5]  # OK

# 陷阱4: += 对可变对象是原地修改
a = [1, 2, 3]
b = a
a += [4]          # list 的 += 是原地修改(等价于 a.extend([4]))
print(b)          # [1, 2, 3, 4] — b 也变了!

# 但对不可变对象是重新绑定
a = 10
b = a
a += 1            # int 的 += 创建新对象
print(b)          # 10 — b 不变

何时使用

  • 解包赋值: 函数返回多个值、遍历键值对、交换变量
  • 扩展解包 *: 只关心首尾元素、跳过中间元素
  • 链式赋值: 仅用于不可变对象(如 a = b = c = 0
  • 全大写常量: 模块级别的配置值、魔法数字

2.3 基本数据类型

Java/Kotlin 对比

// Java: 基本类型 + 包装类型,两套体系
int x = 42;                    // 基本类型
Integer boxed = 42;            // 包装类型
double pi = 3.14;
boolean flag = true;
String name = "Alice";         // 引用类型
String nullStr = null;         // null 表示"没有值"

// 类型检查
if (x instanceof Integer) { }  // 编译时类型已知,很少需要运行时检查
if (str instanceof String) { }

// 类型转换
int y = (int) 3.14;            // 强制转换,可能丢失精度
String s = String.valueOf(42); // 转字符串
// Kotlin: 统一类型系统,编译器自动装箱
val x: Int = 42               // 编译为 int 或 Integer
val pi: Double = 3.14
val flag: Boolean = true
val name: String = "Alice"
val nullStr: String? = null   // ? 表示可空

// 类型检查
if (x is Int) { }             // 智能转换
if (x is Number) { }

// 类型转换
val y: Int = 3.14.toInt()    // 显式转换
val s: String = 42.toString()

Python 实现

# === None vs null ===
# Python 用 None 表示"没有值",不是 null
name = None
print(name)              # None
print(type(None))        # <class 'NoneType'>

# None 是单例,用 is 比较(不是 ==)
x = None
print(x is None)         # True
print(x == None)         # True(但推荐用 is)

# 函数没有显式 return 时返回 None
def no_return():
    pass

result = no_return()
print(result)            # None

# === int: 无上限大整数 ===
# Python 的 int 没有上限!不像 Java 的 int (32位) 或 long (64位)
big = 2 ** 100
print(big)
# 1267650600228229401496703205376

print(type(big))         # <class 'int'> — 不会变成 BigInteger
print(big.bit_length())  # 101

# Java/Kotlin 开发者注意: Python 没有溢出!
# Java: Integer.MAX_VALUE + 1 → -2147483648(溢出)
# Python: 2147483647 + 1 → 2147483648(正常)

# 不同进制
binary = 0b1010         # 10
octal = 0o755           # 493
hex_num = 0xFF          # 255

# 数字分隔符(Python 3.6+,类似 Java 的 _)
million = 1_000_000
print(million)           # 1000000

# === float: 双精度浮点数 ===
pi = 3.14159
print(type(pi))          # <class 'float'>

# 浮点数精度问题(和 Java 一样)
print(0.1 + 0.2)        # 0.30000000000000004(不是精确的 0.3!)
print(0.1 + 0.2 == 0.3) # False!

# 正确比较浮点数
import math
print(math.isclose(0.1 + 0.2, 0.3))  # True

# 特殊值
print(float('inf'))      # inf
print(float('-inf'))     # -inf
print(float('nan'))      # nan
print(float('nan') == float('nan'))  # False! NaN 不等于任何值,包括自己
print(math.isnan(float('nan')))      # True

# === bool: int 的子类 ===
print(type(True))        # <class 'bool'>
print(isinstance(True, int))  # True!bool 是 int 的子类
print(True + 1)          # 2
print(False + 1)         # 1
print(sum([True, False, True]))  # 2

# 这意味着 bool 参与数值运算时不会报错
# Java: boolean 不能和 int 运算
# Python: True + 1 == 2(合法但容易出 bug)

# === str: 不可变字符串 ===
name = "Alice"
print(type(name))        # <class 'str'>

# === bytes: 不可变字节序列 ===
data = b"hello"
print(type(data))        # <class 'bytes'>
print(data[0])           # 104 — 这是 int,不是 char!
# data[0] = 65           # TypeError: 'bytes' object does not support item assignment

# === bytearray: 可变字节序列 ===
mutable = bytearray(b"hello")
mutable[0] = 72          # 修改第一个字节
print(mutable)           # bytearray(b'Hello')

# === 类型检查 ===
# type(): 返回确切类型(不推荐用于类型检查)
print(type(42))           # <class 'int'>
print(type(True))         # <class 'bool'> — 注意: 不是 int!

# isinstance(): 检查是否是某个类型或其子类(推荐)
print(isinstance(42, int))      # True
print(isinstance(True, int))    # True(bool 是 int 的子类)
print(isinstance(True, bool))   # True

# 检查多种类型
def process(value):
    if isinstance(value, (int, float)):
        print(f"数值: {value}")
    elif isinstance(value, str):
        print(f"字符串: {value}")
    else:
        print(f"其他类型: {type(value)}")

process(42)         # 数值: 42
process(3.14)       # 数值: 3.14
process("hello")    # 字符串: hello
process([1, 2])     # 其他类型: <class 'list'>

核心差异

维度Java/KotlinPython
整数范围int 32位, long 64位无上限
整数溢出溢出(环绕)不会溢出
nullnullNone(单例)
布尔类型独立类型int 的子类
浮点精度同 IEEE 754同 IEEE 754
类型检查instanceof / isisinstance()(推荐)
字节类型byte[]bytes(不可变)、bytearray(可变)

常见陷阱

# 陷阱1: bool 是 int 的子类
print(True == 1)       # True
print(False == 0)      # True
print([True, False] == [1, 0])  # True

# 这会导致意外的字典键冲突
d = {}
d[True] = "yes"
d[1] = "one"
print(d)               # {True: 'one'} — True 和 1 是同一个键!
print(len(d))          # 1,不是 2

# 陷阱2: None 的比较
x = None
print(x is None)       # True(推荐)
print(x == None)       # True(可以但不推荐)
# 为什么推荐 is? 因为 == 可以被重载,is 不能
# 自定义类可能让 obj == None 返回 True,但 obj is None 只有 obj 真的是 None 时才为 True

# 陷阱3: type() vs isinstance()
print(type(True) == int)          # False(type 返回 bool)
print(isinstance(True, int))      # True(isinstance 考虑继承)
# 类型检查永远用 isinstance(),除非你确实需要精确匹配类型

# 陷阱4: 浮点数比较
print(0.1 + 0.2 == 0.3)  # False!
# 用 math.isclose() 或 Decimal

何时使用

  • isinstance(): 运行时类型检查(替代 Java 的 instanceof
  • type(): 调试、日志输出(不要用于类型判断)
  • None: 函数默认返回值、可选参数的默认值
  • bytes: 网络传输、文件读写(二进制模式)
  • math.isclose(): 比较浮点数

2.4 字符串

Java/Kotlin 对比

// Java: 字符串用双引号,字符用单引号
String name = "Alice";
char ch = 'A';

// 字符串拼接
String greeting = "Hello, " + name + "!";  // + 拼接

// 字符串模板 (Java 15+)
String msg = STR."Hello, \{name}!";

// 字符串方法
name.length();        // 5
name.toUpperCase();   // "ALICE"
name.substring(0, 3); // "Ali"
name.split(",");      // String[]
String.join("-", parts);  // 静态方法

// 多行字符串 (Java 15+)
String text = """
    Hello
    World
    """;
// Kotlin: 字符串用双引号,字符用单引号
val name = "Alice"
val ch = 'A'

// 字符串插值
val greeting = "Hello, $name!"         // 变量
val info = "Age: ${person.age}"        // 表达式

// 多行字符串
val text = """
    Hello
    World
    """.trimIndent()

// 原始字符串(无转义)
val regex = Regex("\\d+")              // 需要双反斜杠
val raw = """\d+"""                    // 三引号自动原始

Python 实现

# === 字符串创建 ===

# 单引号、双引号完全等价
s1 = 'hello'
s2 = "hello"
print(s1 == s2)  # True

# 为什么有两种?方便嵌套
quote = 'He said "hello"'
quote2 = "He said 'hello'"

# 三引号: 多行字符串
poem = """
Roses are red,
Violets are blue,
Python is awesome,
And so are you.
"""
print(poem)
# 注意: 三引号字符串会包含换行符和前导空格

# 去除前导空格
import textwrap
poem = textwrap.dedent("""\
    Roses are red,
    Violets are blue,
    Python is awesome.
""")
# \ 避免开头的换行

# === f-string (Python 3.6+) ===
name = "Alice"
age = 30

# 基础插值
print(f"Hello, {name}!")           # Hello, Alice!
print(f"Age: {age}")               # Age: 30

# 表达式
print(f"Next year: {age + 1}")     # Next year: 31
print(f"Name upper: {name.upper()}")  # Name upper: ALICE

# 格式化
pi = 3.14159
print(f"Pi: {pi:.2f}")             # Pi: 3.14
print(f"Pi: {pi:.4f}")             # Pi: 3.1416
print(f"{'hello':>20}")            # '               hello'(右对齐,宽20)
print(f"{'hello':<20}")            # 'hello                '(左对齐)
print(f"{'hello':^20}")            # '       hello        '(居中)
print(f"{1000000:,}")              # 1,000,000(千位分隔符)
print(f"{255:#x}")                 # 0xff(十六进制)
print(f"{255:#b}")                 # 0b11111111(二进制)

# 调试模式 (Python 3.8+)
x = 42
y = 3.14
print(f"{x = }")       # x = 42
print(f"{y = :.2f}")   # y = 3.14
print(f"{x * 2 = }")   # x * 2 = 84

# f-string 中不能使用 \ 转义(Python 3.12+ 放宽了限制)
# print(f"{'hello\nworld'}")  # Python 3.11 及以下: SyntaxError
# 解决: 先赋值给变量
newline = '\n'
print(f"{'hello' + newline + 'world'}")  # OK

# === 字符串方法 ===
s = "  Hello, World!  "

# 去除空白
print(s.strip())       # "Hello, World!"(两端)
print(s.lstrip())      # "Hello, World!  "(左端)
print(s.rstrip())      # "  Hello, World!"(右端)

# 大小写
print("hello".upper())       # "HELLO"
print("HELLO".lower())       # "hello"
print("hello world".title()) # "Hello World"
print("hello world".capitalize())  # "Hello world"

# 查找
print("hello".find("ll"))     # 2(索引,找不到返回 -1)
print("hello".index("ll"))    # 2(索引,找不到抛 ValueError)
print("hello".startswith("he"))  # True
print("hello".endswith("lo"))    # True
print("hello".count("l"))        # 2

# 替换
print("hello world".replace("world", "Python"))  # "hello Python"
print("aaa".replace("a", "b", 2))  # "bba"(最多替换2次)

# 分割和连接
print("a,b,c".split(","))     # ['a', 'b', 'c']
print("a  b  c".split())      # ['a', 'b', 'c'](默认按空白分割,自动去空)
print("a\nb\nc".splitlines()) # ['a', 'b', 'c']

# join: 用指定字符串连接列表(和 Java 的 String.join 类似)
words = ["Hello", "World"]
print(" ".join(words))        # "Hello World"
print("-".join(words))        # "Hello-World"
print("".join(words))         # "HelloWorld"

# 判断
print("123".isdigit())        # True
print("abc".isalpha())        # True
print("abc123".isalnum())     # True
print("   ".isspace())        # True
print("Hello".isupper())      # False
print("HELLO".isupper())      # True

# 对齐
print("hello".center(20, "="))  # "=======hello========"
print("hello".ljust(20, "-"))   # "hello---------------"
print("hello".rjust(20, "-"))   # "---------------hello"

# === 原始字符串 ===
# r 前缀: 反斜杠不转义(正则表达式常用)
import re
pattern = r"\d+"       # 匹配数字,不需要双反斜杠
print(re.findall(pattern, "abc123def456"))  # ['123', '456']

path = r"C:\Users\name\file.txt"  # Windows 路径
print(path)  # C:\Users\name\file.txt(\n 不会被解释为换行)

# === bytes vs str ===
# str: Unicode 文本
# bytes: 二进制数据

text = "hello"
encoded = text.encode("utf-8")    # str → bytes
print(encoded)                     # b'hello'
print(type(encoded))               # <class 'bytes'>

decoded = encoded.decode("utf-8")  # bytes → str
print(decoded)                     # 'hello'

# bytes 只能包含 ASCII 字符
# b"你好"  # SyntaxError: bytes can only contain ASCII literal characters
b_data = "你好".encode("utf-8")    # b'\xe4\xbd\xa0\xe5\xa5\xbd'

# format 方法(旧式,不推荐,了解即可)
old_style = "Hello, {}!".format("Alice")
print(old_style)  # Hello, Alice!

# 更旧的风格(不推荐)
very_old = "Hello, %s! Age: %d" % ("Alice", 30)
print(very_old)  # Hello, Alice! Age: 30

核心差异

维度JavaKotlinPython
字符串引号仅双引号仅双引号单/双/三引号均可
字符类型char(单引号)Char(单引号)无字符类型,长度为1的字符串
字符串插值STR."..." (15+)"...$var..."f"...{expr}"
多行字符串"""...""" (15+)"""...""""""..."""
原始字符串r"..."
null 字符串nullString?None(不是字符串)
字符串拼接+(编译器优化)+(StringBuilder)+f""join
字符串不可变

常见陷阱

# 陷阱1: 字符串拼接的性能
# Java 编译器自动用 StringBuilder,Python 不会
# 慢: 循环中用 + 拼接
parts = []
for i in range(1000):
    parts.append(str(i))    # 先收集
result = "".join(parts)     # 一次 join(快)

# 慢:
# result = ""
# for i in range(1000):
#     result += str(i)      # 每次创建新字符串

# 陷阱2: f-string 和 format 的区别
# f-string 在定义时求值,format 在调用时求值
name = "Alice"
greeting = f"Hello, {name}"
name = "Bob"
print(greeting)  # Hello, Alice(f-string 在创建时已经求值)

# 陷阱3: bytes 和 str 不能混用
# text = "hello" + b"world"  # TypeError!
# 必须显式编码/解码
text = "hello" + b"world".decode()  # "helloworld"
data = b"hello" + "world".encode()  # b"helloworld"

# 陷阱4: 字符串的 in 操作
print("ell" in "hello")    # True(子串检查)
# Java: "hello".contains("ell")
# Python: "ell" in "hello"(更直观)

何时使用

  • f-string: 所有字符串插值场景(Python 3.6+,推荐)
  • 三引号: 多行文本、SQL 查询、正则表达式
  • r 前缀: 正则表达式、Windows 路径
  • join(): 拼接多个字符串(性能最优)
  • encode()/decode(): 网络传输、文件 I/O(二进制模式)

2.5 运算符

Java/Kotlin 对比

// Java 运算符
int a = 7 / 2;       // 3(整数除法,截断)
double b = 7.0 / 2;  // 3.5
int c = 7 % 2;       // 1(取模)
int d = (int) Math.pow(2, 10);  // 1024(幂运算需要 Math.pow)

// 比较
String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2;            // false(引用比较)
s1.equals(s2);       // true(值比较)

// 逻辑运算
boolean x = true && false;  // false
boolean y = true || false;  // true
boolean z = !true;          // false

// 位运算
int bits = 0b1010 & 0b1100;  // 0b1000 (8)
// Kotlin 运算符
val a = 7 / 2        // 3(整数除法)
val b = 7.0 / 2      // 3.5
val c = 7 % 2        // 1
val d = 2.0.pow(10)  // 1024.0(中缀函数)

// 比较(== 已经是值比较,不需要 equals)
val s1 = "hello"
val s2 = "hello"
s1 == s2    // true(值比较)
s1 === s2   // true(引用比较)

// 范围
val range = 1..10
1 in range  // true

Python 实现

# === 算术运算符 ===

# / 总是返回 float(和 Java 不同!)
print(7 / 2)       # 3.5(不是 3!)
print(7.0 / 2)     # 3.5

# // 整除(floor division)
print(7 // 2)      # 3
print(-7 // 2)     # -4(向下取整!不是 -3)
# Java: -7 / 2 = -3(向零取整)
# Python: -7 // 2 = -4(向下取整/地板除)

# % 取模(和整除配合)
print(7 % 2)       # 1
print(-7 % 2)      # 1(不是 -1!因为 -7 = -4 * 2 + 1)
# Java: -7 % 2 = -1
# Python: -7 % 2 = 1(结果符号和除数相同)

# ** 幂运算(不需要 Math.pow)
print(2 ** 10)     # 1024
print(2 ** 0.5)    # 1.4142135623730951(平方根)
print(2 ** 100)    # 1267650600228229401496703205376(大整数)

# @ 矩阵乘法(需要 numpy)
# import numpy as np
# a = np.array([[1, 2], [3, 4]])
# b = np.array([[5, 6], [7, 8]])
# print(a @ b)  # 矩阵乘法

# === 比较运算符 ===

# == 值相等
print(1 == 1)           # True
print("hello" == "hello")  # True
print([1, 2] == [1, 2])    # True(列表值比较,Java 的 ArrayList.equals 也这样)
print([1, 2] == [1, 3])    # False

# is 身份比较(是否是同一个对象)
a = [1, 2]
b = [1, 2]
print(a == b)   # True(值相等)
print(a is b)   # False(不是同一个对象)

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

# 小整数缓存(-5 到 256)
x = 256
y = 256
print(x is y)   # True(缓存)
x = 257
y = 257
print(x is y)   # False(不缓存)

# None 比较
print(None is None)  # True
# 用 is 判断 None,不用 ==

# 链式比较(Python 独有,Java/Kotlin 没有)
x = 5
print(1 < x < 10)    # True(等价于 1 < x and x < 10)
print(1 < x > 3)     # True(等价于 1 < x and x > 3)
print(0 < x != 5)    # True(等价于 0 < x and x != 5)

# === 逻辑运算符 ===
# Python 用 and, or, not(不是 &&, ||, !)

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

# 关键差异: and/or 返回的是操作数的值,不是 True/False
print(1 and 2)          # 2(返回最后一个为真的值)
print(0 and 2)          # 0(第一个为假,直接返回)
print(1 or 2)           # 1(第一个为真,直接返回)
print(0 or 2)           # 2(第一个为假,返回第二个)
print("" or "default")  # "default"(空字符串是假值)
print(None or "fallback")  # "fallback"

# 这个特性常用于设置默认值
name = None
display_name = name or "Anonymous"  # "Anonymous"

# === 成员运算符 ===
print(3 in [1, 2, 3])          # True
print(4 not in [1, 2, 3])      # True
print("key" in {"key": "val"}) # True(检查键)
print("hello" in "hello world") # True(子串检查)

# === 身份运算符 ===
# is / is not: 比较对象身份(内存地址)
a = [1, 2]
b = [1, 2]
print(a is not b)  # True

# === 海象运算符 := (Python 3.8+) ===
# 在表达式中同时赋值和返回值

# 场景1: while 循环中
import re
text = "abc123def456"
pattern = re.compile(r"\d+")
match = pattern.search(text)
while match:
    print(match.group())   # 123, 456
    match = pattern.search(text, match.end())

# 用海象运算符简化
text = "abc123def456"
pos = 0
while (match := pattern.search(text, pos)):
    print(match.group())   # 123, 456
    pos = match.end()

# 场景2: 列表推导式中复用计算结果
data = [1, 2, 3, 4, 5, 6]
# 不用海象: 需要计算两次或用普通循环
results = [y for x in data if (y := x ** 2) > 10]
print(results)  # [16, 25, 36]

# 场景3: 条件表达式中
if (n := len(data)) > 5:
    print(f"Too many items: {n}")

# === 位运算 ===
print(0b1010 & 0b1100)   # 8 (0b1000)  — 与
print(0b1010 | 0b1100)   # 14 (0b1110) — 或
print(0b1010 ^ 0b1100)   # 6 (0b0110)  — 异或
print(~0b1010)            # -11          — 取反
print(0b1010 << 2)        # 40 (0b101000) — 左移
print(0b1010 >> 2)        # 2 (0b10)       — 右移

核心差异

运算符Java/KotlinPython差异
/整数除法(截断)浮点除法7/2 → Java: 3, Python: 3.5
//不存在整除(向下取整)-7//2 → Java: -3, Python: -4
%向零取模向下取模-7%2 → Java: -1, Python: 1
**Math.pow()内置幂运算Python 更直观
==引用/值取决于类型始终值比较Python 的 == 像 Kotlin 的 ==
is不存在身份比较Python 的 is 像 Kotlin 的 ===
&& / `/!`逻辑运算and / or / notPython 返回操作数值,不是布尔值
:=不存在海象运算符Python 3.8+ 独有
链式比较不支持1 < x < 10Python 独有

常见陷阱

# 陷阱1: / 和 // 混淆
print(7 / 2)    # 3.5(不是 3!)
print(7 // 2)   # 3
# 从 Java 转过来的开发者容易忘记 Python 的 / 总是返回 float

# 陷阱2: 负数整除和取模
print(-7 // 2)  # -4(不是 -3!向下取整)
print(-7 % 2)   # 1(不是 -1!)
# 记住: a == (a // b) * b + (a % b) 恒成立
# -7 == (-4) * 2 + 1 → -7 == -8 + 1 → -7 == -7 ✓

# 陷阱3: and/or 返回值不是布尔值
print(0 or "hello")   # "hello"(不是 True)
print(1 and 0)        # 0(不是 False)
# 这在 if 条件中没问题(因为 0 和 "" 都是假值)
# 但如果用于赋值,要注意类型

# 陷阱4: is 用于值比较
x = 257
y = 257
print(x == y)   # True(值相等)
print(x is y)   # False(不是同一个对象!)
# 永远不要用 is 比较数值和字符串,用 ==

# 陷阱5: 链式比较的优先级
# print(1 < 2 < 3 == 3)  # True(等价于 1 < 2 and 2 < 3 and 3 == 3)
# print(1 < 2 > 1)       # True(等价于 1 < 2 and 2 > 1)

何时使用

  • //: 需要整数结果时(分页、索引计算)
  • **: 幂运算(比 pow() 更直观)
  • is: 仅用于 NoneTrueFalse 的比较
  • ==: 所有值比较
  • and/or: 逻辑运算 + 短路默认值
  • :=: 在表达式内需要赋值时(while 循环、推导式)
  • in: 成员检查(列表、字典键、子串)

2.6 控制流

Java/Kotlin 对比

// Java: if/else if/else
if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else {
    grade = "C";
}

// switch (Java 17+ 模式匹配)
switch (obj) {
    case Integer i -> System.out.println("Integer: " + i);
    case String s -> System.out.println("String: " + s);
    case null -> System.out.println("null");
    default -> System.out.println("Other");
}

// for 循环
for (int i = 0; i < 10; i++) { }
for (String item : list) { }

// while
while (condition) { }
do { } while (condition);
// Kotlin: if 是表达式(有返回值)
val grade = if (score >= 90) "A" else if (score >= 80) "B" else "C"

// when(比 Java switch 更强大)
when (obj) {
    is Int -> println("Integer: $obj")
    is String -> println("String: $obj")
    in 1..10 -> println("In range")
    else -> println("Other")
}

// for 循环
for (i in 0 until 10) { }
for (item in list) { }

// while
while (condition) { }
do { } while (condition)

Python 实现

# === if/elif/else ===
score = 85

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
else:
    grade = "F"

print(f"Grade: {grade}")  # Grade: B

# Python 的 if 也是表达式(Python 3.8+ 海象运算符,或三元表达式)
grade = "A" if score >= 90 else "B" if score >= 80 else "C"
# 等价于 Java 的: score >= 90 ? "A" : (score >= 80 ? "B" : "C")

# 注意: Python 没有 switch...case(直到 3.10 才有 match/case)

# === for 循环(基于迭代器协议)===
# Python 的 for 不是 C 风格的 for(i=0; i<n; i++)
# 而是类似 Java 的 for-each / Kotlin 的 for-in

# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

# 带索引遍历(类似 Java 的普通 for 循环)
for i, fruit in enumerate(fruits):
    print(f"{i}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry

# 遍历 range(类似 Java 的 for(int i=0; i<10; i++))
for i in range(10):
    print(i, end=" ")  # 0 1 2 3 4 5 6 7 8 9

for i in range(2, 10, 3):  # start, stop, step
    print(i, end=" ")       # 2 5 8

# 遍历字典
user = {"name": "Alice", "age": 30}
for key in user:              # 遍历键
    print(key)

for key, value in user.items():  # 遍历键值对
    print(f"{key}: {value}")

# 同时遍历多个序列
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 92]
for name, score in zip(names, scores):
    print(f"{name}: {score}")
# Alice: 95
# Bob: 87
# Charlie: 92

# 反向遍历
for i in reversed(range(5)):
    print(i, end=" ")  # 4 3 2 1 0

# 排序后遍历
for fruit in sorted(fruits, key=len):
    print(fruit)  # apple, banana, cherry(按长度排序)

# === while 循环 ===
count = 0
while count < 5:
    print(count)
    count += 1

# Python 没有 do-while
# 用 while True + break 模拟
while True:
    user_input = input("Enter 'quit' to exit: ")
    if user_input == "quit":
        break
    print(f"You entered: {user_input}")

# === break, continue, pass ===
for i in range(10):
    if i == 3:
        continue    # 跳过本次,继续下一次
    if i == 7:
        break       # 退出循环
    print(i, end=" ")  # 0 1 2 4 5 6

# pass: 空操作占位
for i in range(5):
    pass  # 什么都不做

# === for/while 的 else 子句(Python 独有!)===
# 循环正常结束时执行 else,被 break 打断时不执行

# 查找质数
for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            print(f"{n} = {x} * {n // x}")
            break
    else:
        # 内层 for 没有被 break,说明 n 是质数
        print(f"{n} is prime")

# === match/case (Python 3.10+) 结构模式匹配 ===
# 类似 Java switch (17+) / Kotlin when,但更强大

def http_status(code):
    match code:
        case 200:
            return "OK"
        case 404:
            return "Not Found"
        case 500:
            return "Internal Server Error"
        case _:
            return "Unknown"

print(http_status(200))  # OK
print(http_status(404))  # Not Found

# 字面量模式
def describe(value):
    match value:
        case 0:
            return "zero"
        case 1 | 2 | 3:         # 多个值(类似 Kotlin 的 case 1, 2, 3 ->)
            return "small"
        case _:
            return "other"

# 捕获模式(解构)
def process_point(point):
    match point:
        case (0, 0):
            return "origin"
        case (x, 0):
            return f"on x-axis at {x}"
        case (0, y):
            return f"on y-axis at {y}"
        case (x, y):
            return f"at ({x}, {y})"

print(process_point((3, 0)))   # on x-axis at 3
print(process_point((1, 2)))   # at (1, 2)

# 序列模式
def process_sequence(seq):
    match seq:
        case []:
            return "empty"
        case [first]:
            return f"single: {first}"
        case [first, second]:
            return f"pair: {first}, {second}"
        case [first, *rest]:    # 捕获剩余元素
            return f"first: {first}, rest: {rest}"

print(process_sequence([1, 2, 3, 4]))  # first: 1, rest: [2, 3, 4]

# 映射模式
def process_config(config):
    match config:
        case {"role": "admin", **rest}:
            return f"Admin with extra: {rest}"
        case {"role": role, "name": name}:
            return f"{role}: {name}"
        case {}:
            return "empty config"

print(process_config({"role": "admin", "name": "Alice", "level": 5}))
# Admin with extra: {'name': 'Alice', 'level': 5}

# guard 子句(if 条件)
def classify(number):
    match number:
        case n if n < 0:
            return "negative"
        case 0:
            return "zero"
        case n if n % 2 == 0:
            return "even positive"
        case _:
            return "odd positive"

print(classify(-5))   # negative
print(classify(4))    # even positive
print(classify(7))    # odd positive

# 嵌套模式
def process_data(data):
    match data:
        case {"status": "ok", "data": [first, *rest]}:
            return f"OK, first item: {first}, total: {1 + len(rest)}"
        case {"status": "error", "message": msg}:
            return f"Error: {msg}"
        case _:
            return "Unknown format"

print(process_data({"status": "ok", "data": [1, 2, 3]}))
# OK, first item: 1, total: 3

核心差异

维度Java/KotlinPython
条件分支if/else if/elseif/elif/else
三元表达式cond ? a : ba if cond else b
switch/whenswitch/whenmatch/case (3.10+)
for 循环C 风格 + for-each仅 for-each(基于迭代器)
循环变量仅在循环内可见循环后仍可见(泄漏!)
循环 else不存在for...else / while...else
do-while没有(用 while True + break
模式匹配Java 17+ / Kotlin whenPython 3.10+ match/case

常见陷阱

# 陷阱1: for 循环变量泄漏
for i in range(5):
    pass
print(i)  # 4 — i 在循环外仍然可见!
# Java: for(int i=0; i<5; i++) {} — i 在循环外不可见
# Python: 循环变量不会销毁,循环结束后保留最后一个值

# 陷阱2: 在循环中修改列表
items = [1, 2, 3, 4, 5]
for item in items:
    if item == 3:
        items.remove(item)  # 修改正在遍历的列表!
print(items)  # [1, 2, 4, 5] — 看起来对,但实际跳过了 4!
# 因为 remove 改变了索引

# 正确: 遍历副本或用列表推导式
items = [1, 2, 3, 4, 5]
items = [item for item in items if item != 3]
# 或
items = [1, 2, 3, 4, 5]
for item in items[:]:  # 遍历副本
    if item == 3:
        items.remove(item)

# 陷阱3: for...else 的语义
def find_item(items, target):
    for item in items:
        if item == target:
            print(f"Found: {item}")
            break
    else:
        # 只有 for 没有被 break 时才执行
        print("Not found")

find_item([1, 2, 3], 2)  # Found: 2
find_item([1, 2, 3], 5)  # Not found

# 陷阱4: match/case 是结构模式匹配,不是 switch
# Java switch: case 值匹配
# Python match: 模式匹配(结构、类型、guard)
# match 不会 fall-through(不需要 break)
# match 的 case 是模式,不只是值

何时使用

  • if/elif/else: 条件分支(简单逻辑)
  • 三元表达式: 简单的二选一赋值
  • match/case: 复杂的多分支匹配(3.10+),尤其是结构化数据
  • for: 遍历集合、range()enumerate()zip()
  • while: 循环次数不确定时
  • for...else: 查找是否找到(替代"找到标志"变量)
  • break: 提前退出循环
  • continue: 跳过当前迭代

2.7 推导式

Java/Kotlin 对比

// Java: Stream API
List<Integer> nums = List.of(1, 2, 3, 4, 5);

// map + filter
List<Integer> evens = nums.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .collect(Collectors.toList());
// [4, 16]

// Java Stream 需要大量样板代码
// Kotlin: 函数式链式调用
val nums = listOf(1, 2, 3, 4, 5)

val evens = nums
    .filter { it % 2 == 0 }
    .map { it * it }
// [4, 16]

// Kotlin 更简洁,但仍不如 Python 推导式直观

Python 实现

# === 列表推导式 (List Comprehension) ===
# 语法: [表达式 for 变量 in 可迭代对象 if 条件]

# 基础
squares = [x ** 2 for x in range(10)]
print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 带条件
evens = [x for x in range(20) if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 带转换
labels = [f"item_{i}" for i in range(5)]
print(labels)  # ['item_0', 'item_1', 'item_2', 'item_3', 'item_4']

# 嵌套循环(展平二维列表)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

# 嵌套循环带条件
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
print(pairs)  # [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]

# === 字典推导式 (Dict Comprehension) ===
# 语法: {键表达式: 值表达式 for 变量 in 可迭代对象 if 条件}

# 基础
squares_dict = {x: x ** 2 for x in range(6)}
print(squares_dict)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 键值交换
original = {"a": 1, "b": 2, "c": 3}
swapped = {v: k for k, v in original.items()}
print(swapped)  # {1: 'a', 2: 'b', 3: 'c'}

# 过滤
scores = {"Alice": 95, "Bob": 72, "Charlie": 88, "Diana": 60}
passed = {name: score for name, score in scores.items() if score >= 70}
print(passed)  # {'Alice': 95, 'Bob': 72, 'Charlie': 88}

# === 集合推导式 (Set Comprehension) ===
# 语法: {表达式 for 变量 in 可迭代对象 if 条件}

# 去重
words = ["hello", "world", "hello", "python", "world"]
unique_lengths = {len(word) for word in words}
print(unique_lengths)  # {5, 6}

# 数学运算
nums = [1, 2, 3, 4, 5, 5, 4, 3]
unique_squares = {x ** 2 for x in nums}
print(unique_squares)  # {1, 4, 9, 16, 25}

# === 生成器表达式 (Generator Expression) ===
# 语法: (表达式 for 变量 in 可迭代对象 if 条件)
# 和列表推导式的区别: 惰性求值,不立即创建列表

# 列表推导式: 立即计算,占用内存
squares_list = [x ** 2 for x in range(1_000_000)]
# 内存: ~8MB(存储所有结果)

# 生成器表达式: 惰性计算,几乎不占内存
squares_gen = (x ** 2 for x in range(1_000_000))
# 内存: ~100 bytes(只存储生成规则)

# 生成器只能消费一次
gen = (x ** 2 for x in range(5))
print(list(gen))  # [0, 1, 4, 9, 16]
print(list(gen))  # [](已耗尽!)

# 生成器表达式常用场景
# 1. 作为函数参数(不需要额外括号)
total = sum(x ** 2 for x in range(100))
print(total)  # 328350

# 2. 传给 max/min/any/all
max_val = max(len(word) for word in words)
print(max_val)  # 6

# 3. 传给 join
result = ",".join(str(x) for x in range(10))
print(result)  # 0,1,2,3,4,5,6,7,8,9

# === 嵌套推导式 ===
# 矩阵转置
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
transposed = [[row[i] for row in matrix] for i in range(3)]
print(transposed)
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

# 分组
names_by_length = {}
for name in ["Alice", "Bob", "Charlie", "Diana", "Eve"]:
    names_by_length.setdefault(len(name), []).append(name)
# 用推导式 + defaultdict 更简洁
from collections import defaultdict
names_by_length = defaultdict(list)
for name in ["Alice", "Bob", "Charlie", "Diana", "Eve"]:
    names_by_length[len(name)].append(name)
print(dict(names_by_length))
# {5: ['Alice', 'Diana'], 3: ['Bob', 'Eve'], 7: ['Charlie']}

核心差异

维度Java StreamKotlin 链式调用Python 推导式
语法stream().filter().map().collect().filter{}.map{}[expr for x in iter if cond]
惰性求值是(Stream)否(List)列表推导式否,生成器表达式是
可读性嵌套时差中等嵌套时差
性能有开销(Stream 管道)较好通常比 for 循环快
嵌套循环flatMapflatMap直接写多个 for

常见陷阱

# 陷阱1: 推导式中的变量泄漏(Python 3 修复)
# Python 2: 推导式中的变量会泄漏到外部作用域
# Python 3: 推导式有自己的作用域,不会泄漏
result = [x for x in range(5)]
# print(x)  # NameError: name 'x' is not defined(Python 3)

# 陷阱2: 嵌套推导式超过2层就很难读
# 差: 3层嵌套
# result = [[(x, y, z) for z in range(3)] for y in range(3) for x in range(3)]
# 超过2层请用普通 for 循环

# 陷阱3: 在推导式中使用有副作用的函数
# 差: 推导式用于副作用(打印、写入文件等)
# [print(x) for x in range(5)]  # 能运行但不 Pythonic
# 正确: 用普通 for 循环处理副作用
for x in range(5):
    print(x)

# 陷阱4: 生成器表达式只能消费一次
gen = (x for x in range(3))
print(list(gen))  # [0, 1, 2]
print(list(gen))  # [](已耗尽)
# 如果需要多次使用,用列表推导式

# 陷阱5: 推导式中的 if 位置
# if 在 for 后面: 过滤条件
evens = [x for x in range(10) if x % 2 == 0]
# if 在表达式前面: 三元表达式
labels = ["even" if x % 2 == 0 else "odd" for x in range(5)]
print(labels)  # ['even', 'odd', 'even', 'odd', 'even']

何时使用

  • 列表推导式: 需要立即使用结果列表时
  • 字典推导式: 从一个集合构建映射时
  • 集合推导式: 需要去重时
  • 生成器表达式: 数据量大、只需遍历一次时(传给 sum()max()any() 等函数)
  • 普通 for 循环: 逻辑复杂、有副作用、超过2层嵌套时

2.8 函数定义

Java/Kotlin 对比

// Java: 方法必须在类中,需要声明类型
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    // 重载(同名不同参数)
    public double add(double a, double b) {
        return a + b;
    }

    // 可变参数
    public int sum(int... nums) {
        int total = 0;
        for (int n : nums) total += n;
        return total;
    }

    // Lambda
    Runnable r = () -> System.out.println("hello");
    Function<Integer, Integer> doubleIt = x -> x * 2;
}
// Kotlin: 函数可以独立于类(顶层函数)
fun add(a: Int, b: Int): Int = a + b

// 默认参数(Java 不支持,需要重载)
fun greet(name: String = "World"): String = "Hello, $name"

// 命名参数
greet(name = "Alice")

// 可变参数
fun sum(vararg nums: Int): Int = nums.sum()

// Lambda
val doubleIt: (Int) -> Int = { x -> x * 2 }
val square: (Int) -> Int = { it * it }  // it 是隐式参数名

Python 实现

# === def 基础 ===
def add(a, b):
    """两数相加(docstring)"""
    return a + b

result = add(3, 5)  # 8

# 函数是一等公民: 可以赋值给变量、存入列表、作为参数传递
my_add = add
print(my_add(3, 5))  # 8

# === 默认参数 ===
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Alice"))          # Hello, Alice!
print(greet("Alice", "Hi"))    # Hi, Alice!

# 命名参数(关键字参数)
print(greet(name="Bob", greeting="Hey"))  # Hey, Bob!
print(greet(greeting="Hey", name="Bob"))  # Hey, Bob!(顺序无关)

# === 可变默认参数陷阱!!!===
# 这是 Python 最经典的陷阱之一

# 错误: 可变对象作为默认参数
def bad_append(item, target=[]):
    """每次调用共享同一个 list!"""
    target.append(item)
    return target

print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2] — 不是 [2]!
print(bad_append(3))  # [1, 2, 3] — 越来越多!

# 原因: 默认参数在函数定义时求值,不是每次调用时求值
# target=[] 在定义时创建了一个 list 对象,所有调用共享它

# 正确: 用 None 作为默认值
def good_append(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

print(good_append(1))  # [1]
print(good_append(2))  # [2] — 每次都是新列表
print(good_append(3))  # [3]

# 同样的陷阱适用于 dict、set 等可变对象
def bad_config(options={}):
    options.setdefault("debug", False)
    return options

# 正确:
def good_config(options=None):
    if options is None:
        options = {}
    options.setdefault("debug", False)
    return options

# === *args 和 **kwargs ===
# *args: 接收任意数量的位置参数(打包为 tuple)
# **kwargs: 接收任意数量的关键字参数(打包为 dict)

def variadic(*args, **kwargs):
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

variadic(1, 2, 3, name="Alice", age=30)
# args: (1, 2, 3)
# kwargs: {'name': 'Alice', 'age': 30}

# 实际应用: 日志函数
def log(level, message, **extra):
    print(f"[{level}] {message}")
    for key, value in extra.items():
        print(f"  {key}: {value}")

log("INFO", "User logged in", user_id=42, ip="192.168.1.1")
# [INFO] User logged in
#   user_id: 42
#   ip: 192.168.1.1

# *args 的实际应用: 求和
def my_sum(*numbers):
    total = 0
    for n in numbers:
        total += n
    return total

print(my_sum(1, 2, 3, 4, 5))  # 15

# 解包参数
def add(a, b, c):
    return a + b + c

args = (1, 2, 3)
print(add(*args))  # 6(解包 tuple)

kwargs = {"a": 1, "b": 2, "c": 3}
print(add(**kwargs))  # 6(解包 dict)

# === 仅关键字参数 (Keyword-Only Arguments) ===
# * 之后的参数必须用关键字传递
def safe_divide(numerator, denominator, *, round_to=2):
    """denominator 和 round_to 必须用关键字传递"""
    result = numerator / denominator
    return round(result, round_to)

print(safe_divide(10, 3, round_to=4))  # 3.3333
# print(safe_divide(10, 3, 4))  # TypeError: safe_divide() takes 2 positional arguments

# === lambda 表达式 ===
# 语法: lambda 参数: 表达式
# 只能包含一个表达式,不能包含语句

square = lambda x: x ** 2
print(square(5))  # 25

add = lambda a, b: a + b
print(add(3, 4))  # 7

# 常用场景: 排序
pairs = [(1, "banana"), (3, "apple"), (2, "cherry")]
pairs.sort(key=lambda x: x[1])  # 按第二个元素排序
print(pairs)  # [(3, 'apple'), (1, 'banana'), (2, 'cherry')]

# 常用场景: 作为高阶函数参数
numbers = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, numbers))
print(doubled)  # [2, 4, 6, 8, 10]

evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4]

# 但推荐用推导式替代 map/filter + lambda
doubled = [x * 2 for x in numbers]       # 更 Pythonic
evens = [x for x in numbers if x % 2 == 0]  # 更 Pythonic

# === 类型注解(基础)===
# Python 3.5+ 支持类型注解,但运行时不强制

def greet(name: str) -> str:
    return f"Hello, {name}"

def add(a: int, b: int) -> int:
    return a + b

# 类型注解不影响运行时
add("hello", "world")  # "helloworld"(类型注解只是提示)

# 可选类型
from typing import Optional

def find_user(user_id: int) -> Optional[str]:
    if user_id == 1:
        return "Alice"
    return None

# Python 3.10+ 联合类型语法
def process(value: int | str) -> str:
    return str(value)

# === 函数作为一等公民 ===
# 函数可以赋值、传参、返回、存入数据结构

# 赋值
say_hello = greet

# 存入列表
operations = [lambda x: x + 1, lambda x: x * 2, lambda x: x ** 2]
for op in operations:
    print(op(5))  # 6, 10, 25

# 作为参数
def apply(func, value):
    return func(value)

print(apply(lambda x: x ** 2, 5))  # 25

# 作为返回值
def make_multiplier(factor):
    def multiplier(x):
        return x * factor
    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))  # 10
print(triple(5))  # 15

# 函数装饰器(高阶函数的典型应用)
def timer(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer  # 等价于 slow_function = timer(slow_function)
def slow_function():
    import time
    time.sleep(0.1)
    return "done"

print(slow_function())  # slow_function took 0.1001s \n done

核心差异

维度JavaKotlinPython
函数位置必须在类中可以在类外(顶层函数)可以在模块顶层
参数类型必须声明可推断可选(类型注解)
返回类型必须声明可推断可选
默认参数不支持(需重载)支持支持
可变参数Type... argsvararg args*args, **kwargs
命名参数不支持支持支持(关键字参数)
函数重载支持支持不支持(用默认参数或 *args
Lambda(x) -> expr{ x -> expr }lambda x: expr
类型注解编译时强制编译时强制运行时忽略(工具检查)

常见陷阱

# 陷阱1: 可变默认参数(前面已详述)
# 永远不要用 []、{}、set() 作为默认参数
# 用 None 代替

# 陷阱2: lambda 的闭包陷阱
funcs = [lambda: i for i in range(5)]
print([f() for f in funcs])  # [4, 4, 4, 4, 4] — 全是 4!
# 原因: lambda 捕获的是变量 i 的引用,不是值
# 循环结束后 i = 4,所有 lambda 都返回 4

# 正确: 用默认参数捕获值
funcs = [lambda i=i: i for i in range(5)]
print([f() for f in funcs])  # [0, 1, 2, 3, 4]

# 陷阱3: *args 和 **kwargs 的命名
# *args 和 **kwargs 只是约定,不是关键字
# 你可以写 *params 和 **options,但不要这么做(违反惯例)

# 陷阱4: 默认参数的求值时机
import time
def report(timestamp=time.time()):
    print(f"Generated at: {timestamp}")

report()  # Generated at: 1712345678.123
time.sleep(1)
report()  # Generated at: 1712345678.123 — 同一个时间戳!
# 默认参数在函数定义时求值,不是每次调用时

# 陷阱5: lambda 只能是单表达式
# lambda x: x if x > 0 else -x  # OK(三元表达式是单表达式)
# lambda x:   # 语法错误!不能包含语句
#     if x > 0:
#         return x
#     return -x
# 多行逻辑用 def

何时使用

  • def: 所有需要多行逻辑的函数
  • 默认参数: 减少函数重载,提供便捷调用
  • *args: 函数需要接受任意数量的位置参数
  • **kwargs: 函数需要接受任意数量的关键字参数(配置选项、扩展参数)
  • 仅关键字参数 *: 防止位置参数误传,提高可读性
  • lambda: 简短的一次性函数(排序 key、map/filter 参数)
  • 类型注解: 团队项目、库开发、IDE 提示

2.9 作用域: LEGB 规则

Java/Kotlin 对比

// Java: 块级作用域
public class ScopeDemo {
    private static int global = 10;  // 类级别

    public void method() {
        int local = 20;  // 方法级别

        if (true) {
            int blockScoped = 30;  // 块级别
            System.out.println(local);     // OK: 可以访问外层变量
            System.out.println(blockScoped); // OK
        }
        // System.out.println(blockScoped);  // 编译错误: 块外不可见
    }

    public void anotherMethod() {
        // System.out.println(local);  // 编译错误: 方法外不可见
        System.out.println(global);   // OK: 类变量
    }
}

// Java 的作用域规则: 内层可以访问外层,外层不能访问内层
// 变量在声明的作用域内有效,离开作用域即销毁
// Kotlin: 同样是块级作用域
fun method() {
    val outer = 1
    if (true) {
        val inner = 2
        println(outer)  // OK
    }
    // println(inner)  // 编译错误
}

// lambda 中可以捕获外层变量(闭包)
fun makeCounter(): () -> Int {
    var count = 0
    return { count++ }  // 捕获 count
}

Python 实现

# === LEGB 规则 ===
# Python 查找变量的顺序: Local → Enclosing → Global → Built-in

# Built-in: 内置作用域(print, len, int, str 等)
# Global: 模块级别
# Enclosing: 嵌套函数的外层函数
# Local: 当前函数内部

# === Global 作用域 ===
x = "global"

def outer():
    # x 在这里不是 local,也不是 enclosing,会去 global 找
    print(f"outer: {x}")  # global

    def inner():
        # 同样去 global 找
        print(f"inner: {x}")  # global

    inner()

outer()

# === Local 作用域 ===
x = "global"

def func():
    x = "local"  # 创建了新的 local 变量,不影响 global
    print(f"func: {x}")  # local

func()
print(f"global: {x}")  # global(没变)

# === Enclosing 作用域 ===
def outer():
    x = "enclosing"

    def inner():
        # x 不是 inner 的 local,去 enclosing 找
        print(f"inner: {x}")  # enclosing

    inner()

outer()

# === global 关键字 ===
counter = 0

def increment():
    global counter  # 声明使用 global 变量
    counter += 1     # 修改 global 变量

increment()
increment()
increment()
print(counter)  # 3

# 没有 global 时
counter = 0

def broken_increment():
    # counter += 1  # UnboundLocalError!
    # 原因: Python 看到 counter += 1 中的赋值,认为 counter 是 local 变量
    # 但在赋值前就读取了 counter,所以报错
    pass

# === nonlocal 关键字 ===
def make_counter():
    count = 0  # enclosing 变量

    def increment():
        nonlocal count  # 声明使用 enclosing 变量
        count += 1
        return count

    return increment

counter = make_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

# 没有 nonlocal 时
def broken_counter():
    count = 0

    def increment():
        # count += 1  # UnboundLocalError!
        pass

    return increment

# === for 循环变量泄漏 ===
# Python 的 for 循环没有块级作用域!
for i in range(5):
    x = i * 2

print(i)  # 4 — 循环变量泄漏
print(x)  # 8 — 循环内定义的变量也泄漏

# 这和 Java 完全不同:
# Java: for(int i=0; i<5; i++) { int x = i*2; }
# // i 和 x 在循环外不可见

# 列表推导式不泄漏(Python 3)
result = [i for i in range(5)]
# print(i)  # NameError(Python 3 中推导式有自己的作用域)

# === 闭包 ===
def make_multiplier(factor):
    """返回一个乘法函数,捕获 factor"""
    def multiplier(x):
        return x * factor  # factor 来自 enclosing 作用域
    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))   # 10
print(triple(5))   # 15

# 闭包中的变量是延迟绑定的
def make_funcs():
    funcs = []
    for i in range(3):
        def func():
            return i  # 捕获变量 i,不是值
        funcs.append(func)
    return funcs

f0, f1, f2 = make_funcs()
print(f0())  # 2(不是 0!)
print(f1())  # 2(不是 1!)
print(f2())  # 2

# 修复: 用默认参数捕获值
def make_funcs_fixed():
    funcs = []
    for i in range(3):
        def func(i=i):  # 默认参数在定义时求值
            return i
        funcs.append(func)
    return funcs

f0, f1, f2 = make_funcs_fixed()
print(f0())  # 0
print(f1())  # 1
print(f2())  # 2

# === LEGB 演示: 完整示例 ===
x = "global"

def outer():
    x = "enclosing"

    def middle():
        x = "middle"

        def inner():
            x = "local"
            print(f"inner: {x}")  # local

        inner()
        print(f"middle: {x}")  # middle

    middle()
    print(f"outer: {x}")  # enclosing

outer()
print(f"module: {x}")  # global

# === Built-in 作用域 ===
# 可以覆盖内置函数(但不要这么做!)
# len = 5  # 覆盖了内置的 len 函数
# len([1, 2, 3])  # TypeError: 'int' object is not callable

# 查看内置作用域
import builtins
print(dir(builtins)[:10])  # ['ArithmeticError', 'AssertionError', ...]

核心差异

维度Java/KotlinPython
作用域类型块级作用域函数级作用域(LEGB)
for 循环变量循环结束后不可见循环结束后仍可见(泄漏)
if 块变量块结束后不可见块结束后仍可见
修改外层变量直接修改(如果是 final 则不能)需要 globalnonlocal 声明
闭包变量绑定值捕获延迟绑定(变量引用)
内置函数覆盖不可能(关键字/内置名受保护)可以覆盖(但不要这么做)

常见陷阱

# 陷阱1: 在函数中修改 global 变量忘记声明 global
count = 0

def increment():
    # count += 1  # UnboundLocalError!
    # Python 看到 += 中的赋值操作,认为 count 是 local 变量
    # 但在赋值前就尝试读取它,所以报错
    global count  # 必须声明!
    count += 1

# 陷阱2: 闭包中的延迟绑定
# 前面已详述(make_funcs 示例)
# 解决: 用默认参数捕获值

# 陷阱3: 循环变量泄漏导致 bug
funcs = []
for i in range(3):
    funcs.append(lambda: i)

print([f() for f in funcs])  # [2, 2, 2] — 不是 [0, 1, 2]

# 陷阱4: 在 comprehension 中试图修改外部变量
total = 0
# [total := total + x for x in range(5)]  # 不行!列表推导式不支持赋值
# 用普通循环
for x in range(5):
    total += x
print(total)  # 10

# 陷阱5: class 中的作用域
x = "global"

class MyClass:
    x = "class"

    def method(self):
        # print(x)  # NameError! class 作用域不包含在 LEGB 中
        # Python 的 class 不算 enclosing 作用域
        # 需要通过 MyClass.x 或 self.x 访问
        print(MyClass.x)  # class

何时使用

  • global: 非常少用。大多数情况应该用函数参数和返回值传递数据
  • nonlocal: 闭包中需要修改外层函数的变量(如计数器、状态机)
  • 理解 LEGB: 调试 UnboundLocalError 时,先检查是否需要 global/nonlocal
  • 避免 for 循环变量泄漏: 循环结束后不需要用到循环变量时不用管;需要用到时显式赋值

本章总结: 语法速查表

知识点Java/KotlinPython最容易犯的错
代码块{}缩进混用 Tab 和空格
变量赋值类型声明名字绑定链式赋值共享可变对象
nullnullNone== 而非 is 比较 None
整数有范围无上限以为会溢出(不会)
字符串仅双引号单/双/三引号bytes 和 str 混用
除法/ 整除/ 浮点, // 整除7/2 以为是 3
逻辑运算&&, ||, !and, or, notand/or 返回值不是布尔
for 循环C 风格 + for-each仅迭代器循环变量泄漏
推导式Stream API[expr for x in iter]嵌套超过2层
默认参数不支持/支持支持可变默认参数陷阱
作用域块级函数级 (LEGB)忘记 global/nonlocal

核心认知: Python 的语法看似简单,但"看起来像"的东西往往"行为不同"。本章的每个陷阱都是从 Java/Kotlin 转过来的开发者踩过的坑。遇到 UnboundLocalErrorIndentationErrorTypeError 时,回来翻这一章。