跟老齐学Python之深入变量和引用对象_python

在《永远强大的函数》那一讲中,老齐我已经向看官们简述了一下变量,之后我们就一直在使用变量,每次使用变量,都要有一个操作,就是赋值。本讲再次提及这个两个事情,就是要让看官对变量和赋值有一个知其然和知其所以然的认识。当然,最后能不能达到此目的,主要看我是不是说的通俗易懂了。如果您没有明白,就说明我说的还不够好,可以联系我,我再为您效劳。

变量和对象

在《learning python》那本书里面,作者对变量、对象和引用的关系阐述的非常明了。我这里在很大程度上是受他的启发。感谢作者Mark Lutz先生的巨著。

应用《learning python》中的一个观点:变量无类型,对象有类型

在python中,如果要使用一个变量,不需要提前声明,只需要在用的时候,给这个变量赋值即可。这里特别强调,只要用一个变量,就要给这个变量赋值。

所以,像这样是不行的。

复制代码 代码如下:

>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

 反复提醒:一定要注意看报错信息。如果光光地写一个变量,而没有赋值,那么python认为这个变量没有定义。赋值,不仅仅是给一个非空的值,也可以给一个空值,如下,都是允许的

复制代码 代码如下:

>>> x = 3
>>> lst = []
>>> word = ""
>>> my_dict = {}

 在前面讲述中,我提出了一个类比,就是变量通过一根线,连着对象(具体就可能是一个int/list等),这个类比被很多人接受了,算是我老齐的首创呀。那么,如果要用一种严格的语言来描述,变量可以理解为一个系统表的元素,它拥有过指向对象的命名空间。太严肃了,不好理解,就理解我那个类比吧。变量就是存在系统中的一个东西,这个东西有一种能力,能够用一根线与某对象连接,它能够钓鱼。

对象呢?展开想象。在机器的内存中,系统分配一个空间,这里面就放着所谓的对象,有时候放数字,有时候放字符串。如果放数字,就是int类型,如果放字符串,就是str类型。

接下来的事情,就是前面说的变量用自己所拥有的能力,把对象和自己连接起来(指针连接对象空间),这就是引用。引用完成,就实现了赋值。

看到上面的图了吧,从图中就比较鲜明的表示了变量和对象的关系。所以,严格地将,只有放在内存空间中的对象(也就是数据)才有类型,而变量是没有类型的。这么说如果还没有彻底明白,就再打一个比喻:变量就好比钓鱼的人,湖水里就好像内存,里面有好多鱼,有各种各样的鱼,它们就是对象。钓鱼的人(变量)的任务就是用某种方式(鱼儿引诱)把自己和鱼通过鱼线连接起来。那么,鱼是有类型的,有鲢鱼、鲫鱼、带鱼(带鱼也跑到湖水了了,难道是淡水带鱼?呵呵,就这么扯淡吧,别较真),钓鱼的人(变量)没有这种类型,他钓到不同类型的鱼。

这个比喻太烂了。凑合着理解吧。看官有好的比喻,别忘记分享。

同一个变量可以同时指向两个对象吗?绝对不能脚踩两只船。如果这样呢?

复制代码 代码如下:

>>> x = 4
>>> x = 5
>>> x
5

 变量x先指向了对象4,然后指向对象5,当后者放生的时候,自动跟第一个对象4接触关系。再看x,引用的对象就是5了。那么4呢?一旦没有变量引用它了,它就变成了孤魂野鬼。python是很吝啬的,它绝对不允许在内存中存在孤魂野鬼。凡是这些东西都被看做垃圾,而对垃圾,python有一个自动的收回机制。

