python中可变和不可变对象(复值,拷贝,函数值传递)

2024-06-13 20:48

本文主要是介绍python中可变和不可变对象(复值,拷贝,函数值传递),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

python中有可变对象和不可变对象,

可变对象: list, dict.

不可变对象有: int, string, float, tuple.

python不可变对象

int, string, float, tuple

先来看一个例子

def int_test(): 

    i = 77

    j = 77

    print(id(77))                    #140396579590760

    print('i id:' + str(id(i)))      #i id:140396579590760

    print('j id:' + str(id(j)))      #j id:140396579590760

    print i is j                     #True

    j = j + 1

    print('new i id:' + str(id(i)))  #new i id:140396579590760

    print('new j id:' + str(id(j)))  #new j id:140396579590736

    print i is j                     #False

if __name__ == '__main__':

    int_test()

有 i 和 j 俩个变量的值为77,通过打印 77的 ID 和变量 i,j 在内存中的 ID 我们得知它们都是指向同一块内存。所以说 i 和 j 都是指向同一个对象的。然后我们修改j的值,让 j 的值 +1.按道理 j 修改之后应该i的值也发生改变的,因为它们都是指向的同一块内存,但结果是并没有。因为 int类型是不可变类型,所有其实是 j 复制了一份到新的内存地址然后+1,然后 j 又指向了新的地址。所以j的内存 id 发生了变化。

内存分配情况如下:

python可变对象

dict,list

def dict_test():

    a = {}

    b = a

    print(id(a))

    a['a'] = 'hhhh'

    print('id a:' + str(id(a)))

    print('a:' + str(a))

    print('id b:' + str(id(b)))

    print('b:' + str(b))

if __name__ == '__main__':

    dict_test()

运行结果如下:  

  140367329543360

  id a:140367329543360

  a:{'a': 'hhhh'}

  id b:140367329543360

  b:{'a': 'hhhh'}

可以看到 a 最早的内存地址 id 是140367329543360 然后把 a 赋值给 b 其实就是让变量 b 的也指向 a 所指向的内存空间。然后我们发现当 a 发生变化后, b 也跟着发生变化了,因为 list 是可变类型,所以并不会复制一份再改变,而是直接在 a 所指向的内存空间修改数据,而 b 也是指向该内存空间的,自然 b 也就跟着改变了。

内存变化如下:

python函数的参数传递

由于python规定参数传递都是传递引用,也就是传递给函数的是原变量实际所指向的内存空间,修改的时候就会根据该引用的指向去修改该内存中的内容,所以按道理说我们在函数内改变了传递过来的参数的值的话,原来外部的变量也应该受到影响。但是上面我们说到了python中有可变类型和不可变类型,这样的话,当传过来的是可变类型 (list, dict)时,我们在函数内部修改就会影响函数外部的变量。而传入的是不可变类型时在函数内部修改改变量并不会影响函数外部的变量,因为修改的时候会先复制一份再修改。下面通过代码证明一下:

def test(a_int, b_list):

    a_int = a_int + 1

    b_list.append('13')

    print('inner a_int:' + str(a_int))

    print('inner b_list:' + str(b_list))

if __name__ == '__main__':

    a_int = 5

    b_list = [10, 11]

    test(a_int, b_list)

    print('outer a_int:' + str(a_int))

    print('outer b_list:' + str(b_list))

运行结果如下:

inner a_int:6

inner b_list:[10, 11, '13']

outer a_int:5

outer b_list:[10, 11, '13']

好啦!答案显而易见啦,经过 test() 方法修改后,传递过来的 int 类型外部变量没有发生改变,而 list 这种可变类型则因为 test() 方法的影响导致内容发生了改变。

总结:

在很多的其他语言中在传递参数的时候允许程序员选择值传递还是引用传递(比如c语言加上*号传递指针就是引用传递,而直接传递变量名就是值传递),而python只允许使用引用传递,但是它加上了可变类型和不可变类型,让我们感觉有点混乱了。听说python只允许引用传递是为方便内存管理,因为python使用的内存回收机制是计数器回收,就是每块内存上有一个计数器,表示当前有多少个对象指向该内存。每当一个变量不再使用时,就让该计数器-1,有新对象指向该内存时就让计数器+1,当计时器为0时,就可以收回这块内存了。当然我觉得它肯定不止用了计数器吧,应该还有其他的技术,比如分代回收什么的。不再讨论之列,就这样了...

ps:

值传递:表示传递直接传递变量的值,把传递过来的变量的值复制到形参中,这样在函数内部的操作不会影响到外部的变量

引用传递:我个人觉得可以把引用理解为一个箭头,这个箭头指向某块内存地址,而引用传递,传递过来的就是这个箭头,当你修改内容的时候,就是修改这个箭头所指向的内存地址中的内容,因为外部也是指向这个内存中的内容的,所以,在函数内部修改就会影响函数外部的内容。

原文链接 http://www.jianshu.com/p/c5582e23b26c

这篇关于python中可变和不可变对象(复值,拷贝,函数值传递)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1058388

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

Python 字符串占位

在Python中,可以使用字符串的格式化方法来实现字符串的占位。常见的方法有百分号操作符 % 以及 str.format() 方法 百分号操作符 % name = "张三"age = 20message = "我叫%s,今年%d岁。" % (name, age)print(message) # 我叫张三,今年20岁。 str.format() 方法 name = "张三"age

java中查看函数运行时间和cpu运行时间

android开发调查性能问题中有一个现象,函数的运行时间远低于cpu执行时间,因为函数运行期间线程可能包含等待操作。native层可以查看实际的cpu执行时间和函数执行时间。在java中如何实现? 借助AI得到了答案 import java.lang.management.ManagementFactory;import java.lang.management.Threa

一道经典Python程序样例带你飞速掌握Python的字典和列表

Python中的列表(list)和字典(dict)是两种常用的数据结构,它们在数据组织和存储方面有很大的不同。 列表(List) 列表是Python中的一种有序集合,可以随时添加和删除其中的元素。列表中的元素可以是任何数据类型,包括数字、字符串、其他列表等。列表使用方括号[]表示,元素之间用逗号,分隔。 定义和使用 # 定义一个列表 fruits = ['apple', 'banana

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

SQL Server中,isnull()函数以及null的用法

SQL Serve中的isnull()函数:          isnull(value1,value2)         1、value1与value2的数据类型必须一致。         2、如果value1的值不为null,结果返回value1。         3、如果value1为null,结果返回vaule2的值。vaule2是你设定的值。        如

Windows 可变刷新率是什么?如何开启?

在现代计算设备中,显示屏的刷新率对用户体验起着至关重要的作用。随着显示技术的不断进步,固定刷新率显示器逐渐被支持可变刷新率(Variable Refresh Rate, VRR)技术的显示器所取代。 可变刷新率定义 可变刷新率是什么?可变刷新率(VRR)是一种显示技术,它允许显示器的刷新率动态调整,以匹配显卡输出的帧率。传统的显示器通常具有固定的刷新率(如60Hz、75Hz等),这意味着显示器

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python

python 喷泉码

因为要完成毕业设计,毕业设计做的是数据分发与传输的东西。在网络中数据容易丢失,所以我用fountain code做所发送数据包的数据恢复。fountain code属于有限域编码的一部分,有很广泛的应用。 我们日常生活中使用的二维码,就用到foutain code做数据恢复。你遮住二维码的四分之一,用手机的相机也照样能识别。你遮住的四分之一就相当于丢失的数据包。 为了实现并理解foutain