在Python中,一切都是指针。

一:对象三特性

        所有的Python对象都有三个特性:身份,类型和值。

        身份:每一个对象都有一个唯一的身份标识,任何对象的身份标识可以使用内建函数id()来得到。它可以被认为是该对象的内存地址。

        类型:对象的类型决定了该对象可以保存什么类型的值,可以进行什么样的操作,以及遵循什么样的规则。可以用内建函数type()查看Python对象的类型。

        值:对象表示的数据项。

        上面三个特性在对象创建的时候就被赋值,除了值之外,其它两个特性都是只读的。如果对象支持更新操作,那么它的值就可以改变,否则它的值也是只读的。对象的值是否可以更改被称为对象的可改变性(mutability)。

二:可变对象和不可变对象

        一种对对象进行分类的方式是:可变对象和不可变对象。

        可变对象允许他们的值被更新,而不可变对象则不允许他们的值被更改。下表列出了支持更新和不支持更新的类型:

        数值和字符串对象是不可改变的。尽管通过给数字对象(重新)赋值,可以“更新”一个数值对象。之所以给更新这两个字加上引号,是因为实际上并没有更新该对象的原始数值。

        因为数值对象是不可改变对象。Python的对象模型与常规对象模型有些不同。你所认为的更新实际上是生成了一个新的数值对象,并得到它的引用。

        在Python中,变量像一个指针指向装变量值的盒子。
对不可改变类型来说,无法改变盒子的内容,但可以将指针指向一个新盒子。每次将另外的数字赋给变量的时候,实际上创建了一个新的对象并把它赋给变量.(不仅仅是数字,对于所有的不可变类型,都是这么回事)。见下例:

x = 'Pythonnumbers and strings'

x = 'are immutable?!? What gives?'

i = 0

i = i + 1

        上面的例子中,事实上是一个新对象被创建,然后它取代了旧对象。就是这样。

        新创建的对象被关联到原来的变量名,旧对象被丢弃,垃圾回收器会在适当的时机回收这些对象。可以通过内建函数id()来确认对象的身份在两次赋值前后发生了变化。下面在上面的例子里加上id()调用,就会清楚的看到对象实际上已经被替换了:

>>> x = 'Python numbers andstrings'

>>> id(x)

>>> x = 'are immutable?!? Whatgives?'

>>> id(x)

>>> a=1

>>> id(a)

>>> a = a + 1

>>> id(a)

        可变对象,比如列表,可以被修改而无须替换原始对象,看下面的例子:

>>> aList = ['ammonia', 83, 85,'lady']

>>> id(aList)

>>> id(aList[2])

>>> id(aList[3])

>>> aList[2] = aList[2] + 1

>>> aList[3] = 'stereo'

>>> aList

['ammonia', 83, 86, 'stereo']

>>> id(aList)

>>> id(aList[2])

>>> id(aList[3])

        注意列表的值不论怎么改变,列表的 ID 始终保持不变。但是列表中的元素的ID却发生了变化。

三:变量

        在Python中,无需显式变量声明语句,变量在第一次被赋值时自动声明。和其他大多数语言一样,变量只有被创建和赋值后才能被使用。

        Python中不但变量名无需事先声明,而且也无需类型声明。对象的类型和内存占用都是运行时确定的。尽管代码被编译成字节码,Python仍然是一种解释型语言。在创建----也就是赋值时,解释器会根据语法和右侧的操作数来决定新对象的类型。

        在对象创建后,一个该对象的引用会被赋值给左侧的变量。

四:引用

        在Python语言中,赋值并不是直接将一个值赋给一个变量,对象是通过引用传递的。在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。

        要保持追踪内存中的对象,Python使用了引用计数这一简单技术。也就是说Python内部记录着所有使用中的对象各有多少引用。

        当对象被创建时,引用计数置为1,当这个对象不再需要时,也就是这个对象的引用计数变为0时,它被垃圾回收。

        1:增加引用计数

        当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为1。

        当同一个对象(的引用)又被赋值给其它变量时,或作为参数传递给函数,方法或类实例时,
或者被赋值为一个窗口对象的成员时,该对象的一个新的引用就被创建,该对象的引用计数自动加1。

        以下声明:

x = 3.14

y = x

        语句 x = 3.14 创建了一个浮点数对象并将其引用赋值给x。 x是第一个引用,因此,该对象的引用计数被设置为1。语句
y=x 创建了一个指向同一对象的别名y。见下图:

        事实上并没有为Y创建一个新对象,而是该对象的引用计数增加了1 次(变成了2)。这是对象引用计数增加的方式之一。

        还有一些其它的方式也能增加对象的引用计数,比如该对象作为参数被函数调用或这个对象被加入到某个容器对象当中时,比如下面的例子:

>>> x=3.14

>>> y=x

>>> id(x)

>>> id(y)

>>> def printid(a):

...    print id(a)

...

>>> printid(x)

>>> atuple=(1, x, 2)

>>> id(atuple[1])

>>> alist=[1,x,2]

>>> id(alist[1])

>>> alist = [1,2,3]

>>> blist = alist

>>> alist.append(4)

