Python对象内存地址

    在python中,万物皆对象,常见的整数、浮点数、字符串、元祖、列表等类型,以及各种class、class instance等等都是对象。这些对象在python解释器内部的地址是怎样的呢?这里我们只简单看下python对象内存地址的相关基础知识,以及编码过程中一些注意事项,关于python解释器的内存管理机制,涉及到解释器内核的内存池原理,这里不做深入探讨,有兴趣的朋友可以去阅读解释器源代码。

0x01 不可变对象

    不可变对象是指对象的内存值不能被改变。Python中变量以引用的方式指向对象,如果变量引用了不可变对象,当改变该变量时,由于其所指的对象的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址,即变量引用了新的对象。

    数值类型(整数和浮点)、字符串str、元组tuple都是不可变类型。比如a=1,b=[1],c={'a':1},id(a)、id(b[0])、id(1)、id(c['a'])将输出一样的值,因为1是不可变对象,其在内存中是不可改变的。

0x02 可变对象

    可变对象是指对象的内存值可以被改变,变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的内存地址,通俗点说就是原地改变。列表list、字典dict、集合set是可变类型。

0x03 对象的内存地址

    可以使用内置函数id()查看python对象的内存地址。下面是一些注意事项:

    (1) python中所有数字、字符串、list等值,创建时会分配内存空间,变量通过引用的方式使用它们。比如a=1和b=1,id(a)和id(b)的输出一样,表示a和b都指向相同的内存地址,即引用了同一个不可变对象;但是a=[1]和b=[1],id(a)和id(b)将输出不一样的值,a和b指向的是不同的内存地址,即引用了不同的可变对象,说明各可变对象是相互独立的,在内存中有独立的内存地址

    (2) 可用 is 判断两个对象的id(即内存地址)是否一样,用 == 判断两个对象的值是否一样。None值也有内存地址

    (3) list、set对象有各自的独立内存空间,他们的各元素以引用的方式指向可变、不可变对象;

    (4) 函数形参的默认值,在内存中会开辟独立的内存空间。比如测试代码中test函数的param参数,其默认值是空list,如果调用时未传参,则param指向内存中预先分配好的地址,该地址存储的是list类型的值;当调用时传参为a,则param引用了a指向的内存空间;

    (5) python使用引用计数和垃圾回收来释放内存对象,每个内存对象都维护了一个引用计数包括各种数字、字符串、list、set等类型值,以及类实例对象等等,当这些对象的引用计数为 0 时,会被解释器回收内存。每次对对象进行引用操作,都会导致其引用计数加1, 如下面测试代码中的整数1,列表a、b、c、d、n都引用了整数1,以及test函数中的append操作,都会导致数字1的引用计数加1

    (6) copy和deepcopy方法都创建了新的内存对象,如测试代码中的b和c都是新的变量,其各个元素可能是指向同一个内存空间。赋值操作是指向同一个内存块,同时增加引用计数。copy是浅拷贝,deepcopy是深拷贝,特别对于可变对象,copy是以引用的方式指向同一个可变对象,而deepcopy会开辟新的内存地址,也就是创建了新的可变对象。

0x04 测试代码

# -*- coding: utf8 -*-
import copy
import sys

a = [1, 2, [3, 4]]
b = copy.copy(a)
c = copy.deepcopy(a)
d = a

print 'address of a:', id(a)
print 'address of b:', id(b)
print 'address of c:', id(c)
print 'address of d:', id(d)
print 'address of 1:', id(1)
print 'address of element 0 in a:', id(a[0])
print 'address of element 0 in b:', id(b[0])
print 'address of element 0 in c:', id(c[0])
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

a[0] = 99
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

print 'address of element 0 in a:', id(a[0])
print 'address of element 0 in b:', id(b[0])
print 'address of element 0 in c:', id(c[0])

print 'address of element 2 in a:', id(a[2])
print 'address of element 2 in b:', id(b[2])
print 'address of element 2 in c:', id(c[2])

a[2].append(5)
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

def test(param=[]):
    print 'address of param:', id(param)
    param.append(1)
    print 'reference count of 1:', sys.getrefcount(1)
    return param

