目录

    • (1)Python变量不能独立存在
    • (2)变量是内存中数据的引用
  • 3、Python的参数传递(重点)

1、Python的变量

(1)Python变量不能独立存在

  • 比如在 C++ 等语言中,变量的声明和赋值是可以分开的。

    int a;
    a=343;
  • 而在Python中却不行,在声明Python变量的同时必须进行赋值操作,否则会报错。
    Python Console: starting.
    Python 3.7.7
    >>> a
    Traceback (most recent call last):File "<input>", line 1, in <module>
    NameError: name 'a' is not defined
    >>> a = 343>>> a
    343

    如果你直接使用一个不存在的变量,就会发生错误: NameError: name 'a' is not defined 。

(2)变量是内存中数据的引用

a = 343 这样代码被执行时,首先要在内存中创建出 343 这个对象,然后让 a 指向它,这便是 引用 。

此后,我们在程序中使用变量 a 时,其实都是在使用 343 ,Python可以通过 a 找到 343 这个值,这是对引用最通俗的解释。

如下图所示:

(3)注意点

赋值语句执行过程中,有一点极容易被忽略掉,那就是这个过程中,在内存中创建了新的数据的问题。

a = [1]
b = [1]print(a == b)
Trueprint(a is b)
False

两行赋值语句,分别将列表 [1] 赋值给 a 和 b ,表达式 a==b 的结果是 Ture ,因为他们的内容的确相同,但表达式 a is b 的结果是 False ,因为这两行赋值语句执行过程中,一共创建了两个列表,他们只是内容相同而已,但内存地址绝对不相同,下图是这两个变量的内存描述示意图。

2、了解变量的引用

在Python中,变量的值是靠引用来传递来的。

我们可以用 id() 函数来判断两个变量是否引用的同一个值。

id() 函数返回对象的唯一标识符,标识符是一个整数,是对象的内存地址。如果两个变量的内存地址相同,说明两个变量引用的是同一个地址。

看下面综合示例:

# 1. int类型
"""
声明变量保存整型数据,把这个数据赋值到另一个变量;
id()检测两个变量的id值(内存的十进制值)
"""
a = 1
b = aprint(b)  # 打印变量b的值为1# id(a):返回a变量在内存中的十进制地址
# 变量a和变量b的内存地址一样,说明ab引用的是同一数据。
print(id(a))  # 140708464157520
print(id(b))  # 140708464157520# 将变量a重新赋值
a = 2
print(b)  # 打印变量b的值为1# 因为修改了a的数据,内存要开辟另外一份内存取存储2,
# id检测a和b的地址不同
print(id(a))  # 140708464157552,此时得到是的数据2的内存地址
print(id(b))  # 140708464157520# 2. 列表(可变数据类型)
aa = [10, 20]
bb = aa# 发现a和b的id值相同的,说明引用的是同一个内存地址
print(id(aa))  # 2325297783432
print(id(bb))  # 2325297783432# 变量aa添加数据
aa.append(30)
print(bb)  # 变量bb的值为[10, 20, 30], 列表为可变类型# 打印结果
print(id(aa))  # 2325297783432
print(id(bb))  # 2325297783432# 继续操作
bb = bb + [666]
print(aa) # [10, 20, 30]
print(bb) # [10, 20, 30, 666]print(id(aa)) # 30233096
print(id(bb)) # 40054280
# 这时我们会发现可变类型变量的引用也会发生改变,
# 这是为什么呢?这里就不解释了,下面一点进行详细说明。

3、Python的参数传递(重点)

Python的参数传递也就是Python变量的引用。

在Python中,所有的变量都是指向某一个地址,变量本身不保存数据,而数据是保存在内存中的一个地址上。

通俗的来说,在 Python 中,变量的名字类似于把便签纸贴在数据上。

我看网上很多文章说:

  • Python基本的参数传递机制有两种:值传递和引用传递。
  • 不可变参数是值传递,可变参数是引用传递。

