Python文档中有一段话:

Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se.

我们常说参数的传递分为按值传递与按引用传递,Python中的passed by assignment该如何理解?

argument vs parameter

stackoverflow上关于paramter 和 argument的解释:

A parameter is a variable in a method definition. When a method is called, the arguments are the data you pass into the method's parameters.

  • PARAMETER → PLACEHOLDER (This means a placeholder belongs to the function naming and be used in the function body)
  • ARGUMENT → ACTUAL VALUE (This means an actual value which is passed by the function calling)
 

.NET值传递与引用传递

.NET中类型分为值类型和引用类型两种,默认使用值传递,若使用引用传递,需明确使用refinout关键字。

In C#, arguments can be passed to parameters either by value or by reference. Passing by reference enables function members, methods, properties, indexers, operators, and constructors to change the value of the parameters and have that change persist in the calling environment. To pass a parameter by reference with the intent of changing the value, use the ref, or out keyword. To pass by reference with the intent of avoiding copying but not changing the value, use the in modifier.

Passing Reference-Type Parameters

When you pass a reference-type parameter by value, it is possible to change the data belonging to the referenced object, such as the value of a class member. However, you cannot change the value of the reference itself; for example, you cannot use the same reference to allocate memory for a new object and have it persist outside the method. To do that, pass the parameter using the ref or out keyword. For simplicity, the following examples use ref.

Passing Reference Types by Value

class PassingRefByVal
{
static void Change(int[] pArray)
{
pArray[] = ; // This change affects the original element.
pArray = new int[] {-, -, -, -, -}; // This change is local.
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[]);
} static void Main()
{
int[] arr = {, , };
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr []); Change(arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr []);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: 888
*/

In the preceding example, the array, arr, which is a reference type, is passed to the method without the ref parameter. In such a case, a copy of the reference, which points to arr, is passed to the method. The output shows that it is possible for the method to change the contents of an array element, in this case from 1 to 888. However, allocating a new portion of memory by using the new operator inside the Change method makes the variable pArray reference a new array. Thus, any changes after that will not affect the original array, arr, which is created inside Main. In fact, two arrays are created in this example, one inside Main and one inside the Change method.

Passing Reference Type by Reference

The following example is the same as the previous example, except that the ref keyword is added to the method header and call. Any changes that take place in the method affect the original variable in the calling program.