在网上找了一个图示说明,很好,引用过来(来源:http://www.linuxidc.com/Linux/2012-09/69523.htm)

复制代码 代码如下:

>>> a = 100         #完成了变量a对内存空间中的对象100的引用

 如下图所示:

然后,又操作了:

复制代码 代码如下:

>>> a = "hello"

 如下图所示:

原来内存中的那个100就做为垃圾被收集了。而且,这个收集过程是python自动完成的,不用我们操心。

那么,python是怎么进行垃圾收集的呢?在Quora上也有人问这个问题,我看那个回答很精彩,做个链接,有性趣的读一读吧。Python (programming language): How does garbage collection in Python work?

is和==的效果

以上过程的原理搞清楚了,下面就可以深入一步了。

复制代码 代码如下:

>>> l1 = [1,2,3]
>>> l2 = l1

 这个操作中,l1和l2两个变量,引用的是一个对象,都是[1,2,3]。何以见得?如果通过l1来修改[1,2,3],l2引用对象也修改了,那么就证实这个观点了。

复制代码 代码如下:

>>> l1[0] = 99      #把对象变为[99,2,3]
>>> l1              #变了
[99, 2, 3]
>>> l2             #真的变了吔
[99, 2, 3]

 再换一个方式:

复制代码 代码如下:

>>> l1 = [1,2,3]
>>> l2 = [1,2,3]
>>> l1[0] = 99
>>> l1
[99, 2, 3]
>>> l2
[1, 2, 3]

 l1和l2貌似指向了同样的一个对象[1,2,3],其实,在内存中,这是两块东西,互不相关。只是在内容上一样。就好像是水里长的一样的两条鱼,两个人都钓到了,当不是同一条。所以,当通过l1修改引用对象的后,l2没有变化。

进一步还能这么检验:

复制代码 代码如下:

>>> l1
[1, 2, 3]
>>> l2
[1, 2, 3]
>>> l1 == l2    #两个相等,是指内容一样
True
>>> l1 is l2    #is 是比较两个引用对象在内存中的地址是不是一样
False          #前面的检验已经说明,这是两个东东

>>> l3 = l1   #顺便看看如果这样,l3和l1应用同一个对象
>>> l3
[1, 2, 3]
>>> l3 == l1
True
>>> l3 is l1    #is的结果是True
True

 某些对象,有copy函数,通过这个函数得到的对象,是一个新的还是引用到同一个对象呢?看官也可以做一下类似上面的实验,就晓得了。比如:

复制代码 代码如下:

>>> l1
[1, 2, 3]
>>> l2 = l1[:]
>>> l2
[1, 2, 3]
>>> l1[0] = 22
>>> l1
[22, 2, 3]
>>> l2
[1, 2, 3]

>>> adict = {"name":"qiwsir","web":"qiwsir.github.io"}
>>> bdict = adict.copy()
>>> bdict
{'web': 'qiwsir.github.io', 'name': 'qiwsir'}
>>> adict["email"] = "qiwsir@gmail.com"
>>> adict
{'web': 'qiwsir.github.io', 'name': 'qiwsir', 'email': 'qiwsir@gmail.com'}
>>> bdict
{'web': 'qiwsir.github.io', 'name': 'qiwsir'}

 不过,看官还有小心有点,python不总按照前面说的方式出牌,比如小数字的时候

复制代码 代码如下:

>>> x = 2
>>> y = 2
>>> x is y
True
>>> x = 200000
>>> y = 200000
>>> x is y      #什么道理呀,小数字的时候,就用缓存中的.
False

>>> x = 'hello'
>>> y = 'hello'
>>> x is y
True
>>> x = "what is you name?"
>>> y = "what is you name?"
>>> x is y      #不光小的数字,短的字符串也是
False

 赋值是不是简单地就是等号呢?从上面得出来,=的作用就是让变量指针指向某个对象。不过,还可以再深入一些。走着瞧吧。

时间: 2025-01-20 14:05:17

跟老齐学Python之深入变量和引用对象_python的相关文章

跟老齐学Python之从if开始语句的征程_python

一般编程的教材,都是要把所有的变量类型讲完,然后才讲语句.这种讲法,其实不符合学习的特点.学习,就是要循序渐进的.在这点上,我可以很吹一通了,因为我做过教师,研究教育教学,算是有一点心得的.所以,我在这里就开始讲授语句. 什么是语句 在前面,我们已经写了一些.py的文件,这些文件可以用python来运行.那些文件,就是由语句组成的程序. 为了能够严谨地阐述这个概念,我还是要抄一段维基百科中的词条:命令式编程 命令式编程(英语:Imperative programming),是一种描述电脑所需作出

跟老齐学Python之赋值,简单也不简单_python

变量命名 在<初识永远强大的函数>一文中,有一节专门讨论"取名字的学问",就是有关变量名称的问题,本温故而知新的原则,这里要复习: 名称格式:(下划线或者字母)+(任意数目的字母,数字或下划线) 注意:  1.区分大小写  2.禁止使用保留字  3.遵守通常习惯  •以单一下划线开头的变量名(_X)不会被from module import *语句导入的.  •前后有下划线的变量名(X)是系统定义的变量名,对解释器有特殊意义.  •以两个下划线开头,但结尾没有两个下划线的变

跟老齐学Python之大话题小函数(1)_python

开篇就要提到一个大的话题:编程范型.什么是编程范型?引用维基百科中的解释: 复制代码 代码如下: 编程范型或编程范式(英语:Programming paradigm),(范即模范之意,范式即模式.方法),是一类典型的编程风格,是指从事软件工程的一类典型的风格(可以对照方法学).如:函数式编程.程序编程.面向对象编程.指令式编程等等为不同的编程范型. 编程范型提供了(同时决定了)程序员对程序执行的看法.例如,在面向对象编程中,程序员认为程序是一系列相互作用的对象,而在函数式编程中一个程序会被看作是

跟老齐学Python之编写类之一创建实例_python

说明:关于类的这部分,我参考了<Learning Python>一书的讲解. 创建类 创建类的方法比较简单,如下: 复制代码 代码如下: class Person:  注意,类的名称一般用大写字母开头,这是惯例.当然,如果故意不遵循此惯例,也未尝不可,但是,会给别人阅读乃至于自己以后阅读带来麻烦.既然大家都是靠右走的,你就别非要在路中间睡觉了. 接下来,一般都要编写构造函数,在写这个函数之前,先解释一下什么是构造函数. 复制代码 代码如下: class Person:     def __in

跟老齐学Python之传说中的函数编写条规_python

关于函数的事情,总是说不完的,下面就罗列一些编写函数的注意事项.特别声明,这些事项不是我总结的,我是从一本名字为<Learning Python>的书里面抄过来的,顺便写成了汉语,当然,是按照自己的视角翻译的,里面也夹杂了一些自己的观点.看官也可以理解为源于<Learning Python>但又有点儿不同.  •函数具有独立性.也就是常说的不要有太强的耦合性.要让函数能够独立于外部的东西.参数和return语句就是实现这种独立性的最好方法.  •尽量不要使用全局变量,这也是让函数具

跟老齐学Python之大话题小函数(2)_python

上一讲和本讲的标题是"大话题小函数",所谓大话题,就是这些函数如果溯源,都会找到听起来更高大上的东西.这种思维方式绝对我坚定地继承了中华民族的优良传统的.自从天朝的臣民看到英国人开始踢足球,一直到现在所谓某国勃起了,都一直在试图论证足球起源于该朝的前前前朝的某国时代,并且还搬出了那时候的一个叫做高俅的球星来论证,当然了,勃起的某国是挡不住该国家队在世界杯征程上的阳痿,只能用高俅来意淫一番了.这种思维方式,我是坚定地继承,因为在我成长过程中,它一直被奉为优良传统.阿Q本来是姓赵的,和赵老

跟老齐学Python之关于循环的小伎俩_python

不是说while就不用,比如前面所列举而得那个猜数字游戏,在业务逻辑上,用while就更容易理解(当然是限于那个游戏的业务需要而言).另外,在某些情况下,for也不是简单地把对象中的元素遍历一遍,比如有有隔一个取一个的要求,等等. 在编写代码的实践中,为了对付循环中的某些要求,需要用一些其它的函数,比如前面已经介绍过的range就是一个被看做循环中的计数器的好东西. range 在<有容乃大的list(4)>中,专门对range()这个内置函数做了详细介绍,看官可以回到那节教程复习一番.这里重

跟老齐学Python之私有函数和专有方法_python

在任何语言中,都会规定某些对象(属性.方法.函数.类等)只能够在某个范围内访问,出了这个范围就不能访问了.这是"公"."私"之分.此外,还会专门为某些特殊的东西指定一些特殊表示,比如类的名字就不能用class,def等,这就是保留字.除了保留字,python中还为类的名字做了某些特殊准备,就是"专有"的范畴. 私有函数 在某些时候,会看到有一种方法命名比较特别,是以"__"双划线开头的,将这类命名的函数/方法称之为"

跟老齐学Python之关于类的初步认识_python

在开始部分,请看官非常非常耐心地阅读下面几个枯燥的术语解释,本来这不符合本教程的风格,但是,请看官谅解,因为列位将来一定要阅读枯燥的东西的.这些枯燥的属于解释,均来自维基百科. 1.问题空间 问题空间是问题解决者对一个问题所达到的全部认识状态,它是由问题解决者利用问题所包含的信息和已贮存的信息主动地构成的. 一个问题一般有下面三个方面来定义:  •初始状态--一开始时的不完全的信息或令人不满意的状况:  •目标状态--你希望获得的信息或状态:  •操作--为了从初始状态迈向目标状态,你可能采取的