ITPub博客

首页 > 数据库 > 数据库开发技术 > python面向对象编程篇(18):其实python传递永远是引用传递,没值传递的说法

python面向对象编程篇(18):其实python传递永远是引用传递,没值传递的说法

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-03-22 18:30:00 0 删除 编辑

关于python之中是是值传递还是引用传递的问题,之前也初学的时候一直有所困扰,但是通过多次的对python内存机制的了解后,其实你会发现一个真理!

在python值传递和引用传递的问题,就会发现在python里讨论这个确实不存在任何的意义!

再看变量和对象的关系

1. 栈内存和堆内存关系图

如果你有了解过我之前写关于内存管理机制的内容的话,大致会明白一个道理:

变量 = 对象

变量保持对象的堆内存的地址,所以是叫做对象的引用!

2.再看浅拷贝和深拷贝的图解

其实上面我们也可以总结出一个原理:

  • (对象)堆内存地址 = (变量)栈内存地址 = (变量)对象的引用地址

  • 如果拷贝对象是不可变地址,则共享的是同一个地址(不可变对象共享地址的)

  • 如果拷贝对象是可变,都会生成新的地址

3.其实可以理解为python中永远是引用传递(没有所谓的传值的说法,个人见解)

我们的知道我们的python对象分为可变对象和不可变对象。

PS(个人见解):其实上面的图()里面说的值类型其实也有点误导,严格来说其实不算是一个值类型,它只是一个装载其他不可变对象的一个容器.

如下代码:

3.1 关于不可变对象的引用地址实践探究:

a= 1
b= 1
print('不可变对象1的地址为(把地址给了a变量):',id(a))
print('不可变对象1的地址为(把地址给了b变量):',id(b))
print('a和b的对象引用地址是否一致:',a is b)

输出结果为:
不可变对象1的地址为(把地址给了a变量):140711622468432
不可变对象1的地址为(把地址给了b变量):140711622468432
a和b的对象引用地址是否一致:True

3.2 关于可变对象的引用地址实践探究:

a= [1,2,3]
print('[]是可变对象,装载容器内是不可变对象:',id(a))
print('[0]是不可变对象,装载容的对象不可变对象:',id(a[0]))
print('[1]是不可变对象,装载容的对象不可变对象:',id(a[1]))
print('[2]是不可变对象,装载容的对象不可变对象:',id(a[2]))

b =[1,2,3]

print('b[0]是不可变对象,和a[0]是不可对象地址比较:',id(a[0]),id(b[0]))
print('b[0]是不可变对象,和a[0]是不可对象地址比较:',a[0]is b[0])

输出结果为:

[]是可变对象,装载容器内是不可变对象:2568450203016
[0]是不可变对象,装载容的对象不可变对象:140711622468432
[1]是不可变对象,装载容的对象不可变对象:140711622468464
[2]是不可变对象,装载容的对象不可变对象:140711622468496
b[0]是不可变对象,和a[0]是不可对象地址比较:140711622468432 140711622468432
b[0]是不可变对象,和a[0]是不可对象地址比较:True

3.3 再看参数传引用问题

示例1,传不可变对象:
a = 1

def ceshi(a):
    print('传入的1对象的引用的地址是:',id(a))
    print('传入的1对象的引用的地址对应值是:', a)
    a = 2
    print('内层修改我们的变量的引用地址为对象2的堆内存地址:', id(a))
    print('内层变量a的指向的对象的值是:', a)

ceshi(a)
print('外层变量a的对象引用地址是:',id(a))
print('外层变量a的指向的对象的值是:',a)

输出结果为:

传入的1对象的引用的地址是:140711622468432
传入的1对象的引用的地址对应值是:1
内层修改我们的变量的引用地址为对象2的堆内存地址:140711622468464
内层变量a的指向的对象的值是: 2
外层变量a的对象引用地址是:140711622468432
外层变量a的指向的对象的值是: 1

分析:引用传递,但是上面示例传递引用后,我们的又分配了一个2的新的堆内存的地址,创建了一个新的变量,所以此时我们的a内层的地址是执行了新创建2的对象的堆内存的地址,(但是不会影响到外层的地址1)

示例2 传可变对象(1):

a = [1]

def ceshi(a):
    print('传入的1对象的引用的地址是:',id(a))
    print('传入的1对象的引用的地址对应值是:', a)
    a.append(2)
    print('内层修改我们的变量的引用地址为对象2的堆内存地址:', id(a))
    print('内层变量a的指向的对象的值是:', a)

ceshi(a)
print('外层变量a的对象引用地址是:',id(a))
print('外层变量a的指向的对象的值是:',a)

输出结果是:

传入的1对象的引用的地址是:2310115150344
传入的1对象的引用的地址对应值是: [1]
内层修改我们的变量的引用地址为对象2的堆内存地址:2310115150344
内层变量a的指向的对象的值是: [1, 2]
外层变量a的对象引用地址是:2310115150344
外层变量a的指向的对象的值是: [1, 2]

分析:上面的传递还是我们的外部[]对象的地址,内部的append方法,操作其实是[]内容的对象,因为我们由始至终[]内容的地址其实一直没改变,变的只是里面的内部装载对象。所以也是一个引用传递!,但是由于内存对对象做了新增对象的的操作,所以我们的对应的原来里面的列表容器内的对象是的值是有变化的!

示例2 传可变对象(2):

a = [1]

def ceshi(a):
    print('传入的1对象的引用的地址是:',id(a))
    print('传入的1对象的引用的地址对应值是:', a)
    a = [1] + [2]
    print('内层修改我们的变量的引用地址为对象2的堆内存地址:', id(a))
    print('内层变量a的指向的对象的值是:', a)

ceshi(a)
print('外层变量a的对象引用地址是:',id(a))
print('外层变量a的指向的对象的值是:',a)


输出结果为:

传入的1对象的引用的地址是:3164470430344
传入的1对象的引用的地址对应值是: [1]
内层修改我们的变量的引用地址为对象2的堆内存地址:3164465840712
内层变量a的指向的对象的值是: [1, 2]
外层变量a的对象引用地址是:3164470430344
外层变量a的指向的对象的值是: [1]

分析:重点 a = [1] + [2] 其实是产生了一个新的对象[]+ [] 这个操作已经是产生了新的堆内存的分配操作,所以分配新的内存地址。

总结

其实没必要谈论是值传递还是引用传递,我们里理解其实是永远是一个引用传递。但是

  • 如果我们的传递到内部之后,涉及到新的对象的创建分配的时候,那么它内部的操作,不会影响到外部的变量指向原来的对象的值的内容

  • 如果我们的传递到内部后。不涉及新的对象的创建分配,则它的操作是对象的值,对外部的原来指向的对象的值会有影响

个人其他博客地址

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

小钟同学 | 文 【原创】| QQ:308711822

  • 1:本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。
  • 2:关于文章内容,有部分内容参考自互联网,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111698