class PassingRefByRef
{
static void Change(ref int[] pArray)
{
// Both of the following changes will affect the original variables:
pArray[] = ;
pArray = new int[] {-, -, -, -, -};
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[]);
} static void Main()
{
int[] arr = {, , };
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[]); Change(ref arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: -3
*/

All of the changes that take place inside the method affect the original array in Main. In fact, the original array is reallocated using the new operator. Thus, after calling the Change method, any reference to arr points to the five-element array, which is created in the Change method.

⚠️Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. A method parameter can be modified by ref regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.

Passing Value-Type Parameters

Passing a value-type variable to a method by value means passing a copy of the variable to the method. Any changes to the parameter that take place inside the method have no affect on the original data stored in the argument variable. If you want the called method to change the value of the argument, you must pass it by reference, using the ref or out keyword. You may also use the in keyword to pass a value parameter by reference to avoid the copy while guaranteeing that the value will not be changed. For simplicity, the following examples use ref.

Passing Value Types by Value

class PassingValByVal
{
static void SquareIt(int x)
// The parameter x is passed by value.
// Changes to x will not affect the original value of x.
{
x *= x;
System.Console.WriteLine("The value inside the method: {0}", x);
}
static void Main()
{
int n = ;
System.Console.WriteLine("The value before calling the method: {0}", n); SquareIt(n); // Passing the variable by value.
System.Console.WriteLine("The value after calling the method: {0}", n); // Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}
/* Output:
The value before calling the method: 5
The value inside the method: 25
The value after calling the method: 5
*/

The variable n is a value type. It contains its data, the value 5. When SquareIt is invoked, the contents of n are copied into the parameter x, which is squared inside the method. In Main, however, the value of n is the same after calling the SquareIt method as it was before. The change that takes place inside the method only affects the local variable x.

Passing Value Types by Reference

The following example is the same as the previous example, except that the argument is passed as a ref parameter. The value of the underlying argument, n, is changed when x is changed in the method.

class PassingValByRef
{
static void SquareIt(ref int x)
// The parameter x is passed by reference.
// Changes to x will affect the original value of x.
{
x *= x;
System.Console.WriteLine("The value inside the method: {0}", x);
}
static void Main()
{
int n = ;
System.Console.WriteLine("The value before calling the method: {0}", n); SquareIt(ref n); // Passing the variable by reference.
System.Console.WriteLine("The value after calling the method: {0}", n); // Keep the console window open in debug mode.
System.Console.WriteLine("Press any key to exit.");
System.Console.ReadKey();
}
}
/* Output:
The value before calling the method: 5
The value inside the method: 25
The value after calling the method: 25
*/

In this example, it is not the value of n that is passed; rather, a reference to n is passed. The parameter x is not an int; it is a reference to an int, in this case, a reference to n. Therefore, when x is squared inside the method, what actually is squared is what x refers to, n.

图解按引用传递

曾在Github上提过一个issue,用几幅图描述了按引用传递的情况。

Python passed by assignment

说了这么多,Python中的passed by assignment该怎么理解?Python中类型没有像.NET那样分为值类型与引用类型。Python中所有类型的值都是对象,这些对象分为可变对象与不可变对象两种:

  • 不可变类型
    floatintstrtuplebool

  • 可变类型
    listdictset

Python中,所有的数据类型都是对象,在传参时,传递的是对象的引用。与.NET中按值传递引用类型类似。对于不可变类型:

num = 9

def changeValue(value: int):
# int类型是不可变类型,改变value的值会让value指向新的对象8,不会改变num
value = 8 print(num)
changeValue(num)
print(num) """
输出:
9
9
"""

对于可变类型:

l = [9, 8, 7]

def createNewList(l1: list):
# l1+[0]会创建一个新的list对象,只是改变了局部变量l1的引用地址,不会影响变量l
l1 = l1+[0] def changeList(l1: list):
# 在列表末尾添加一个元素,因为l1和l引用同一个对象,所以l也会改变
l1.append(0) print(l)
createNewList(l)
print(l)
changeList(l)
print(l) """
输出:
[9, 8, 7]
[9, 8, 7]
[9, 8, 7, 0]
"""

小结

按值传递就是拷贝出变量值的一个副本,所有的操作都是针对这个副本的,对原始数据没有影响。

⚠️对于.NET中的值类型,原始数据就是变量的值;对于.NET中的引用类型,原始数据也是值变量的值,只不过这个值存储的是内存地址

从下图中可以看到,按值传递引用类型,变量p2和p在内存中的地址不同,但存储的值相同:

 

按引用传递相当于给变量起了个别名,所有的操作都相当于操作原变量。从下图可以看到,按引用传递引用类型,p1和p在内存中的地址相同,存储的内容也相同:
 

对于按引用传递值类型和按值传递值类型,也是一样的:
 

推荐阅读

Passing Parameters (C# Programming Guide)

Method Parameters (C# Reference)

The address-of operator

Python中的passed by assignment与.NET中的passing by reference、passing by value的更多相关文章

  1. 【python】实践中的总结——列表『持续更新中』

    2016-04-03 21:02:50 python list的遍历 list[a::b]   #从list[a] 开始,每b个得到一个元组,返回新的list 举个例子: >>> l ...

  2. 《Python自然语言处理》中文版-纠错【更新中。。。】

    最近在看<Python自然语言处理>中文版这本书,可能由于是从py2.x到py3.x,加上nltk的更新的原因,或者作者的一些笔误,在书中很多代码都运行不能通过,下面我就整理一下一点有问题 ...

  3. Python在金融,数据分析,和人工智能中的应用

    Python在金融,数据分析,和人工智能中的应用   Python最近取得这样的成功,而且未来似乎还会继续下去,这有许多原因.其中包括它的语法.Python开发人员可用的科学生态系统和数据分析库.易于 ...

  4. 《Python CookBook2》 第一章 文本 - 检查字符串中是否包含某字符集合中的字符 && 简化字符串的translate方法的使用

    检查字符串中是否包含某字符集合中的字符  任务: 检查字符串中是否出现了某个字符集合中的字符 解决方案: 方案一: import itertools def containAny(seq,aset): ...

  5. python中print()函数的“,”与java中System.out.print()函数中的“+”

    python中的print()函数和java中的System.out.print()函数都有着打印字符串的功能. python中: print("hello,world!") 输出 ...

  6. 用python在excel中读取与生成随机数写入excel中

    今天是我第一次发博客,就关于python在excel中的应用作为我的第一篇吧. 具体要求是:在一份已知的excel表格中读取学生的学号与姓名,再将这些数据放到新的excel表中的第一列与第二列,最后再 ...

  7. python编程中的if __name__ == 'main与windows中使用多进程

    if __name__ == 'main 一个python的文件有两种使用的方法,第一是直接作为程序执行,第二是import到其他的python程序中被调用(模块重用)执行. 因此if __name_ ...

  8. python之匿名函数以及在内置函数中的使用

    一. 匿名函数 Python使用 lambda 来创建匿名函数.所谓匿名函数,它与用 def 关键字定义的函数相比,没有函数名称. 1.1 匿名函数定义及特点 语法: lambda [para1, p ...

  9. Python “No module named” 以及在Python2中可以导入,但在python3中却出现的原因

    Python “No module named” 以及在Python2中可以导入,但在python3中却出现的原因 原因之1: 例如有这样的一个包和它的模块: Test __init__.py Mod ...

随机推荐

  1. 全网趣味网站分享:今日热榜/Pixiv高级搜索/win10激活工具/songtaste复活/sharesome汤不热替代者

    1.回形针手册 由科普类视频节目“回形针PaperClip”近期提出的一个实用百科工具计划,计划名称是回形针手册. 包含了当下科技,农业等等各行各业的各种相关信息,计划刚刚开始! 关于回形针手册的详细 ...

  2. ansible小结(八)ansible-playbook简单使用

    ansbile-playbook是一系统ansible命令的集合,其利用yaml 语言编写,运行过程,ansbile-playbook命令根据自上而下的顺序依次执行.同时,playbook开创了很多特 ...

  3. zookeeper — 实现分布式锁

    一.前言 在之前的文章中介绍过分布式锁的特点和利用Redis实现简单的分布式锁.但是分布式锁的实现还有很多其他方式,但是万变不离其宗,始终遵循一个特点:同一时刻只能有一个操作获取.这篇文章主要介绍如何 ...

  4. Think in Speed (关于速度的一点思考)

    天下武功,无坚不摧,唯快不破!所以我们重视速度没毛病! 老话说:不要过早优化.赞同! 我们在写代码过程中,有时可能就是为了追求所谓的性能,然后,就给自己挖坑了. 关于开发速度,我有以下几点思考: 1. ...

  5. wpf file embeded resource is readonly,Copy always will copy the file and its folder to the bin folder

    Wpf file embeded resource will compile the file into the assembly and it will be readonly and can no ...

  6. python基础(2):python的安装、第一个python程序

    1. 第一个python程序 1.1 python的安装 自己百度,这是自学最基本的,安装一路确定即可,记得path下打钩. 1.2 python的编写 python程序有两种编写方式: 1.进入cm ...

  7. Python3---爬虫Post传参

    前言 Python3 Post 传参主要用到的是urllib.request.urlopen(url,data)参数当中data.data参数主要是设置post的传参. 修改时间:20191218 天 ...

  8. 对data标签获取到的时间进行比较

    前言 data(时间属性)是HTML5中新增的一个属性,常用于选择日期.时间,但这个是无法通过属性去限制其不能选择过去的时间的,这个时候就需要我们在JQ中对其进行比较了! 由于放效果图,需要在数据库里 ...

  9. [20190918]关于函数索引问题.txt

    [20190918]关于函数索引问题.txt 1.环境:SCOTT@test01p> @ ver1PORT_STRING                    VERSION        BA ...

  10. MySQL数据篇(五)--SQL对数据进行按月统计,或对数据进行按星期统计

    对于所有的需求,当你不知道怎么处理的时候,你就先用最简单的方法,或者说的明白一点,用最原始的方法,先实现业务需求再说. 一.对提现队列数据表“ims_checkout_task”进行汇总统计,按月汇总 ...