关于python传参引发的一些思考

人总有不会的,遇到一些问题深究下去必定有所收获

这个问题是在我写python爬虫项目的时候的疑问,可能是我太菜了(以前没学透彻),也可能是上学期学Java的时候按值传递的特点给搞混了,因为当时在用多线程的生产者消费者问题处理资源队列,参考别人代码的时候突然蒙了一下,但后来查了查资料发现原来是下面的原因,值得记录一下坑点,顺便当复习,对语言有个更深入的理解也挺好的

前置的一些知识
  1. 在python里面一切皆为对象,而这个对象分成两种类型,第一种是可变的,另外一种是不可变的。

  2. 按值传递:会在堆中建立一个新的副本,以后操作只对副本操作,对原来主函数里面的值不影响。

    按引用传递:会在堆中建立一个地址的引用,也就是参数的地址,一旦改变这个值就会把主函数里面的变量也会改变。

做一些验证

这里我以参考的代码里面的一部分进行验证,这里用类去类比一下函数,一样的效果,通过id参数打印一下地址

class Consumer(object):
    def __init__(self,page_queue,*args,**kwargs):
        super(Consumer, self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        print(id(self.page_queue))
        
def main():
	page_queue = 1
	print(id(page_queue))
	c = Consumer(page_queue)
    
if __name__ == '__main__':
    main()

输出结果是这样的

140722209422880
140722209422880

可以发现两处的地址是一样的,可以脑补一下图应该是这样的,好像是引用传值,到底是不是这样的呢?

AO7U9f.png

再来一段代码验证

class Consumer(object):
    def __init__(self,page_queue,*args,**kwargs):
        super(Consumer, self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        print(id(self.page_queue))
        self.page_queue += 1
        print(id(self.page_queue))
        print(id(page_queue),page_queue)
        
def main():
	page_queue = 1
	print(id(page_queue))
	c = Consumer(page_queue)

    
if __name__ == '__main__':
    main()       

结果是这个样子的

140722209422880
140722209422880
140722209422912
140722209422880 1

可以发现,以本来引用的常规思路去看的话,这样的操作应该会对同一个地址的东西修改了才对,你会发现,他重新开辟了一个新的空间去容纳新的值,原来传进去的参数没有存在任何影响,脑补一下这个图,现在变成了这样,跟平常的引用是不是有点不一样。

AOHK5q.png

再来看这样一段代码,以队列为例,然后对传进的队列做修改,再观察一下地址内容的改变,查看其是否为空

# -*- coding: utf-8 -*-
# Author:0verWatch

from queue import Queue

class Consumer(object):
    def __init__(self,page_queue,*args,**kwargs):
        super(Consumer, self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        self.page_queue.put(1)   #增加一个值
        print(id(self.page_queue))
        print(id(page_queue),page_queue.empty())

def main():
    page_queue = Queue(100)
    print(id(page_queue))
    print(page_queue.empty())
    c = Consumer(page_queue)

if __name__ == '__main__':
    main()

输出的结果是这个样子的

1519902231520
True
1519902231520
1519902231520 False

可以发现值变化了,地址却没发生变化,明显的引用传参的例子

自己的小结

这里就可以对照一下上面为什么说python对象有两种类型,一种是可变的,另外一种是不可变的,因为在python这个语言中,对于不可变对象的传参例如(tuple,数字,字符)他们一旦发生改变,就会重新在堆里面分配你一块空间,去给变化的值,这也在宏观上给人一种按值传递的错觉,但是这样的机制也优化了python的运行,对于可变的对象的传参例如(list,dict,还有上面提及到的queue类)相当于通过按引用来传递对象。

写代码的时候才发现自己有多菜2333333,大佬们请忽略

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值