print test(a)
print test()
print test()
print 'a=', a
print 'b=', b
print 'c=', c
print 'd=', d

print 'reference count of 1:', sys.getrefcount(1)
n = 1
print 'reference count of 1:', sys.getrefcount(1)
del n
print 'reference count of 1:', sys.getrefcount(1)

0x06 运行结果

address of a: 54681224
address of b: 54716296
address of c: 54692104
address of d: 54681224
address of 1: 48258856
address of element 0 in a: 48258856
address of element 0 in b: 48258856
address of element 0 in c: 48258856
a= [1, 2, [3, 4]]
b= [1, 2, [3, 4]]
c= [1, 2, [3, 4]]
d= [1, 2, [3, 4]]
a= [99, 2, [3, 4]]
b= [1, 2, [3, 4]]
c= [1, 2, [3, 4]]
d= [99, 2, [3, 4]]
address of element 0 in a: 48260488
address of element 0 in b: 48258856
address of element 0 in c: 48258856
address of element 2 in a: 54692232
address of element 2 in b: 54692232
address of element 2 in c: 54716360
a= [99, 2, [3, 4, 5]]
b= [1, 2, [3, 4, 5]]
c= [1, 2, [3, 4]]
d= [99, 2, [3, 4, 5]]
address of param: 54681224
reference count of 1: 161
[99, 2, [3, 4, 5], 1]
address of param: 54716424
reference count of 1: 162
[1]
address of param: 54716424
reference count of 1: 163
[1, 1]
a= [99, 2, [3, 4, 5], 1]
b= [1, 2, [3, 4, 5]]
c= [1, 2, [3, 4]]
d= [99, 2, [3, 4, 5], 1]
reference count of 1: 163
reference count of 1: 164
reference count of 1: 163

 

### Python 对象内存管理深度解析 Python对象内存管理机制是一个复杂但高效的体系,主要包括以下几个核心部分: #### 1. 引用计数 Python 使用引用计数作为其主要的内存管理策略之一。每个对象都维护了一个 `ob_refcnt` 属性,用于记录该对象当前被引用的次数[^5]。当一个新变量指向这个对象时,引用计数加一;反之,如果某个变量不再指向它,则引用计数减一。一旦引用计数降为零,说明没有任何地方再使用此对象,此时 Python 将自动释放该对象所占用的内存。 #### 2. 垃圾回收机制 除了引用计数外,Python 还提供了一套完整的垃圾回收系统以处理更复杂的场景,例如循环引用等问题。具体来说,Python 的垃圾回收分为三类: - **引用计数**:实时追踪对象的生命周期。 - **标记清除**:针对无法通过简单引用计数解决的情况(如循环引用),定期扫描未被访问的对象并清理它们。 - **分代回收**:基于对象存活时间的不同将其划分为多个世代,优先回收较年轻的对象,从而提高性能效率[^3]。 可以通过导入标准库中的 `gc` 模块来自定义这些行为设置参数或强制执行某些操作。 #### 3. 内存池机制 对于小型对象 (<256KB),Python 利用了专门设计的小型缓冲区分配器 Pymalloc 来进一步优化资源利用状况。这种方式能够显著降低因频繁申请/释放小块空间而导致的整体开销,并减少由于长期运行可能引发的碎片化现象发生几率[^4]。 以下是展示如何查看某特定实例内部状态以及手动触发GC过程的一个例子: ```python import sys import gc class MyClass: pass obj = MyClass() print(f"Reference count before creating a new reference: {sys.getrefcount(obj)}") another_reference = obj print(f"Reference count after creating another reference: {sys.getrefcount(obj)}") del another_reference print(f"Reference count after deleting one of the references: {sys.getrefcount(obj)}") # Manually run garbage collection collected_objects = gc.collect() print(f"{collected_objects} objects were collected during manual GC.") ``` 上述脚本展示了基本原理的同时也体现了灵活性所在之处在于允许程序员介入控制流程当中去调整适应不同需求环境下的表现形式. --- ####
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值