python 对象_python的引用传递,可变对象,不可变对象

(1)python中的引用传递

  • 首先必须理解的是,python中一切的传递都是引用(地址),无论是赋值还是函数调用,不存在值传递。

(2)不可变对象(变量指向的内存的中的值不能够被改变)

  • 当更改该对象时,由于所指向的内存中的值不可改变,所以会把原来的值复制到新的空间,然后变量指向这个新的地址。python中数值类型(int和float),布尔型bool,字符串str,元组tuple都是不可变对象。

a = 1print id(a)    # 40133000L,整数1放在了地址为40133000L的内存中,a变量指向这个地址。a += 1 print id(a)    # 40132976L,整数int不可改变,开辟新空间存放加1后的int,a指向这个新空间。

(3)可变对象(变量指向的内存的中的值能够被改变)

  • 当更改该对象时,所指向的内存中的值直接改变,没有发生复制行为。python中列表list,字典dict,集合set都是可变对象。包括自定义的类对象也是可变对象。

a = [1,2,3]print id(a)    # 44186120L。a += [4,5]     # 相当于调用了a.extend([4,5])print id(a)    # 44186120L,列表list可改变,直接改变指向的内存中的值,没开辟新空间。a = a + [7,8]  # 直接+和+=并不等价,使用+来操作list时,得到的是新的list,不指向原空间。print id(a)    # 44210632Ldef f(default_arg=[]):  default_arg.append('han')f() # ['han']f() # ['han', 'han'] # 函数默认的可变参数并不会每次重新初始化,而是使用上次的作为默认值。f([]) # ['han'] # 自行传入参数即可。def f(default_arg=None):  # 一个常见的做法是判断是否为空,为空则新建list,否则append    if default_arg is None:        default_arg = []    default_arg.append("some_string")    return default_arg

(4)可变对象和不可变对象

  • python变量保存的是对象的引用,这个引用指向堆内存里的对象,在堆中分配的对象分为两类,一类是可变对象,一类是不可变对象。不可变对象的内容不可改变,保证了数据的不可修改(安全,防止出错),同时可以使得在多线程读取的时候不需要加锁。

() is ()     # 返回True,因为tuple是不可变对象(不可改变,怎么定义都一样)'' is ''     # 返回True,因为str是不可变对象None is None # 返回True,None也是不可变的[] is []    # 返回False,因为是可变对象(可能改变,定义出来的两个必然要不一样){} is {}    # 返回False,因为是可变对象[] == []    # 返回True,注意==和is的不同,==只比较内容,is比较地址(id)class Student:  passStudent() is Student()  # 返回False,自定义类型也是可变对象,两次定义的对象地址是不同的id(Student()) == id(Student())  # 返回True,这里比较神奇,是因为创建一个Student对象,id()后返回地址但是进行了对象销毁,第二次又重新创建,两次占用了同一个地址

(5)不可变对象的编译时驻留(类似java的常量池)

学java的可以回忆回忆JVM的常量池

int 的驻留:-5到256之间的整数都会进行驻留,再次定义的变量地址不变,为什么是-5到256呢,这是解释器决定的,依赖于具体实现。str的驻留:只包含字母,数字,下划线的字符串会驻留;长度为0或1的会驻留;

a = -5    b = -5   a is b     # True,-5到256之间的整数,驻留(直觉上这部分数据会频繁调用,驻留可以节省资源)a = 256    b = 256   a is b     # True,-5到256之间的整数,驻留a = -6    b = -6   a is b     # False,非-5到256之间的整数,不驻留a = 257    b = 257   a is b     # False,非-5到256之间的整数,不驻留a = 'hello_world'    b = 'hello'+'_'+'world'a is b     # True,只包含字母,数字,下划线的字符串会驻留a = 'hello_world!'    b = 'hello_world!'a is b     # False,包含了特殊字符!, 不驻留'hello_world' is '_'.join(['hello', 'world'])     # False,因为驻留是编译阶段发生的,join在解释阶段才产生结果,未进行驻留a, b = 'hello_world!', 'hello_world!'a is b    # True 编译器的优化,在同一行赋值字符串时,只创建一个对象,指给两个引用。(ps:不适用3.7.x以下版本,3.7.x以下中会返回False,本焓主要用Python3.8.x)

(6)关于驻留的陷阱

  • 跟驻留没有直接关系,是在命令行运行和py文件直接运行有一些差异。先看之前的小例子。

a = 257    b = 257   a is b     # False,非-5到256之间的整数,不驻留。
  • 事实上,在命令行运行得到的才是False(我做的小实验一般都在交互式命令行上运行)如果把这三行放到py文件里,再直接运行,得到的是True,因为py文件是一次性编译的,而交互式命令行按一行为单位(严格说是命令结束时的全部,因为会有for while这种)编译或者在交互式中把这三行定义为函数,再调用函数,返回也是True。

def func():    a = 257  b = 257  return a is bfunc()  # 返回True

00bc9ef6c3236cace4010784b01729f4.png

以上程序在命令行运行和py文件直接运行有一些差异。

  • 这是由python的代码块机制导致的,在同一代码块中相同值的赋值会指向同一个对象。函数体,类对象体,py文件,模块都可以看作一个代码块。

  • 在交互式命令行上,一行看作一个代码块(严格说是命令结束时的全部,因为会有for while这种),所以,这里所谓“代码块的优化”,就是前面提到的,同行赋值的优化,只在一行(代码块)上优化。

  • 到具体直接运行py文件,又有了更大范围的代码块的优化,所以连着两行相同赋值的对象,会指向同一个对象。

420d2e86e7da1b755b3ce57850eb025b.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值