python main参数传递方式_Python 有趣的参数传递机制

刚刚发现 Python 的参数传递机制很有意思,值得专门写个博客记录一下。

在 C/C++ 中,有两种参数传递方式:值传递 (pass by value) 和引用传递 (pass by reference)。值传递:传入函数的实参被复制给该函数的形参。形参是局部变量,函数运行完成后,这些局部变量的内存被释放,不会影响函数外部实参的值。

引用传递:将实参的地址传入函数中,可以在函数内部直接通过这个地址修改实参。

由于值传递会复制传入的参数,因此形参地址和实参地址是不同的,例如:

#include

int f(int a){

printf("In f: &a=%p\n", &a);

}

int main(){

int a = 3;

printf("In main: &a=%p\n", &a);

f(a);

return 0;

}

// 运行结果// In main: &a=0x7ffefede5954// In f: &a=0x7ffefede593c

但在 Python 中,参数传递的规则却不太一样:

def f(a):

print("In f: id(a)={}".format(id(a)))

def main():

a = 3

print("In main: id(a)={}".format(id(a)))

f(a)

if __name__=='__main__':

main()

# 运行结果

# In main: id(a)=94285635591488

# In f: id(a)=94285635591488

f 中的id(a)竟然与 main 中的id(a)是一样的!那在 f 中改变 a 的值,岂不是会影响到 main 中 a 的值吗?(并不会)。

我们进一步发现:当 a 在 f 中没有被改变的时候,f 中的 id(a) 与 main 中的id(a) 相同,而一旦 f 内部改变了 a 的值,这两个 id 就不同了:

def f(a):

print("In f before change: id(a)={}".format(id(a)))

a += 1

print("In f after change: id(a)={}".format(id(a)))

def main():

a = 3

print("In main before call f: id(a)={}".format(id(a)))

f(a)

print("In main after call f: id(a)={}".format(id(a)))

if __name__=='__main__':

main()

# 结果

# In main before call f: id(a)=94870107749696

# In f before change: id(a)=94870107749696

# In f after change: id(a)=94870107749728

# In main after call f: id(a)=94870107749696

这一规则,适用于 python 中的所有不可变对象(immutable objects),例如 int, float, bool, string, unicode, tuple 等等一旦被创建,就无法(在原地址上)被修改的对象:

In [1]: a = "hello world"

In [2]: a[0] = "H"

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in

----> 1 a[0] = "H"

TypeError: 'str' object does not support item assignment

而对于 python 中的可变对象(mutable objects),例如 list, dict, set 以及用户自定义的类,等可以修改其状态的对象,传递规则又有不同:

def f(a):

print("In f before change: id(a)={}".format(id(a)))

a.append(1)

print("In f after change: id(a)={}".format(id(a)))

def main():

a = [1, 2, 3]

print("In main before call f: id(a)={}".format(id(a)))

f(a)

print("In main after call f: id(a)={}".format(id(a)))

if __name__=='__main__':

main()

# 结果

# In main before call f: id(a)=140263104163584

# In f before change: id(a)=140263104163584

# In f after change: id(a)=140263104163584

# In main after call f: id(a)=140263104163584

我们发现,修改了 list 后,它的 id 并不会改变。类似 C/C++ 中的引用传递,此时在 f 中对 a 做的改变,将影响到 main 中的 a。但这和引用传递也有明显区别,例如在 f 中为 a 赋值一个新的 list,main 中的 a 是不会受到影响的:

def f(a):

a = [4, 5]

print("In f: a={}".format(a))

def main():

a = [1, 2, 3]

f(a)

print("In main: a={}".format(a))

if __name__=='__main__':

main()

# 结果

# In f: a=[4, 5]

# In main: a=[1, 2, 3]

原因是,f 中的 a=[4, 5] 相当于把局部变量 a 绑定了一个新的 list 对象,这个 a 所指向的内存,已经不是 main 中的 a 所指向的那块内存了。由于 list 是可变对象, a.append(5) 并不会开辟一块新的内存,而是会在原内存位置的对象上增加一个元素。

综上,不能简单地将“值传递”或者“引用传递”的概念套用到 Python 中,Python 中的参数传递机制,更类似于“内存传递”。当这块内存存储的是可变对象时,可以直接在它上面做修改;当这块内存存储的是不可变对象时,修改某个变量,本质上并没有修改这块内存的数据,而是开辟了一块新的内存,将新内存赋值给该变量,而旧内存则被垃圾回收了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值