python@可变对象和不可变对象@按值传递和引用传递@python运行可视化工具

news2024/9/20 16:30:49

文章目录

    • 可变对象和不可变对象🎈
      • 可视化工具🎈
      • 可变对象和id
      • eg
      • eg
      • 变量名和内存地址🎈
      • 函数调用对参数的修改😂
      • Note
    • 按值传递vs引用传递
      • note🎈
      • 如何借助函数修改外部变量的值?
      • Note

可变对象和不可变对象🎈

  • 在Python中,对象可以分为可变对象和不可变对象两种类型。

    不可变对象:在创建后无法被修改的对象,例如数字、字符串、元组等。

    可变对象:在创建后可以被修改的对象,例如列表、字典、集合等。

    可变对象和不可变对象的区别主要在于它们的赋值和传递方式不同。对于不可变对象,赋值或传递时会创建一个副本,而对副本的修改不会影响原对象,例如:

    a = 1
    b = a # 创建a的副本赋值给b
    b = 2 # 修改b的值,不会影响a的值
    print(a) # 输出:1
    print(b) # 输出:2
    

    而对于可变对象,赋值或传递时只是创建了一个引用,即两个变量指向同一个对象,因此对其中一个变量的修改会影响另一个变量,例如:

    a = [1, 2, 3]
    b = a # 创建a的引用赋值给b
    b[0] = 0 # 修改b中的元素,会影响a中的元素
    print(a) # 输出:[0, 2, 3]
    print(b) # 输出:[0, 2, 3]
    
  • Python官方文档中有一些关于可变对象和不可变对象的说明,可以参考以下链接:

    • Python官方文档:数据模型
    • Python官方文档:不可变序列
    • Python官方文档:可变序列

可视化工具🎈

  • Python Tutor: Learn Python, JavaScript, C, C++, and Java programming by visualizing code
    • Online Python compiler and debugger - Python Tutor - Learn Python by visualizing code
    • Python Preview - Visual Studio Marketplace
  • Debug Visualizer - Visual Studio Marketplace

可变对象和id

  • id()|Built-in Functions — Python documentation

  • 在Python中,id()函数用于获取对象的唯一标识符。每个对象都有一个唯一的标识符,可以用于比较对象是否相等。这个标识符是一个整数,可以被认为是对象的内存地址。

    以下是一些示例代码,演示如何使用id()函数:

    # 获取整数对象的标识符
    x = 10
    print(id(x))  # 输出一个整数
    
    # 获取字符串对象的标识符
    s = "hello"
    print(id(s))  # 输出一个整数
    
    # 获取列表对象的标识符
    lst = [1, 2, 3]
    print(id(lst))  # 输出一个整数
    
    # 获取自定义对象的标识符
    class MyClass:
        pass
    
    obj = MyClass()
    print(id(obj))  # 输出一个整数
    

    在上面的代码中,我们分别定义了一个整数、一个字符串、一个列表和一个自定义类对象,并使用id()函数获取它们的标识符。需要注意的是,即使两个对象的值相同,它们的标识符也可能不同,因为它们可能位于不同的内存位置。

    id()函数通常用于比较对象是否相等。如果两个对象的标识符相同,则它们是同一个对象。如果两个对象的标识符不同,则它们是不同的对象。例如:

    a = [1, 2, 3]
    b = [1, 2, 3]
    c = a
    
    print(id(a))  # 输出一个整数
    print(id(b))  # 输出一个不同的整数
    print(id(c))  # 输出与a相同的整数
    
    print(a == b)  # 输出True,因为a和b的值相等
    print(a is b)  # 输出False,因为a和b是不同的对象
    print(a is c)  # 输出True,因为a和c是同一个对象
    

    在上面的代码中,我们定义了三个列表对象abc,其中ab的值相同,但它们是不同的对象。ca相同,是同一个对象。我们使用id()函数获取它们的标识符,并使用is运算符比较它们是否相同。需要注意的是,==运算符比较的是值是否相等,而is运算符比较的是对象是否相同。

