详解python函数传参传递dict/list/set等类型的问题

传参时传递可变对象,实际上传的是指向内存地址的指针/引用

这个标题是我的结论,也是我在做项目过程查到的。学过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)返回的是{},是一个不可变对象

到此这篇关于详解python函数传参传递dict/list/set等类型的问题的文章就介绍到这了,更多相关python函数传参传递内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python中的单继承与多继承实例分析

    本文实例讲述了Python中的单继承与多继承.分享给大家供大家参考,具体如下: 单继承 一.介绍 Python 同样支持类的继承,如果一种语言不支持继承,类就没有什么意义.派生类的定义如下所示: class DerivedClassName(BaseClassName1): <statement-1> . . . <statement-N> 需要注意圆括号中基类的顺序,若是基类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找基类

  • python类的继承链实例分析

    1.子类可以继承父类,同样,父类也可以继承自己的父类,一层一层地继承. class A: def have(self): print('I hava an apple') class B(A): pass class C(B): pass 2.如果想判断一个类别是否是另一个类别的子类别,可以使用内置函数issubclass(). >>> issubclass(C, A) True >>> issubclass(B, A) True >>> issubc

  • 浅谈Python类的单继承相关知识

    一.类的继承 面向对象三要素之一,继承Inheritance 人类和猫类都继承自动物类. 个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性. 在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样就可以减少代码.多服用.子类可以定义自己的属性和方法 class Animal: def __init__(self,name): self._name = name def shout(self): print("{} shouts".format(self.__c

  • python利用K-Means算法实现对数据的聚类案例详解

    目的是为了检测出采集数据中的异常值.所以很明确,这种情况下的簇为2:正常数据和异常数据两大类 1.安装相应的库 import matplotlib.pyplot as plt # 用于可视化 from sklearn.cluster import KMeans # 用于聚类 import pandas as pd # 用于读取文件 2.实现聚类 2.1 读取数据并可视化 # 读取本地数据文件 df = pd.read_excel("../data/output3.xls", heade

  • python调用stitcher类自动实现多个图像拼接融合功能

    使用stitcher需要注意,图像太大会报错而且计算慢. 特点和适用范围:图像需有足够重合相同特征区域. 优点:适应部分倾斜/尺度变换和畸变情形,拼接效果好,使用简单,可以一次拼接多张图片. 缺点:需要有足够的相同特征区域进行匹配,速度较慢(和图像大小有关). 原图(可下载) 代码(两张图片拼接) import sys import cv2 if __name__ == "__main__": img1 = cv2.imread('C:/Users/Guaguan/Desktop/im

  • python实现批量提取指定文件夹下同类型文件

    本文通过实例为大家分享了python实现批量提取指定文件夹下同类型文件,供大家参考,具体内容如下 代码 import os import shutil def take_samefile(or_path, tar_path, tar_type): tar_path = tar_path if not os.path.exists(tar_path): os.makedirs(tar_path) path = or_path files = os.listdir(path) # 读取or_path

  • 详细总结Python类的多继承知识

    一.Python不同版本的类 Python2.2之前是没有共同的祖先的,之后引入Object类,它是所有类的共同祖先类Object Python2中为了兼容,分为古典类(旧式类)和新式类 Python3中全部都是新式类 新式类都是继承自Object的,新式类可以使用super #古典类在python2.x中运行 class A: pass print(dir(A)) # ['__doc__', '__module__'] print(A.__bases__) # () a = A() print

  • python 基于空间相似度的K-means轨迹聚类的实现

    这里分享一些轨迹聚类的基本方法,涉及轨迹距离的定义.kmeans聚类应用. 需要使用的python库如下 import pandas as pd import numpy as np import random import os import matplotlib.pyplot as plt import seaborn as sns from scipy.spatial.distance import cdist from itertools import combinations from

  • Python绘制分类图的方法

    前言 遥感影像分类图一般为特定数值对应一类地物,用Python绘制时,主要在颜色的映射和对应的图例生成. plt.matplotlib.colors.ListedColormap支持自定义颜色.matplotlib.patches mpatches对象可以生成一个矩形对象,控制其颜色和地物类型的颜色对应就可以生成地物分类的图例了.具体用法可以自行Google和百度.下面给出一个模拟地物分类数据的可视化例子. 代码 import numpy as np import matplotlib.pypl

  • python-pandas创建Series数据类型的操作

    1.什么是pandas 2.查看pandas版本信息 print(pd.__version__) 输出: 0.24.1 3.常见数据类型 常见的数据类型: - 一维: Series - 二维: DataFrame - 三维: Panel - - 四维: Panel4D - - N维: PanelND - 4.pandas创建Series数据类型对象 1). 通过列表创建Series对象 array = ["粉条", "粉丝", "粉带"] # 如

  • Python中的类对象示例详解

    抽象特点 Python 一切皆对象,基于此概念,对 类 class 有以下特点: 类与实例的属性 类对象创建可选择定义类属性,创建实例对象时,实例属性自动执行类的__init__方法初始化 实例对象自动继承相应的类属性(如果有),但实例属性优先级更高 实例方法,类方法,静态方法的参数 实例方法是一般函数但实例方法需要传入self参数(与一般函数的区别) 类方法和静态方法是通过装饰器实现的函数,类方法需要传入cls参数,静态方法无需传入self参数或者是cls参数(但也能传入参数) 其中self参

  • Python进阶学习之带你探寻Python类的鼻祖-元类

    Python是一门面向对象的语言,所以Python中数字.字符串.列表.集合.字典.函数.类等都是对象. 利用 type() 来查看Python中的各对象类型 In [11]: # 数字 In [12]: type(10) Out[12]: int In [13]: type(3.1415926) Out[13]: float In [14]: # 字符串 In [15]: type('a') Out[15]: str In [16]: type("abc") Out[16]: str

随机推荐