关于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

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

关于python传参引发的一些思考_第1张图片

再来一段代码验证

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

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

关于python传参引发的一些思考_第2张图片

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

# -*- 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,大佬们请忽略

你可能感兴趣的:(python,编程语言,python)