【python】参数传递:赋值传递

一、Python的变量及其赋值的基本原理

在Python中,变量是一种用于存储数据的容器。与许多其他编程语言不同,Python的变量是动态类型的,这意味着在声明变量时不需要指定其数据类型。Python会根据赋给变量的值自动确定其数据类型。

1、变量的命名规则
  1. 变量名必须以字母或下划线_开头。
  2. 变量名可以包含字母、数字和下划线,但不能包含空格或特殊字符(除了_)。
  3. 变量名是大小写敏感的。
2、变量的赋值

在Python中,赋值是通过=操作符完成的。当你将一个值赋给变量时,Python会在内存中为该值分配一个空间,并将该空间的地址与变量名关联起来。

例如:

x = 10  # 将整数10的地址赋给变量x

这里,x是变量名,10是值。赋值操作后,x就指向了内存中存储整数10的位置。

3、变量与对象

在Python中,所有的值都是对象,包括整数、浮点数、字符串、列表、元组、字典等。当你创建一个变量并给它赋值时,实际上是在创建一个指向该对象的引用(或说是指针)。

4、赋值操作的本质

赋值操作并不会复制数据,而只是创建了新的引用。例如:

a = [1, 2, 3]  # 创建一个列表对象,并将其地址赋给变量a
b = a  # 将a的引用(即列表对象的地址)赋给b

在上述代码中,ab都指向同一个列表对象。因此,对ab所做的任何修改都会影响到同一个列表对象。

二、参数是如何传递的

在Python中,参数的传递方式通常被描述为“按值传递”(pass-by-value),但这并不意味着在传递参数时Python会复制对象的值。实际上,对于不可变对象(如整数、浮点数、字符串和元组),Python确实传递了这些对象的值的一个“拷贝”,但由于这些对象是不可变的,所以这个“拷贝”在大多数情况下是无关紧要的。

然而,对于可变对象(如列表、字典、集合和自定义对象),Python传递的是对象的引用(即内存地址的引用),而不是对象的实际内容。这意味着函数内部对可变对象的修改会影响到原始对象,因为函数内部和外部都是引用同一个对象。

让我们通过几个例子来详细解释这一点:

1、不可变对象的参数传递
def modify_string(s):
    s = s + "world"  # 这里的s是一个新的字符串对象
    print("Inside function:", s)

original_string = "hello"
modify_string(original_string)
print("Outside function:", original_string)  # 输出仍然是"hello"

在上面的例子中,尽管我们在函数内部修改了s的值,但原始字符串original_string并没有改变。这是因为字符串是不可变的,当我们执行s = s + "world"时,Python实际上创建了一个新的字符串对象,并将s指向这个新对象。原始的字符串对象(“hello”)并没有受到影响。

2、可变对象的参数传递
def modify_list(lst):
    lst.append(4)  # 直接修改了lst引用的列表对象
    print("Inside function:", lst)

original_list = [1, 2, 3]
modify_list(original_list)
print("Outside function:", original_list)  # 输出[1, 2, 3, 4],列表被修改了

在上面的例子中,我们直接修改了函数内部lst引用的列表对象。由于列表是可变的,并且我们传递的是列表的引用,所以原始列表original_list也被修改了。

总结来说,Python中的参数传递可以看作是“赋值传递”或者“按对象引用传递”的。对于不可变对象,这种传递方式在大多数情况下表现得像“按值传递”,因为不可变对象的值不能被修改。简单的赋值只能改变其中一个变量的值,其余变量则不受影响。
而对于可变对象,这种传递方式则表现得像“按引用传递”,因为函数内部和外部都可以修改同一个对象。即当其改变时,所有指向这个对象的变量都会改变。

敲黑板

如果你想通过一个函数来改变某个变量的值,通常有两种方法。一种是直接将可变数据类型(比如列表,字典,集合)当作参数传入,直接在其上修改;第二种则是创建一个新变量,来保存修改后的值,然后将其返回给原变量。在实际工作中,我们更倾向于使用后者,因为其表达清晰明了,不易出错。
当然,你的观点是正确的。下面我将给出两种方法的示例,并解释为什么在某些情况下第二种方法可能更受欢迎。

第一种方法:直接修改传入的可变数据类型


> def modify_list_in_place(lst):  
>     lst.append(4)  
>     print("Inside function:", lst)      
> original_list = [1, 2, 3]   
> print("Before function call:", original_list)  
> modify_list_in_place(original_list)   
> print("After function call:",original_list)  
> # 输出:[1, 2, 3, 4],列表被修改了 ```

第二种方法:创建并返回新变量

    new_lst = lst + [4]  # 创建一个新的列表,包含原列表的内容和新元素
    print("Inside function:", new_lst)
    return new_lst

original_list = [1, 2, 3] print("Before function call:",
original_list) original_list = modify_list_return_new(original_list) 
# 注意这里重新赋值了original_list print("After function call:", original_list)  # 输出:[1, 2, 3, 4],但它是新的列表对象 ```

在这个例子中,`modify_list_return_new` 函数创建了一个新的列表 `new_lst`,它是原始列表 `lst`
和新元素 `[4]` 的组合。然后函数返回这个新列表,并在函数外部重新将 `original_list` 赋值为这个新列表。

为什么第二种方法在某些情况下更受欢迎?

  1. 清晰性:通过返回一个新对象,你可以明确地知道哪个对象在函数外部被修改了。这有助于减少潜在的副作用和意外的状态变化。

  2. 可维护性:对于其他阅读你代码的人来说,返回一个新对象通常更容易理解,因为它避免了复杂的引用和状态管理。

  3. 线程安全:在多线程环境中,直接修改传入的参数可能导致竞态条件和数据不一致。返回一个新对象可以避免这些问题。

  4. 函数式编程风格:在某些情况下,你可能希望函数没有副作用,即函数不修改其外部状态。返回一个新对象是实现这一点的有效方法。

然而,需要注意的是,在某些情况下(例如,性能关键的应用或需要大量内存的应用),直接修改传入的可变对象可能更为高效。在这些情况下,你需要权衡清晰性和性能之间的利弊。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值