eg

  • 下面提到的例子中的python指针仅仅标识地址(类比于c语言中的指针)

    ##
    a,b=[1,2],34
    
    ## 利用成组赋值初始化c,d两个新变量(指针)
    #c和a指向相同内存区域,d和b指向相同区域
    c,d=[a,b]
    
    ##尝试通过指针c来修改c,和a共同指向的区域,值从[1,2]变为[1,2,3,4]
    c+=[11,22]
    
    ##尝试将指针d指向另一个内存区域(保存着100)(不同于b所指向的内存区域(保存着34))
    d=100
    
    ##
    a,b,c,d
    #([1, 2, 11, 22], 34, [1, 2, 11, 22], 100)
    #可以发现,a,c由于始终指向同一片内存,所以通过其中的一个指针(c或a)来修改同一片内存,通过a,c访问到的内容是始终保持一样
    #因为list是可变对象,对这类对象执行+=操作不会改变指针所指的内存区域(首地址)
    #(如果是str这类不可变对象,使用+=会导致指针指向其他内存区域!)
    ##
    #这几个变量指向的地址一致性分析:a,c指向相同的区域,b,d指向不同的区域
    [id(x) for x in[a,b,c,d]]
    #[2363406317120, 2363321642320, 2363406317120, 2363321832912]
    
    
    • s="abc"
      id(s)#2363326296880
      s+="d"	
      id(s)#2363407021040
      
      • 可以看到s指向的内存首地址(id)发生了改变
  • 如果您对以下内容有所了解,那么理解起来回简单一些:

    • 操作系统上的硬链接(hardlink)
    • c语言中的指针
  • 以硬链接为例,假设

    • 您在A目录有一个文件X(理解为名字或者指针),它在硬盘上的地址记为idx
    • B目录下建立了一个指向磁盘的idx的名为X'的指针
    • 但是通过XX'均可以找到磁盘上的位置idx,也就是说,通过X或X’都可以打开/修改idx开始的一片区域
    • 也就是说,X,X'访问到的内容始终是一样的
    • 但是,您无法通过修改X'指向到idy的新区域,同时将X也指向idy
    • X,X'各自不知道对方的存在
    • 在python编程中(尤其是面向对象中),要注意这些东西
  • 再形象点说,假设公司派了2个人(设为a,b)去一个同一个地方P1办事,a先受到具体通知(a=P1),a将地点告诉b(类似于执行语句b=a)

  • 假设b再执行任务的过程中被公司紧急改派到另一个地方P2执行任务(b=P2)

  • 此时a并没有因为b绑定的任务发生变化而跟着变化

  • 假设公司由派遣了第3个人c去协助a到P1地方(对象)去工作

  • 如果P1是一个可变对象(比如列表),比如修改P1的某个成员P[i],a,c两个人都可以看到相同的变化

  • P1=[1,2,3]
    a
    c
    • a[1]=100,c[1]=100,P1[1]=100效果是一样的,也就是说,修改对象内部的东西,对于所有引用了这个对象的变量是可见的,但是如果形如c=P2这样的语句直接使得c不再指向P1,而是指向P2,那么对c的后续的任何使用和修改都对立与a,P1

eg

  • a,b=[1,2],34
    t=[a,b]
    t[1] is b#true
    t[1]=8 
    id(t[1]),id(b)#(2363321641488, 2363321642320)
    
    • t[1]b最开始指向同一片区域,但是后来t[1]被修改指向其他区域,不再和b有相同的取值
    • b的取值也无法直接通过t[1]来修改(只能是直接将b)作为左值,才能使其指向其他地方
    • 反之也一样,修改b无法直接影响到t[1](除非,b指向一个可变对象(比如list,同时做的是原地修改))

变量名和内存地址🎈

  • 在 Python 中,变量名是用于引用对象的标识符,而对象则是存储在内存中的数据结构。

  • 每个对象都有一个唯一的标识符,即对象的内存地址。

  • 变量名引用了对象的内存地址,而不是对象本身

  • 当我们创建一个变量并为其赋值时,Python 解释器会在内存中创建一个对象,并将变量名与对象的内存地址进行绑定。

    • 注意访问变量/创建变量/给变量赋值的差异
  • 当我们引用变量时,Python 解释器会查找变量名对应的内存地址,然后返回存储在该地址中的对象。

  • python中的变量名和c语言中的指针有所不同

  • print(f"{id(10)=}")
    a=10
    print(f"{id(a)=}")
    
    def f(x):
        print(f"{id(x)=}")
        x=20
        print(f"{id(20)=}")
        print(f"{id(x)=}")
    
    f(a)
    a
    print('a: ', a)#10
    
    • id(10)=3207627500112
      id(a)=3207627500112
      id(x)=3207627500112
      id(20)=3207627500432
      id(x)=3207627500432
      a:  10
      
  • 上面的语句b=a将a所引用的内存地址告诉了b,从而b指向的和a所指的空间一致

    • 尽管如此,b,a还是相对独立的
    1
    a
    b
  • 修改a,使其指向别处

    1
    100
    b
    a

