详解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

标签:python,函数,传参,传递
0
投稿

猜你喜欢

  • Python大数据之使用lxml库解析html网页文件示例

    2022-05-10 11:06:53
  • Python编程对列表中字典元素进行排序的方法详解

    2023-11-23 04:48:26
  • 关于MySQL编码问题的经验总结

    2007-08-23 16:10:00
  • django框架model orM使用字典作为参数,保存数据的方法分析

    2021-03-11 00:18:30
  • Mootools 1.2教程(16)——排序类和方法简介

    2008-12-10 14:18:00
  • Javascript语法检查插件 jsLint for Vim

    2009-03-11 16:37:00
  • JavaScript的私有成员

    2009-03-25 20:45:00
  • 解决Pandas的DataFrame输出截断和省略的问题

    2021-10-28 10:22:19
  • pip/anaconda修改镜像源,加快python模块安装速度的操作

    2022-06-01 10:42:26
  • PHP中让json_encode不自动转义斜杠“/”的方法

    2023-07-12 22:44:58
  • Python实现快速排序算法及去重的快速排序的简单示例

    2021-06-02 19:58:09
  • 详解Python import方法引入模块的实例

    2021-01-10 04:21:08
  • python实现LBP方法提取图像纹理特征实现分类的步骤

    2023-05-24 02:12:27
  • ASPError(err)对象的相关基础知识

    2008-03-24 20:23:00
  • 使用CSS选择器创建个性化链接样式

    2009-06-02 13:07:00
  • Linux安装卸载Mysql数据库

    2011-01-29 16:45:00
  • php strftime函数的详细用法

    2023-06-07 19:09:37
  • Python实现删除列表中满足一定条件的元素示例

    2023-11-07 14:38:39
  • python入门for循环嵌套理解学习

    2021-03-01 21:42:16
  • ASP生成静态模版技术(带参数的标签)

    2009-03-03 12:29:00
  • asp之家 网络编程 m.aspxhome.com