map作为全局变量赋值_Pyhon中的赋值与参数传递,或许跟你想的不太一样!

小知识点 — Remember that arguments are passed by assignment in Python

Python中的赋值与参数传递

问题提出

def func(m):
    m[0] = 20
    m = [4, 5, 6]
    return m

l = [1, 2, 3]
func(l)
print('l =', l)

以上代码输出为多少?

答案是:[20, 2, 3],  过了就不用看啦

基本规则

Remember that arguments are passed by assignment in Python.

函数中参数通过赋值传递

赋值

对Python赋值的两种理解

  • 直观印象:变量是一个container,给变量赋值类似于往container中填入一个对象,再次赋值就是把container中的对象进行替换

  • 正确理解:Python中的变量可理解为label,给变量赋值类似于把label贴在某个对象上,再次赋值就是把当前的label贴在另一个对象上

以上两种理解的差异点:前者,变量作为固定存在,赋值只改变固定存在中的对象内容;后者,变量仅仅作为一个label,赋值等价于往各种被创建的对象上贴标签

Python中万物解对象,包括函数、类、模块,字符串’hello’,数字1、2、3等。

在 Python 中,前一个理解是不准确的!在 Python 中,前一个理解是不准确的!在 Python 中,前一个理解是不准确的!

《fluent python》中的经典例题

a = [1, 2, 3]
b = a
a.append(4)
b
[1, 2, 3, 4]
image-20200910002355076

使用标识符identifier说明这一点

a = [1, 2, 3]
print( "a--->",id(a)  )
a = [1, 2, 3]
print( "a--->",id(a)  )
a---> 2322204749960
a---> 2322204695304
a = [1, 2, 3]
print( "a--->",id(a)  )
b = [3,2,1]
print( "b--->",id(b)  )
c = [13,12,11]
print( "c--->",id(c)  )
a = c
print( "a--->",id(a)  )
a---> 2322208596488
b---> 2322207525576
c---> 2322208596872
a---> 2322208596872

前者,虽然内容一样,但是本质上是new了两个identifier,所以id()数值不一样;后者a=c 使得 a、c变量贴到了同一个对象上,所以label a和c 对应的identifier值一样

在Python中,使用语法糖 { }、[ ]、( ) 等替代了dict、set、list、tuple等,在Java中实例化对象直接使用new,更impressive

给变量重新赋值,label就被从原来的对象上撕下来贴到了新的对象上

参数传递

Python中参数传递的两个基本考虑点:(1)Python中的参数是通过赋值传递;(2)作用域问题

def fn(x):
    x = 3

a = 1
fn(a)
print(a)
1

以上代码的输出结果为1,a 没有变化。上述过程的实现过程如下:

  • Step 1、调用 fn(a) 相当于做了一次x=a,即把 label  x 贴在a 所映射到对象上;
  • Step 2、 在作用域 fn 内部,把label  x 从a 所映射到对象上撕下来、并贴到3这个新对象上。

换一种 list 数据类型效果相同

def fn(x):
    x = [4,5,6]

a = [1,2,3]
fn(a)
print(a)
[1, 2, 3]

再看一种各种推文教材上常用的例子,涉及到list是mutable对象这个知识点。其中,a和b标签均被贴到同一个list对象上,b[1] 是对所指对象的改动,而不是对b 这个label 本身的改动 ( b中存储的标识符 identifier 不改变,所映射的 identifier不改变 )

a = [1,2,3]
print('a', a, id(a))
b = a
print('b', b, id(b))
b[1] = 5
print('a', a, id(a))
print('b', b, id(b))
a [1, 2, 3] 2713876240136
b [1, 2, 3] 2713876240136
a [1, 5, 3] 2713876240136
b [1, 5, 3] 2713876240136

问题solution

(1)先看文首提出的问题一个变体

def func(m):
    m[0] = 20
    # m = [4, 5, 6]
    return m

l = [1, 2, 3]
func(l)
print('l =', l)

答案:l = [20, 2, 3]。上述过程的实现过程如下:

Step 1、在内存中new 一个list 对象 [1,2,3],并将标签label I 贴到这个list上

Step 2、调用func(l),即赋值操作 m =  I, 在作用域func 中对 刚才new出来的list对象贴上 m 标签

Step 3、 m[0] = 20 表示更改刚才list对象中的第0项element值,并退出func作用域

Step 4、print('l =', l) 表示输出打印刚才的 list对象

(2)再看文首提出的问题

def func(m):
    m[0] = 20
    m = [4, 5, 6]
    return m

l = [1, 2, 3]
func(l)
print('l =', l)

答案:l = [20, 2, 3]。上述过程的实现过程如下:

Step 1、在内存中new 一个list 对象 [1,2,3],并将标签label I 贴到这个list上

Step 2、调用func(l),即赋值操作 m =  I, 在作用域func 中对 刚才new出来的list对象贴上 m 标签

Step 3、 m[0] = 20 表示更改刚才list对象中的第0项element值

Step 4、重新在内存中new一个list对象 [4, 5, 6],同时把m标签从第一个list对象上撕下来贴到新new出来的list对象上,并退出func作用域

Step 5、print('l =', l) 表示输出打印第一个 list对象

(3)再看文首提出问题的令一个变体

def func(m):
    m[0] = 20
    m = [4, 5, 6]
    return m

l = [1, 2, 3]
l=func(l)
print('l =', l)

答案:l = [4, 5, 6]。上述过程的实现过程如下:

Step 1、在内存中new 一个list 对象 [1,2,3],并将标签label I 贴到这个list上

Step 2、调用func(l),即赋值操作 m =  I, 在作用域func 中对 刚才new出来的list对象贴上 m 标签

Step 3、 m[0] = 20 表示更改刚才list对象中的第0项element值

Step 4、重新在内存中new一个list对象 [4, 5, 6],同时把m标签从第一个list对象上撕下来贴到新new出来的list对象上

Step 5、把第二个new出来的list对象return出来并贴上l标签然后退出作用域

Step 6、print('l =', l) 表示输出打印第一个 list对象

notes:(1) Python中所有function均有返回值,不写Return时,默认返回None;(2)函数有非None返回值,并在调用时对将返回值进行了赋值,就相当于给函数内部返回的对象贴标签。

总结

通过一个function调用来修改外部变量的值,实现方法有:

  • 通过 Return 返回值 赋值 =

  • 使用 全局变量 global

  • 修改 list 或 dict 这类mutable对象的内部元素

  • 修改class实例化的成员变量

有很多的教程把 Python 的函数参数传递分为mutable对象和immutable对象来说明。这种理解困扰了我很久,其实是错误的,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.赋值本身就是创建一份 对象的引用(对象唯一标识符identifier或者内存地址id),形参和实参间不存在别名的关系、不存在引用传递。

网上很容易搜到“参数是可变对象就相当于引用传递”这种错误的理解,一直困扰了我好久

References

[1]  知乎  Crossin的编程教室   https://zhuanlan.zhihu.com/p/38375434?utm_source=wechat_session&utm_medium=social&utm_oi=986911835743977472&s_r=0

Motto

日拱一卒,功不唐捐

长按下图  关注xiao乐
e6356dd828422daba2e762f2fa4e1359.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值