函数调用对参数的修改😂

  • a=10
    def f(x):
        x=20
    f(a)
    a
    print('a: ', a)#10
    
  • 初学者可能会认为最后打印的会是20,但实际上会是10

  • 在这段代码中,首先将值10赋给变量a

  • 然后定义了一个名为f的函数,它有一个参数x,在函数内部,将值20赋给参数x

  • 当你以a做为实参,执行f(a),python会分别一个变量x,并且x绑定到一个同为20的值

  • 因此,当你调用函数后打印a的值,你会得到10,也就是a的原始值。

  • print(f"{id(10)=}")
    a=10
    print(f"{id(a)=}")
    
    def f(x):
        print(f"{id(x)=}")
        x=20
        print(f"{id(20)=}")
        print(f"{id(x)=}")
    
    f(a)
    a
    print('a: ', a)#10
    

Note

  • 在 Python 中,函数参数是按值传递的,因此在函数内部修改参数的值不会影响函数外部的变量。

  • 但是,你可以使用可变对象作为参数来达到修改参数值的目的。

  • 例如,你可以将参数 x 设计成一个列表或字典,然后在函数内部修改列表或字典元素的值,这样就可以修改参数的值了。

    以下是一个示例代码:

    def f(x):
        x[0] = 20
    
    a = [10]
    f(a)
    print(a[0])  # 输出 20
    
  • 在这个示例中,我们将参数 x 设计成一个列表 a

  • 在函数 f 内部,我们将列表 a 的第一个元素修改为 20。这样,在函数外部打印 a[0] 的值时,会输出 20,因为参数 a 的值已经被修改了。

  • 上述例子中,可以理解为,f(a)执行了x=a,从而一个和a不同的变量x就被创建,他们此时指向相同的内存区域

  • 进入函数内部这个过程中,只有x进入,而a没有受到影响,x被修改不会影响a

  • 需要注意的是,这种方式修改参数值的做法并不是很常见,因为它可能会导致代码难以理解和维护。通常来说,我们更倾向于使用函数的返回值来传递函数处理后的结果。

按值传递vs引用传递

  • 按值传递和引用传递是两种不同的函数参数传递方式。
  • 按值传递(call by value)是将参数的复制一份,然后传递给函数。
    • 在函数内部,对参数进行修改不会影响函数外部的变量。这种方式是最常见的参数传递方式,也是 Python 中默认的参数传递方式
  • 引用传递(call by reference)是将参数的引用或地址传递给函数。
    • 在函数内部,对参数进行修改会影响函数外部的变量。这种方式通常是通过指针或引用来实现的,比如在 C 语言中,可以通过指针来实现引用传递。
  • 在 Python 中,函数参数默认是按值传递的
  • 但是当参数是可变对象时(比如列表或字典),即使(看起来和按值传递一样),但可以在函数内部被修改参数的值,因为可变对象是引用传递的。

note🎈

  • 注意区分:

    a=[1,2,3]
    def f(x):
    	x=100
    f(a)
    print(f"{a=}")#[1,2,3]
    
    b=[1,2,3]
    def g(x):
    	x[0]=100
    g(b)
    print(f"{b=}")#[100,2,3]
    
    
    
    • 这里a,b是两个独立的列表,只是他们的内容是一样的,都是1,2,3

    • 运行结果:

    • a=[1, 2, 3]
      b=[100, 2, 3]
      
    • a没有被修改;而b被修改了

    • 因为a是值,而b[0]是引用

    • 在这里插入图片描述在这里插入图片描述
      函数g(x)的参数x传入实参列表对象b
      在这里插入图片描述在这里插入图片描述
      函数f内部执行了x=100,x从list这个Object断开,而指向一个常量100
  • 当我们将一个列表或字典等可变对象作为函数参数传递时,实际上传递的是对象的引用或地址,而不是对象的值。这意味着,在函数内部对参数进行修改时,实际上修改的是对象本身,而不是对象的副本。因此,修改后的对象的值将在函数外部可见。

    当我们将一个整数、字符串等不可变对象作为函数参数传递时,实际上传递的是对象的值的副本。这意味着,在函数内部对参数进行修改时,实际上修改的是参数的副本,而不是原始对象。因此,在函数外部,原始对象的值不会受到影响。

  • 在第一个示例中,函数 f 接收参数 x 并将其赋值为 100,但是在函数外部调用 f 并不会改变变量 a 的值,因为在函数内部的赋值语句 x=100 只是将参数 x 的引用指向了一个新的对象(100),并不会影响变量 a 的引用。因此,变量 a 的值仍然是 [1, 2, 3]

    • 这里x,a都是指向同一个内存区域的指针
    • 我们修改x指向其他地方,不会影响到a,a依然指向原来的内存
  • 但是,如果a,x所指的是可变对象,那么a[i],x[i]始终是一样的(指向相同内存)

  • 在第二个示例中,函数 g 接收参数 x 并将其第一个元素赋值为 100,由于列表是可变对象,所以在函数内部修改参数 x 的第一个元素也会影响到变量 b。因此,变量 b 的值被修改为 [100, 2, 3]

    需要注意的是,Python 中的列表、字典等可变对象是引用传递的,而整数、字符串等不可变对象则是按值传递的。在函数内部对可变对象进行修改时,可能会影响到函数外部的变量,而对不可变对象进行修改时则不会。

