python中函数参数的引用和传值
2017-03-14 21:00
211 查看
python核心:万物皆对象。
知识点: 传值,引用,gc(垃圾回收),可变(mutable)对象和不可变(immutable)对象。
如果大学的时候学习过C语言,可能对函数的传值和引用这一块会更好理解,本文尽可能针对所有基础的人讲的清楚一点。
声明:下面所说的 引用===传址 !==传值
在C类语言中,
但是在python中,一切传递的都是对象的引用,也可以认为是传址。
python中,对象分为可变(mutable)和不可变(immutable)两种类型,tuple,number等为不可变对象,list等对象是可变对象。
先说说内存地址和垃圾回收:
在计算机中,程序运行时,数据会存储在计算机的内存中,我们平时定义的变量
内存地址:
垃圾回收:
python的垃圾回收机制可简单理解为引用计数机制,当引用计数为0时,内存地址被回收。
相对于C类语言的好处就是,我们生声明一个变量之后,无需我们手动去释放内存,python的gc会自动帮我们处理,以便我们将更多的精力放在业务逻辑处理上,无需专门去考虑因为变量未及时释放而导致的内存泄漏。
下面说参数的引用和传值。
简单的理解上,对于最终的效果,我们都可以理解为:
1 对于不可变对象作为函数参数,相当于C系语言的值传递;
2 对于可变对象作为函数参数,相当于C系语言的引用传递。
但是这样理解从结果上理解是正确的,从原理上说,前面已经说过了,python一切对象传递的都是地址,不同的是,在函数内部转换的过程不一样。
下面举例说明:
代码解释:
在程序内部,test_list的地址和test_number的地址都传递都函数test中去。
执行.pop()操作的时候,是在原地址上进行操作,因此会改变原来的值。
执行+1操作的时候,test_number + 1 = 2,开辟了一段内部地址指向了2,局部地址(局部对象)指向了这个新对象2,原来的test_number并没有改变,因此原来的test_number并不会改变。
同理:
如果将test_list.pop()改成 test_list = [1],那么原来的test_list的地址也不会改变,即print test_lists仍然会打印出 [1,2,3]
为了加深理解,
最后附上一段多线程中生产者消费者中的引用传值和共享变量的实例:
知识点: 传值,引用,gc(垃圾回收),可变(mutable)对象和不可变(immutable)对象。
如果大学的时候学习过C语言,可能对函数的传值和引用这一块会更好理解,本文尽可能针对所有基础的人讲的清楚一点。
声明:下面所说的 引用===传址 !==传值
在C类语言中,
function test(test) { return test = test + 1; } int num = 1; test(num)//传值,不会改变num //test(&num)//引用传参,会改变test
但是在python中,一切传递的都是对象的引用,也可以认为是传址。
python中,对象分为可变(mutable)和不可变(immutable)两种类型,tuple,number等为不可变对象,list等对象是可变对象。
先说说内存地址和垃圾回收:
在计算机中,程序运行时,数据会存储在计算机的内存中,我们平时定义的变量
内存地址:
test = 1 # test与 值为1的内存中的地址绑定
垃圾回收:
test = 1 test = 2 # 将名字test与内存中值为2的内存地址绑定,不修改原来a绑定的内存中的值,此时,内存中值为1的内存地址引用计数-1,当引用计数为0时,内存地址被回收
python的垃圾回收机制可简单理解为引用计数机制,当引用计数为0时,内存地址被回收。
相对于C类语言的好处就是,我们生声明一个变量之后,无需我们手动去释放内存,python的gc会自动帮我们处理,以便我们将更多的精力放在业务逻辑处理上,无需专门去考虑因为变量未及时释放而导致的内存泄漏。
下面说参数的引用和传值。
简单的理解上,对于最终的效果,我们都可以理解为:
1 对于不可变对象作为函数参数,相当于C系语言的值传递;
2 对于可变对象作为函数参数,相当于C系语言的引用传递。
但是这样理解从结果上理解是正确的,从原理上说,前面已经说过了,python一切对象传递的都是地址,不同的是,在函数内部转换的过程不一样。
下面举例说明:
#!/usr/bin/python # coding=utf-8 def test(test_list, test_number): test_list.pop() test_number = test_number + 1 # 参数分别为list类型和number类型, 也就是一个是可变对象,一个是不可变对象 test_list = [1, 2, 3] test_number = 1 test(test_list, test_number) print test_list # 输出[1, 2] print test_number # 输出[1]
代码解释:
在程序内部,test_list的地址和test_number的地址都传递都函数test中去。
执行.pop()操作的时候,是在原地址上进行操作,因此会改变原来的值。
执行+1操作的时候,test_number + 1 = 2,开辟了一段内部地址指向了2,局部地址(局部对象)指向了这个新对象2,原来的test_number并没有改变,因此原来的test_number并不会改变。
同理:
如果将test_list.pop()改成 test_list = [1],那么原来的test_list的地址也不会改变,即print test_lists仍然会打印出 [1,2,3]
为了加深理解,
最后附上一段多线程中生产者消费者中的引用传值和共享变量的实例:
#!/usr/bin/python # coding=utf-8 import threading import time class Producer(threading.Thread): def __init__(self, queue, flag): super(Producer, self).__init__() self.queue = queue self.flag = flag def run(self): while True: length = max(self.queue) + 1 print "============================= producer queue", self.queue self.queue.append(length) print 'flag length=', len(self.flag) if len(self.flag) == 0: print "producer 我也结束了" break time.sleep(2) class Consumer(threading.Thread): def __init__(self, queue, flag): super(Consumer, self).__init__() self.queue = queue self.flag = flag def run(self): while True: try: length = len(self.queue) print "consumer queue", self.queue if length > 5: self.flag.pop() # 注意我是flag raise ValueError('over') self.queue.pop(0) except ValueError as e: # 通知生产者结束 # 如何实现? print "consumer 我结束了", e break # raise(e) time.sleep(4) queue = [1, 2, 3] flag = [0] # 表示正常 Consumer(queue, flag).start() time.sleep(1) Producer(queue, flag).start()
相关文章推荐
- Python 函数参数引用(传值/传址)/copy/deepcopy
- Python 函数参数引用(传值/传址)/copy/deepcopy
- Python 函数参数引用(传值/传址)/copy/deepcopy
- python中函数参数是引用还是传值
- Python 函数参数引用(传值/传址)/copy/deepcopy
- python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变
- Python的函数参数传递:传值?引用?
- Python的函数参数传递:传值?引用?
- Python的函数参数传递:传值?引用?
- python函数的参数传递问题---传值还是传引用?
- Python的函数参数传递:传值?引用?
- Python的函数参数传递:传值?引用?
- [zt]Python的函数参数传递:传值?引用?
- Python函数参数到底是按值还是按引用
- C++函数参数传值OR传引用_C++函数返回值返回对象OR对象引用
- python 像C语言样的函数参数引用实现..
- Python的函数参数传递:传值?引用?
- Python的函数参数传递:传值?引用?
- Python的函数参数传递:传值?引用?
- python函数参数是值传递还是引用传递(以及变量间复制后是否保持一致):取决于对象内容可变不可变