飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 认识Python

    • Python 历史与特点
    • Python 2 与 Python 3 的核心差异
    • 安装与运行 Python 2.7.18
    • 编码规范 PEP 8
  • 第2章 基础语法

    • 变量与对象
    • 数字类型
    • 字符串 str
    • Unicode 字符串
    • 运算符
    • 空值 None
  • 第3章 流程控制

    • if 条件语句
    • if-else 条件语句
    • if-elif-else 多分支
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 与 xrange
    • 循环控制:break、continue、pass
    • 循环 else 子句
  • 第4章 数据结构

    • 列表基础
    • 列表方法
    • 列表推导式
    • 元组
    • 字典基础
    • 字典方法
    • 字典循环技巧
    • 集合
    • 序列解包
    • 序列比较
  • 第5章 函数

    • 定义函数
    • 参数传递机制
    • 默认参数
    • 关键字参数
    • 可变参数
    • Lambda 表达式
    • 文档字符串
    • 函数对象
  • 第6章 模块与包

    • import 导入
    • 模块搜索路径
    • name 与主程序
    • 编译文件 .pyc 与 .pyo
    • 包结构
    • dir() 函数
  • 第7章 文件与IO

    • 打开与关闭文件
    • 文件读写方法
    • with 上下文管理器
    • 格式化输出:% 操作符
    • 格式化输出:str.format()
    • JSON 序列化
  • 第8章 面向对象

    • 类定义与实例化
    • init 构造方法
    • 类变量与实例变量
    • 方法调用与 self
    • 继承基础
    • 多重继承
    • 新式类与旧式类
    • 私有变量与名称改写
    • 属性装饰器 property
    • 类方法与静态方法
    • 魔术方法
    • 空类与数据记录
  • 第9章 异常处理

    • 异常类型
    • try-except
    • try-except-else-finally
    • 抛出异常 raise
    • 自定义异常
    • with 语句与上下文管理器
  • 第10章 迭代器与生成器

    • 迭代器协议
    • 生成器函数
    • 生成器表达式
    • itertools模块
  • 第11章 标准库精要

    • os模块
    • sys模块
    • datetime模块
    • re模块
    • json模块
    • collections模块
    • math与random模块
    • urllib2与网络请求
    • subprocess与命令执行
    • threading与并发
    • unittest与测试
    • 虚拟环境与包管理
  • 第12章 工程实践

    • 调试技巧
    • 性能分析
    • 文档与注释
    • 下一步学习

参数传递机制

Python 的参数传递方式常被误解为"传值"或"传引用"。实际上,Python 使用的是传对象引用(call by object reference 或 call by assignment):函数接收的是对象的引用(类似指针),但引用本身是按值传递的。

核心规则

def modify_list(lst):
    lst.append(4)       # 修改对象本身

def reassign_list(lst):
    lst = [1, 2, 3]     # 让 lst 指向新对象

a = [1, 2, 3]
modify_list(a)
print a             # [1, 2, 3, 4] —— 被修改了!

b = [1, 2, 3]
reassign_list(b)
print b             # [1, 2, 3] —— 没变!

关键区别:

  • lst.append(4):通过引用找到原对象,修改对象的内容
  • lst = [1, 2, 3]:创建新对象,让局部变量 lst 指向它,不影响外部变量 b

不可变对象的传递

数字、字符串、元组是不可变对象,函数内任何"修改"都是创建新对象:

def increment(x):
    x += 1
    print "inside:", x    # 11

n = 10
increment(n)
print "outside:", n       # 10 —— 没变!

x += 1 创建了新整数对象 11,让局部变量 x 指向它。外部变量 n 仍然指向 10。

def append_suffix(s):
    s += " world"
    print "inside:", s    # "hello world"

text = "hello"
append_suffix(text)
print "outside:", text    # "hello" —— 没变!

字符串不可变,s += " world" 创建了新字符串 "hello world"。

可变对象的传递

列表、字典是可变对象,函数内可以修改对象内容:

def add_item(items, item):
    items.append(item)

cart = ["apple"]
add_item(cart, "banana")
print cart          # ['apple', 'banana'] —— 被修改了!

但如果重新赋值,外部不受影响:

def clear_cart(cart):
    cart = []           # 局部变量指向新列表

cart = ["apple"]
clear_cart(cart)
print cart          # ['apple'] —— 没变!

常见陷阱

陷阱 1:默认参数的可变对象

def add_item_bad(item, items=[]):
    items.append(item)
    return items

print add_item_bad(1)   # [1]
print add_item_bad(2)   # [1, 2] —— 列表被共享了!

默认参数在函数定义时求值,只创建一次。所有调用共享同一个列表。

陷阱 2:修改遍历中的列表

def remove_evens(nums):
    for n in nums:
        if n % 2 == 0:
            nums.remove(n)      # 危险!

nums = [1, 2, 4, 3]
remove_evens(nums)
print nums          # [1, 4, 3] —— 4 没被删除!

陷阱 3:浅拷贝的误解

def process(data):
    copy = data[:]      # 浅拷贝
    copy[0][0] = 999    # 修改子列表
    return copy

matrix = [[1, 2], [3, 4]]
result = process(matrix)
print matrix        # [[999, 2], [3, 4]] —— 原矩阵也被改了!

浅拷贝只复制一层,子列表仍然共享。

如何安全地传递参数

需要修改外部对象时:直接修改对象内容

def sort_in_place(nums):
    nums.sort()         # 原地排序

不需要修改时:创建副本

def get_sorted(nums):
    return sorted(nums)     # 返回新列表,原列表不变

需要完全独立的副本时:深拷贝

import copy

def process(data):
    safe_copy = copy.deepcopy(data)
    # 随意修改 safe_copy,不影响外部
    return safe_copy

与 C/Java 的对比

语言机制效果
C传值修改参数不影响外部
C(指针)传地址可以修改外部变量
Java传值(引用)可以修改对象内容,不能重新赋值引用
Python传对象引用同 Java:可以修改可变对象,不能重新绑定外部变量

理解这个机制的关键是区分"修改对象"和"重新赋值变量"。Python 中变量只是名字,名字指向对象。函数内可以修改对象(如果对象可变),但不能让外部名字指向新对象。

上一页
定义函数
下一页
默认参数