如何借助函数修改外部变量的值?

  • 根据上述的讨论,如果您确实需要根据某个函数修改某个变量,在python中,使用返回值是一个不错的选择

    • 例如:python中最常用的是

    • x=[]
      def square(n):
      	res=[x**2 for x in range(n)]
          return res
      x=square(10)
      
    • 而不是:

      • x=[]
        def square(n,x):
        	x=[x**2 for x in range(n)]
        square(10,x)
            
        
  • 另一方面,有一个关键字叫global,使用它可以修改外部变量

    • x=[]
      def square(n):
          global x
          x=[x**2 for x in range(n)]
      square(10)
      
      

Note

  • a=[11,22,33]
    b=[11,22,33]
    print(id(a),"@{id(a)}")
    print(id(b),"@{id(b)}")
    ida=id(a[0])
    idb=id(b[0])
    ida,idb
    
    • 3207784112768 @{id(a)}
      3207784126592 @{id(b)}
      (3207627500144, 3207627500144)
      
  • id(a[0])==id(b[0])#True
    a[0] is b[0]#True
    
  • 这个例子表示a,b是不同的两个列表对象,他们有共同点,就是内容是一样的

  • 既然内容一样为什么还要强调a,b是不同对象?

  • 因为对a的修改不会引起b的变化,反之也一样

    • 就好像两个不同的平台a,b请了同一个专家x做同样的工作
    • 后来其中的平台a请了专家y代替专家x,这不会影响平台b保持聘用专家x
  • 最后注意,这里是显式的各自为a赋值[11,22,33],而不是通过b=a这种方式赋值,后者方式使得b对列表的修改对a是可见的

  • a=[11,22,33]
    b=[11,22,33]
    a[0]=100
    a=[11,22,33]
    b=a
    a[0]=100
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/458639.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

这些不可不知的JVM知识

JVM是面试中必问的部分,本文通过思维导图以面向面试的角度整理JVM中不可不知的知识。 先上图: JVM必备知识 1、JVM基本概念 1.1、JVM是什么 JVM 的全称是 「Java Virtual Machine」,也就是我们耳熟能详的 Java 虚拟机。 JVM具备着计算机的…

vue3 Pinia快速入门

为什么是Pinia 怎么说呢,其实在过往的大部分项目里面,我并没有引入过状态管理相关的库来维护状态。因为大部分的业务项目相对来说比较独立,哪怕自身功能复杂的时候,可能也仅仅是通过技术栈自身的提供的状态管理能力来处理业务场景…

huggingface下载的.arrow数据集读取与使用说明

1.数据下载方式:load_dataset 将数据集下载到本地:(此处下载的是一个物体目标检测的数据集) from datasets import load_dataset # 下载的数据集名称, model_name keremberke/plane-detection # 数据集保存的路径 save_path da…

苹果Mac电脑清理垃圾软件卸载工具CleanMyMac X

最近刚刚入手了一台 M1 Macbook,因为不是很懂下载了很多软件,然后又卸载了一些,导致系统内存在很多垃圾文件,我也不知道怎么清理,后来查询了一些资料,大家都普遍推荐 CleanMyMac X,于是经过我一…

触摸屏是如何诞生的,它又是如何影响和改变着我们的生活?

芊芊玉指在小小的屏幕上滑动,天下事便了然于胸。这就是手机触摸屏给我们的生活带来的改变。 曾几何时,我们是生活在九宫格或者全键盘上的“拇指族”。一股浪潮席卷而来,手机上的实体按键都消失了,虚拟按键仅在需要时出现。触摸屏是…

论文实验1、安装tensorflow运行节点嵌入相关方法

还是官方的教程好使 使用 pip 安装 TensorFlow 只有三步 1.安装python,版本太高不行,在推荐版本里选最高的。 2.安装python虚拟环境venv python -m venv --system-site-packages .\venv .\venv\Scripts\activate 3.在虚拟环境里装tensorflow pip…