>>> blist

[1, 2, 3, 4]

        2:del 语句

        该语句会删除对象的一个引用,例如,在上例中执行del
y 会产生两个结果:从现在的名字空间中删除 y;      x的引用计数减

        引申一步,执行 del x 会删除该对象的最后一个引用,也就是该对象的引用计数会减为0,这会导致该对象从此“无法访问”或“无法抵达”。 从此刻起,该对象就成为垃圾回收机制的回收对象。
注意任何追踪或调试程序会给一个对象增加一个额外的引用,这会推迟该对象被回收的时间。

        3:减少引用计数

        当对象的引用被销毁时,引用计数会减小。最明显的例子就是当引用离开其作用范围时,这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随之减少。

        当变量被赋值给另外一个对象时,原对象的引用计数也会自动减1:

foo = 'xyz'

bar = foo

foo = 123

        当字符串对象"xyz"被创建并赋值给foo时,它的引用计数是1. 当增加了一个别名 bar时,引用计数变成了2.
不过当foo被重新赋值给整数对象123 时,xyz 对象的引用计数自动减1,又重新变成了1。

        其它造成对象的引用计数减少的方式包括:使用 del 语句删除一个变量,或者当一个对象被移出一个窗口对象时,或该容器对象本身的引用计数变成了0
时。

        4:垃圾回收

        不再被使用的内存会被一种称为垃圾收集的机制释放。虽然解释器跟踪对象的引用计数,但垃圾收集器负责释放内存。垃圾收集器是一块独立代码,它用来寻找引用计数为0的对象。它也负责检查那些虽然引用计数大于0
但也应该被销毁的对象。

        5:赋值

        对象可以被赋值到另一个变量(通过引用)。因为每个变量都指向同一个(共享的)数据对象,只要任何一个引用发生改变,该对象的其它引用也会随之改变。

        将变量名看作对象的一个指针。来看以下三个例子:

a: foo1 = foo2 = 4.3

        当从值的观点看这条语句时,它表现的只是一个多重赋值,将4.3 这个值赋给了foo1和foo2 这两个变量。

        这当然是对的,不过它还有另一层含义。
事实是一个值为4.3的数字对象被创建,然后这个对象的引用被赋值给foo1 和foo2, 结果就是 foo1 和foo2 指向同一个对象。如下图:

b:

foo1 = 4.3

foo2 = foo1

        这个例子非常类似上一个,一个值为4.3 的数值对象被创建,然后赋给一个变量,当执行foo2 = foo1 时,foo2 被指向foo1 所指向的同一个对象,这是因为Python通过传递引用来处理对象。foo2
就成为原始值4.3 的一个新的引用。 这样foo1 和foo2 就都指向了同一个对象。示意图也和上图一样。

c:

foo1 = 4.3

foo2 = 4.3

(foo2 = 1.3 + 3.0)

        这个例子有所不同。首先一个数字对象被创建,然后赋值给foo1.
然后第二个数值对象被创建并赋值给foo2. 尽管两个对象保存的是同样大小的值,但事实上系统中保存的都是两个独立的对象,其中foo1 是第一个对象的引用,foo2 则是第二个对象的引用。如下图:

        对象就象一个装着内容的盒子。当一个对象被赋值到一个变量,就象在这个盒子上贴了一个标签,表示创建了一个引用。每当这个对象有了一个新的引用,就会在盒子上新贴一张标签。当一个引用被销毁时,这个标签就会被撕掉。当所有的标签都被撕掉时,这个盒子就会被回收。

        这里的标签数,实际上就是引用计数。每个对象都天生具有一个计数器,记录它自己的引用次数。这个数目表示有多少个变量指向该对象。

        Python提供了is
和is not运算符来测试两个变量是否指向同一个对象。象下面这样执行一个测试:

a is b

这个表达式等价于下面的表达式

id(a) == id(b)

例子如下:

>>> a = [ 5, 'hat', -9.3]

>>> b = a

>>> a is b

True

>>> a is not b

False

>>>

>>> b = 2.5e-5

>>> b

2.5e-005

>>> a

[5, 'hat', -9.3]

>>> a is b

False

>>> a is not b

True

        在上面的例子中,注意到我们使用的是浮点数而不是整数。为什么这样?

        整数对象和字符串对象是不可变对象,所以Python会很高效的缓存它们。这会造成我们认为Python应该创建新对象时,它却没有创建新对象的假象。看下面的例子:

>>> a = 1

>>> id(a)

>>> b = 1

>>> id(b)

>>>

>>> c = 1.0

>>> id(c)

8651220

>>> d = 1.0

>>> id(d)

8651204

        在上面的例子中,a 和 b 指向了相同的整数对象,但是c 和 d 并没有指向相同的浮点数对象。

        Python仅缓存简单整数和短字符串,因为它认为在Python应用程序中这些小整数会经常被用到。当我们在写作本书的时候,Python缓存的整数范围是(-1,
100),不过这个范围是会改变的,所以请不要在你的应用程序使用这个特性。

# True

a = 1

b = 1

print(a is b)

# True

a = "good"

b = "good"