这样理解也是可以的,但我认为都是引用的传递,下面我用示例说明一下。

先来说明一个知识点,在Python中, 不可变数据类型 和 可变数据类型 是如何分配内存地址的。

如下所示:

inta = 10086
intb = 10086
lista = [10,20,30]
listb = [10,20,30]print('inta的内存地址',id(inta))
print('intb的内存地址',id(intb))
print('lista的内存地址',id(lista))
print('listb的内存地址',id(listb))"""
运行结果如下:
inta的内存地址 37930032
intb的内存地址 37930032
lista的内存地址 30233096
listb的内存地址 30233608
"""

我们可以看到:

  • 对于不可变数据类型变量,相同的值是共用内存地址的。(这一点很重要)
  • 对于可变数据类型变量,如上面的 lista 和 listb ,即使内容一样,Python也会给它们分配不同的内存地址。

(1)示例

下面来看一下示例:

提示:对不可变数据类型, + 和 += 都会创建新对象,对可变数据类型来说, += 不会创建新对象。

1)可变数据类型变量示例

我们通过示例来看看可变数据类型变量的引用是如何传递的。

def ChangeParam(paramList):paramList.append([1, 2, 3, 4])print("函数内paramList状态1,取值: ", paramList)print('函数内paramList状态1的内存地址:', id(paramList))paramList += [888]print("函数内paramList状态2,取值: ", paramList)print('函数内paramList状态2的内存地址:', id(paramList))paramList = paramList + [888]print("函数内paramList状态3,取值: ", paramList)print('函数内paramList状态3的内存地址:', id(paramList))returnmylist = [10, 20, 30]
print('mylist函数外的内存地址(前):', id(mylist))ChangeParam(mylist)
print("函数外取值: ", mylist)
print('mylist函数外的内存地址(后):', id(mylist))"""
mylist函数外的内存地址(前): 32264712
函数内paramList状态1,取值:  [10, 20, 30, [1, 2, 3, 4]]
函数内paramList状态1的内存地址: 32264712
函数内paramList状态2,取值:  [10, 20, 30, [1, 2, 3, 4], 888]
函数内paramList状态2的内存地址: 32264712
函数内paramList状态3,取值:  [10, 20, 30, [1, 2, 3, 4], 888, 888]
函数内paramList状态3的内存地址: 42905160
函数外取值:  [10, 20, 30, [1, 2, 3, 4], 888]
mylist函数外的内存地址(后): 32264712
"""

2)不可变数据类型变量示例

我们通过示例来看看不可变数据类型变量的引用是否是值传递。

示例如下:

def NoChangeParam(prarmInt):print('函数中变量prarmInt的初始状态,prarmInt变量的值', prarmInt)print('函数中变量prarmInt的初始状态,prarmInt的内存地址', id(prarmInt))prarmInt += prarmIntprint('函数中状态1,此时prarmInt的值:', prarmInt)print('函数中状态1,prarmInt的内存地址:', id(prarmInt))# 1.定义变量a
a = 1000
print('执行函数前,变量a的内存地址:', id(a))# 2.调用函数
NoChangeParam(a)# 3.打印执行函数后,a变量的值和指向内存地址
print('执行函数后,a变量的值。a =', a)
print('执行函数后,a的内存地址:', id(a))"""
执行函数前,变量a的内存地址: 32817968
函数中变量prarmInt的初始状态,prarmInt变量的值 1000
函数中变量prarmInt的初始状态,prarmInt的内存地址 32817968
函数中状态1,此时prarmInt的值: 2000
函数中状态1,prarmInt的内存地址: 32818224
执行函数后,a变量的值。a = 1000
执行函数后,a的内存地址: 32817968
"""

(2)结论