vue的watch侦听器、watch的属性 immediate(侦听属性)、deep(侦听一个对象)

1.什么是watch侦听器 watch侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。 语法格式如下: const vm new Vue({el: #app,data: { username: },watch: {//监听username值的变化// newVal 是"变化后的新值”,oldVal 是"变…

Golang每日一练(leetDay0046)

目录 136. 只出现一次的数字 Single Number 🌟 137. 只出现一次的数字 II Single Number II 🌟🌟 260. 只出现一次的数字 III Single Number III 🌟🌟🌟 🌟 每日一练刷题专栏 &#x1f3…

linux docker搭建Zfile

1.下载镜像 docker pull stilleshan/zfile2.创建挂载目录 mkdir -p /opt/docker/zfile #自定义路径3.运行 docker run -d --namezfile --restartalways -p 1111:8080 \-v /opt/docker/zfile/conf:/root/.zfile-v4 \-v /opt/docker/zfile/data:/root/zfile/data \stillesha…

C/C++每日一练(20230425)

目录 1. 成绩分布 ※ 2. 汇总区间 🌟 3. 矩阵置零 🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 成绩分布 原标题:统计某一单…

基础数据结构-顺序表

顺序表 顺序表定义结构体定义初始化扩容函数打印函数尾插和尾删头插和头删查找函数指定位置插入和删除顺序表销毁 顺序表定义 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 顺序表又分为…

图像预处理方法

图像预处理 膨胀腐蚀概述 两个基本的形态学操作是腐 和膨胀。他们 的变体构成了开运算 ,闭运算, 梯度等。 根据卷积核的大小前景的所有像素会腐 掉 变为 0 ,所以前景物体会变小整幅图像的白色区域会减少。 对于去除白噪声很有用 也可以用来…

推荐系统搭建全程图文攻略

推荐系统搭建全程图文攻略 推荐系统架构简介 整体推荐架构图: 推荐整体从数据处理开始,默认数据从关系型数据到每天增量导入到hive,在hive中通过中间表和调用python文件等一系列操作,将数据处理为算法数学建模的入口数据&#x…

【SVN】在Windows系统上进行SVN的基本操作(检出,更新,提交,分支合并分支,还原,制造冲突以及解决冲突,忽略)

介绍 这里是小编成长之路的历程,也是小编的学习之路。希望和各位大佬们一起成长! 以下为小编最喜欢的两句话: 要有最朴素的生活和最遥远的梦想,即使明天天寒地冻,山高水远,路远马亡。 一个人为什么要努力&a…

其实苹果知道自己离不开中国制造,因此悄悄给自己留了后路

苹果在加速离开中国,不过从苹果的做法却又可以看到它其实很清醒地认识到无法离开中国制造,因此它在力推印度制造的时候,其实并没拼尽全力,深刻认识到印度制造和印度市场与中国的差距。 一、印度制造和印度市场与中国的差距 2022年…

防雷知识:什么是雷电浪涌

浪涌是突然发生并超过典型工作电压的过电压。一般来说,浪涌是电路中短暂的电流、电压或功率波。今天我们就来科普一下什么是雷电浪涌。 什么是浪涌? 浪涌,顾名思义,是一种突然发生并超过典型工作电压的过电压。一般来说&#xf…

工具链和其他-异步模块加载

目录 CMD/AMD Asynchronous Module Definition(AMD异步模块定义,语法风格) Common Module Definition ES6/CommonJS CommonJS ES6 Module 加载器示例 总结 cmd和amd的区别 现在有哪些异步加载方式 整体结构 编程:commonjs es6 module (有可能解…

基于STM32和oneNET云平台的数据采集系统(MQTT协议)

文章目录 前言一、onenet云平台产品创建二、硬件选择三、设计理念四、实战编程1. 传感器部分2. ESP82663. 定时器4. 串口5. MQTT 五、进阶练习 前言 该篇为基于stm32esp8266通过mqtt协议连接onenet物联网云平台,单片机部分将采集到的数据(温湿度、光照强度、压强等…

DX云音乐(安卓)

首先,软件安装好不用注册登录就可以直接使用,在首页这里有很多推荐的热门歌单,比如,有年度热门的DJ歌曲,有抖音热门DJ,有各种跨年晚会,有运动必备的DGM,有90后的经典旋律等等。 还有…

php+vue 校友交流平台

1.普通用户功能分析 (1)用户注册:用于注册校友录用户。 (2)用户登录:供校友录用户登录。 (3)资料修改:修改当前登录使用者信息。 (4)…