详解python函数传参传递dict/list/set等类型的问题
作者:aphoneringing 发布时间:2021-09-29 16:12:38
传参时传递可变对象,实际上传的是指向内存地址的指针/引用
这个标题是我的结论,也是我在做项目过程查到的。学过C的都知道,函数传参可以传值,也可以传指针。指针的好处此处不再赘述。
先上代码看看效果:
def trans(var):
return var
source = {1: 1}
dist = trans(source)
source[2] = 2
print(source)
print(dist)
运行结果:
{1: 1, 2:2}
{1: 1, 2:2}
可以看到改变了source时,dist也跟着改变了。原因就是source是可变对象,传递参数时,传的是其引用(C的指针)。dist和source都指向了同一片内存空间。在运行source[2] = 2时,是对内存空间的数据的变更,所以dist也跟着变化。
有什么作用呢?场景应该很多,不过本人资历尚浅,想不到典型场景,就拿自己的项目举例。
项目中我定义了一个类,这个类用来读写配置,预存一些json配置,客户端可以读取配置,当预存的配置不包含客户端读取的配置时,就从设备读取。
我需要这个类实例化出多个对象,对应多个客户端。但我希望预存的配置可以是公共的,这样对于陌生配置,不用所有的客户端请求时,都需要从设备读取。
一开始我是这么写的:
global dataset
dataset = {}
class Config(object):
def __init__(self, device_url):
self.device_url = device_url
def get_config(self, key):
global dataset
if key in dataset:
return dataset.get(key)
else:
# 通过device_url从设备获取配置,假如赋值给了value
dataset[key] = value
return value
def other_func(self):
# 其他函数,跟device_url有关
pass
而后来我需要多份公共配置,甚至要达到1000份以上,显然全局变量并不能很好满足。因为要共用内存,所以我传递可变对象,把代码改成了这样:
class Config(object):
def __init__(self, dataset, device_url): # 传递可变对象dataset
self.dataset = dataset
self.device_url = device_url
def get_config(self, key):
if key in self.dataset:
return self.dataset.get(key)
else:
# 通过device_url从设备获取配置,假如赋值给了value
self.dataset[key] = value # 可变对象dataset赋值,其他实例化的dataset属性值也会变化
return value
def other_func(self):
# 其他函数,跟device_url有关
pass
列表、字典、集合不一定是可变对象
网上有一堆资料说列表、字典、集合是可变对象,这句话不完全正确。{} [] set((, ))常量不是可变对象。
上述的Config类,如果实例化时传递{},就不能共享配置。
config1 = Config({})
config2 = Config({})
config1.dataset[1] = 1
print(repr(config1.dataset))
print(repr(config2,dataset))
上述运行结果是
'{1: 1}'
'None'
但如果是这样
share_var = {}
config1 = Config(share_var)
config2 = Config(share_var)
config1.dataset[1] = 1
print(repr(config1.dataset))
print(repr(config2,dataset))
运行结果就会变成:
'{1: 1}'
'{1: 1}'
share_var是可变对象,然而{}是不可变对象,虽然share_var和{}的值一样。
要往更深层次地理解,就需要理解python的命名空间了。
传参和传递可变对象参数需要注意的事情
如果不是要传引用/指针,去操作对应的内存空间,则传参时注意不要传字典、列表、集合、类或类的实例化对象等类型
传递可变对象参数时,注意不要传常量{} [] set((, )),最好是在传参前付给一个变量,传参时传这个变量。
懂了原理可能不至于直接传常量,但是有可能出现下面这种情况:
def func1(mutable_object, flag):
if flag:
return mutable_object
else:
return {}
def func2(mutable_object):
# something to do with mutable_object
pass
func2(func1(mutable_object, False)) # 此处func1(mutable_object, False)返回的是{},是一个不可变对象
来源:https://blog.csdn.net/aphoneringing/article/details/115387735
猜你喜欢
- 一:代码实现class TreeNode: """节点类""&q
- MySQL默认编码是latin1,因业务需要把它转到UTF8。1、导出数据导出表结构 mysqldump -d dataname >
- 1、、软件环境:Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-32-generic x86_64)MySQL 5
- 一、基本结构语句一)、条件语句age = int(input("请输入你家狗狗的年龄: "))print("&
- 通常我们用 Python 绘制的都是二维平面图,但有时也需要绘制三维场景图,比如像下面这样的:这些图怎么做出来呢?今天就来分享下如何一步步绘
- PDOStatement::fetchPDOStatement::fetch — 从结果集中获取下一行(PHP 5 >= 5.1.0,
- 备注1:解决连接MySQL数据库很慢的问题vim /etc/my.cnf添加内容:skip-name-resolve,重启数据库。 
- 一,封装封装是面向对象编程思想的重要特征之一。(一)什么是封装封装是一个抽象对象的过程,它容纳了对象的属性和行为实现细节,并以此对外提供公共
- for循环是一个循环控制结构,可以有效地编写需要执行的特定次数的循环。语法for循环在Go编程语言中的语法是:for [condition
- 简介 Closure 所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分
- 在使用图片的时候有时候我们希望改变背景颜色,这样就只关注于图片本身。比如在连连看中就只有图片,而没有背景,其实我个人感觉有背景好看一点。两种
- DataSet是tensorflow 1.3版本推出的一个high-level的api,在1.3版本还只是处于测试阶段,1.4版本已经正式推
- 一.概念简介 脚本:script是使用一种特定的描述性语言,依据一定的格式编写的可执行文件,又称作宏或批处理文件。 二.背景 近来在Wind
- 主要代码是参考:https://github.com/SoulDGXu/NLPVisualizationSystem/tree/master
- [1]定义:正则又叫规则或模式,是一个强大的字符串匹配工具,在javascript中是一个对象[2]特性:[2.1]贪婪性,匹配最长的[2.
- 大家都知道在Dreamwerver中可以很方便地实现记录集的分页显示,但是生成的代码的确很庞大,影响了网页的显示速度,看起来条理也不是很清晰
- 1、工作流程步骤(1)用spawn来执行一个程序;(2)用expect方法来等待指定的关键字,这个关键字是被执行的程序打印到标准输出上面的;
- Python来进行查询和替换一个文本字符串?可以使用sub()方法来进行查询和替换,sub方法的格式为:sub(replacement, s
- 在学习asp过程中相信很多初学者对Sub与Function的用法有些疑惑,好像它们没什么区别都可以使用。呵呵,看了本文的介绍您就可以了解了S
- JavaScript图片水平翻转后垂直翻反转的特效一:<!--把下列代码加到body区域内--><SCRIPT langu