print(a is b)

# False

a = "very good morning"

b = "very good morning"

print(a is b)

# False

a = []

b = []

print(a is b)

参考:

        《Python核心编程》

Python深入:01内存管理的更多相关文章

  1. Python变量与内存管理

    Python变量与内存管理 –与C语言中的变量做对比,更好的理解Python的变量. 变量 变量在C语言中  全局变量:其存放在内存的静态变量区中.  局部变量:代码块中存放在内存的代码区当中,当被调 ...

  2. python变量的内存管理

    python变量的内存管理 一.变量存在了哪里? 先让我们来看一段代码: height = 100 # 定义变量 # print(100) # print会自动帮你创建一个变量100,打印完之后,马上 ...

  3. Python 中的内存管理

    Python 中一切皆对象,这些对象的内存都是在运行时动态地在堆中进行分配的,就连 Python 虚拟机使用的栈也是在堆上模拟的.既然一切皆对象,那么在 Python 程序运行过程中对象的创建和释放就 ...

  4. python中的内存管理

    不像大多数编译型语言,变量必须在使用之前声明名字和类型,在python中,变量在第一次被赋值时自动声明.在变量创建时,python解释器会根据语法和右侧的操作数来决定新对象的类型,在对象创建后,一个该 ...

  5. python如何进行内存管理

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 语言的内存管理是语言设计的一个重要方面.它是决定语言性能的重要因素.无论是C语言的 ...

  6. Python入门 值内存管理与所有的关键字

    值内存管理 Python采用的是基于值得内存管理方式,如果为不同变量赋值为相同值,这个值在内存中只有一份,多个变量指向同一块内存地址. id(x) : 用于返回变量所指值的内存地址 x = 3 pri ...

  7. Python中的内存管理机制

    Python是如何进行内存管理的 python引用了一个内存池(memory pool)机制,即pymalloc机制,用于管理对小块内存的申请和释放 1.介绍 python和其他高级语言一样,会进行自 ...

  8. python学习Day9 内存管理

    复习 :文件处理 1. 操作文件的三步骤:-- 打开文件:此时该文件在硬盘的空间被操作系统持有 |  文件对象被应用程序持用 -- 操作文件:读写操作 -- 释放文件:释放操作系统对文件在硬盘间的持有 ...

  9. python的变量内存管理

    一.变量的引用机制 当你在python中定义一个值,如x = 500时,python会在内存中开辟一个小地方用于存储数值. x = 500 #定义一个变量 print(id(x)) #打印该变量的内存 ...

随机推荐

  1. ROWID的使用——快速删除重复的记录

    ROWID是数据的详细地址,通过rowid,oracle可以快速的定位某行具体的数据的位置.ROWID可以分为物理rowid和逻辑rowid两种.普通的表中的rowid是物理rowid,索引组织表(I ...

  2. 2018-2-13-wpf-PreviewTextInput-在鼠标输入获得-_u0003

    title author date CreateTime categories wpf PreviewTextInput 在鼠标输入获得 � lindexi 2018-2-13 17:23:3 +08 ...

  3. Uva11384 Help is needed for Dexter

    Dexter is tired of Dee Dee. So he decided to keep Dee Dee busy in a game. The game he planned for he ...

  4. 洛谷P2258 子矩阵[2017年5月计划 清北学堂51精英班Day1]

    题目描述 给出如下定义: 子矩阵:从一个矩阵当中选取某些行和某些列交叉位置所组成的新矩阵(保持行与列的相对顺序)被称为原矩阵的一个子矩阵. 例如,下面左图中选取第2.4行和第2.4.5列交叉位置的元素 ...

  5. vue-eslint配置文件

    做项目的时候,我把eslint设置为了false,可想而知提交会产生的冲突 让我一个一个解决肯定不可能的,eslint的rule很多 在vue的配置文件.eslintrc.js中配置以下选项 这样只需 ...

  6. SQL Server新增用户并控制访问权限设置。

    新增用户: 一.进入数据库:[安全性]—>[登录名]—>[新建登录名] 二.在常规选项卡中.如图所示,创建登录名.注意设置默认的数据库. 三.在[用户映射]下设置该用户所能访问的数据库.并 ...

  7. zoj 1028 Flip and Shift(数学)

    Flip and Shift Time Limit: 2 Seconds      Memory Limit: 65536 KB This puzzle consists of a random se ...

  8. Cross-site scripting(XSS)

    https://en.wikipedia.org/wiki/Cross-site_scripting Definition Cross-site scripting (XSS) is a type o ...

  9. 常用命令3-文件搜索命令1-locate

    新建文件搜索不到,是因为查询是从数据库里查询的,然后数据库是一天后才更新,但是可以强制更新. 优点:能进行模糊搜索. 在tmp目录下创建一个文件,发现,在root家目录下搜不到.是因为配置文件原因. ...

  10. phpcms 按价格、按销量、按时间等排序实现思路

    大体思路是在链接中加入指定排序的参数,例如我们使用get中的order作为排序参数: order=views 人气:order=sells 效率:order=pirce 按价格: 那么这三个排序按钮的 ...