通过上面示例我们可以看到:

  • 在函数执行前后,不可变数据类型变量和可变数据类型变量的所指向的地址都没有发生改变。只不过不可变数据类型变量的值没有改变,而可变数据类型变量的值发生了改变。
  • 不可变数据类型变量和可变数据类型变量,在传入函数的最开始的状态,都和原变量一致, 说明函数的参数传递是地址传递 。
  • 不可变数据类型变量和可变数据类型变量,在函数中只要产生了新对象,内存引用地址都会发生改变。
    也就是说:

    mylist = mylist + [888]
    

(3)总结

通过上面的内容,我们可以知道:

  • 对于不可变类型变量而言:因为不可变类型变量特性,修改变量需要新创建一个对象,形参的标签转而指向新对象,而实参没有变。
  • 对于可变类型变量而言,因为可变类型变量特性,直接在原对象上修改,因为此时形参和实参都是指向同一个对象,所以实参指向的对象自然就被修改了。而如果可变类型变量在函数内的操作创建了新的对象,内存地址的引用也会发生改变,但仅限于在函数内。

(4)补充(重点)

感觉以上的话很啰嗦,在最后整理一下。

看下面例子:

# 交换函数
def swap(a, b):# 下面代码实现a、b变量的值交换a, b = b, aprint("swap函数里,a的值是", a, ";b的值是", b)a = 777b = 999print("swap函数里第二次打印,a的值是", a, ";b的值是", b)a = 666
b = 888
swap(a, b)
print("函数交换结束后,变量a的值是", a, ";变量b的值是", b)"""
swap函数里,a的值是 888 ;b的值是 666
swap函数里第二次打印,a的值是 777 ;b的值是 999
函数交换结束后,变量a的值是 666 ;变量b的值是 888
"""

1)第一步,执行 swap(a, b) 函数

swap

如下图所示:

2)第二步, swap(a, b) 函数内进行了形参a和形参b的值交换。

也就时执行了 a, b = b, a 命令。

  • 形参a和形参b的值进行了交换,因为内存中就有这两个值,所以只是内存地址的引用交互了一下。
  • 之后就执行了打印命令,显示"swap函数里,a的值是 888 ;b的值是 666"

如下图所示:

提示:形参a和b就时给函数内的变量起一个名,用于区分。这里说明一下,因为我这样的描述不是很准确。

3)第三步,继续给形参a和b赋予新的值。

  • 也就是模拟产生新的对象,并指引到新对象的内存地址上。
  • 执行了 a = 777 和 b = 999 ,打印结果为“swap函数里第二次打印,a的值是 777 ;b的值是 999”。

如下图所示:

4)第四步, swap(a, b) 函数执行完毕。

swap(a, b)

如下图所示:

5)总结:

所以对于不可变数据类型变量的参数传递,执行外表上看,好像只传递了数值,其实通过上面的例子弹道,也进行了引用地址的传递。

上面使用了不可变数据类型变量进行了示例,可变数据类型变量是一样的,只不过修改变量的内容,地址是不发生改变的。但产生了新的对象,内存地址的引用会到新的对象上,和不可变数据类型变量是一样的。

最后我觉得到现在再来讨论Python中参数的传递是值传递还是引用传递,就会发现在Python里讨论这个确实是没有意义。

Python变量和参数传递相关推荐

  1. CrazyWing:Python自动化运维开发实战 四、Python变量

    导语: 1.什么是变量 2.变量名称的命名 3.变量赋值 4.变量存储数据的方式 5.引用计数 什么是变量: python学习过程中会用到许多数据,那为了方便操作,需要把这些数据分别用一个简单的名字代 ...

  2. python变量定义类型_03_python的数据类型和变量的定义及使用

    1.数据类型按数值非数值分类 (1)python的数据类型可以分为 数字型 和 非数字型. 数字型 整型 (int) 浮点型(float) 布尔型(bool) 复数型 (complex)(主要用于科学 ...

  3. Python的函数参数传递:传值?引用?

    From: http://blog.csdn.net/winterttr/article/details/2590741 作者:winterTTr (转载请注明) 我想,这个标题或许是很多初学者的问题 ...

  4. python变量赋值

    赋值 python变量赋值传递的都是引用,变量赋新对象相当于将当前变量名的标签从原变量上撕下来,贴到新的对象上. a = [1, 2] b = a 此时a = [1, 2] , b = [1, 2] ...

  5. python:函数的参数传递方法演示

    """ 函数的参数传递方法演示 """ #1.位置传参 def normal(a, b, c):print("1.位置传参:&qu ...

  6. 【Python学习教程】Python变量类型和运算符

    文章目录 Python变量的定义和使用 Python 变量的赋值 Python 变量的使用 Python 是弱类型的语言 Python整数类型(int)详解 关于 Python 2.x 整数的不同进制 ...

  7. Python 变量作用域与函数(4)

    Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...

  8. Python变量与注释高级用法

    Python变量与注释高级用法 1.概述 好的变量和注释并非为计算机而写,而是为每个阅读代码的人而写.变量与注释是表达作者思想的基础,他们对代码质量的贡献母庸质疑. 2.变量 2.1.变量解包 1.什 ...

  9. MATLAB 数组作为 Python 变量

    目录 在 Python 中创建 MATLAB 数组 Python 中的 MATLAB 数组属性和方法 Python 中的多维 MATLAB 数组 在 Python 中对 MATLAB 数组进行索引 在 ...

最新文章

  1. applicationcontext添加配置_Spring源码分析2 — spring XML配置文件的解析流程
  2. 学习笔记(十五)——MySQL(查询)
  3. MVC命名空间中的~UrlHelper中的Action方法告诉我们方法重载的重要性(路由的统一)...
  4. 人工智能实战小程序之语音_前端开发
  5. 按键精灵易语言c,求助(把按键精灵的源码转为易语言的)
  6. escape mysql_mysql_escape_string()函数用法分析
  7. fcm 服务器接入 golang_Golang 写的即时通讯服务器 im(服务组件形式)
  8. linux cmake 编译项目,使用CMake构建复杂工程
  9. Json学习总结(9)——放弃FastJson!Jackson的功能原来如此之牛
  10. 菜单响应函数:ON_COMMAND和ON_UPDATE_COMMAND_UI,ON_COMMAND_RANGE和ON_UPDATE_COMMAND_UI_RANGE
  11. python爬虫今日头条街拍美图开发背景_【Python3网络爬虫开发实战】6.4-分析Ajax爬取今日头条街拍美图...
  12. 网络技术沙龙:主题:数据库优化、CDN、集群负载均衡(1.9日技术聚会召集)
  13. dapperpoco mysql_DapperPoco -- 基于Dapper的、轻量级的、高性能的、简单的、灵活的ORM框架...
  14. 经典并发问题:生产者-消费者
  15. Codeforces Round #352 (Div. 1) B. Robin Hood
  16. 农银电商项目学习笔记(一)
  17. linux下修改文件权限.
  18. 计算机网络实验2-二层交换机的基本配置(eNSP)
  19. 宽带波形测试软件,适用于5G时代的波形测试分析系统是怎样的? - 全文
  20. verilog 7段数码管译码器

热门文章

  1. Tcl与Design Compiler (五)——综合库(时序库)和DC的设计对象
  2. php 服务器系统时间,php获取服务器运行时间
  3. 基于MDKA5D31-EK_T70开发板的QT示例-demo14:Ftp演示
  4. 每天近百亿条用户数据,携程大数据高并发应用架构涅槃
  5. PS如何最简单把彩色图片调成黑白色
  6. macos windowserver占用内存_iMac 5K 硬件升级:加内存、外接 SSD
  7. 计算机一级考试单项选择题及答案
  8. 网络安全实验之《VPN》实验报告
  9. 视口,像素和屏幕宽度获取方法
  10. javascript 获取毫秒、纳秒时间戳