从零学Python之引用和类属性的初步理解


Posted in Python onMay 15, 2014

Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。自从20世纪90年代初Python语言诞生至今,它逐渐被广泛应用于处理系统管理任务和Web编程。Python已经成为最受欢迎的程序设计语言之一。2011年1月,它被TIOBE编程语言排行榜评为2010年度语言。自从2004年以后,python的使用率是呈线性增长。

Python在设计上坚持了清晰划一的风格,这使得Python成为一门易读、易维护,并且被大量用户所欢迎的、用途广泛的语言。

鉴于以上各种优点,忍不住对Python进行了一番学习,略有收获,分享给大家。

最近对Python 的对象引用机制稍微研究了一下,留下笔记,以供查阅。

首先有一点是明确的:「Python 中一切皆对象」。

那么,这到底意味着什么呢?

如下代码:

#!/usr/bin/env python
a = [0, 1, 2] # 来个简单的list
# 最初,list 和其中各个元素的id 是这样的。
print 'origin'
print id(a),a
for x in a:
    print id(x), x
print '----------------------'
# 我们把第一个元素改改
print 'after change a[0]'
a[0] = 4
print id(a),a
for x in a:
    print id(x), x
print '----------------------'
# 我们再把第二个元素改改
print 'after change a[1]'
a[1] = 5
print id(a),a
for x in a:
    print id(x), x
print '----------------------'
# 回头看看直接写个0 ,id是多少
print 'how about const 0?'
print id(0), 0

运行结果如下:

PastgiftMacbookPro:python pastgift$ ./refTest.py 
Origin
[0, 1, 2]
0
1
2
----------------------
after change a[0]
[4, 1, 2]
4
1
2
----------------------
after change a[1]
[4, 5, 2]
4
5
2
----------------------
how about const 0?
0

从「Origin」部分来看,list 中各个元素的地址之间都正好相差24,依次指向各自的数据——这让我想到了数组。

当修改a[0] 的值之后,发现,a[0] 的地址发生了变化。也就是说,赋值语句实际上只是让a[0] 重新指向另一个对象而已。此外,还注意到,a[0] 的地址和a[2]的地址相差48(2个24)。

当再次修改a[1] 之后,同样地,a[1] 的地址也发生变化,有趣的是,这次a[1] 的地址和a[0] 的地址又相差24,和原先的a[2] 相差72(3个24)。

最后,当直接把数字0的地址打印出来后,发现它的地址和最开始的a[0] 的地址完全一样。

至此,基本可以说明,就算是list 中的元素,其实也是引用。修改list 中的元素,实际上还是在修改引用而已。

 

对于Python 中类属性,有人提到过「类属性在同一类及其子类之间共享,修改类属性会影响到同一类及其子类的所有对象」。

听着挺吓人,但仔细研究之后,其实倒也不是什么大不了的事情。

如下代码:

#!/usr/bin/env python
class Bird(object):
    name = 'bird'
    talent = ['fly']
class Chicken(Bird):
    pass
bird = Bird();
bird2 = Bird(); # 同类实例
chicken = Chicken(); # 子类实例
# 最开始是这样的
print 'Original attr'
print id(bird.name),      bird.name
print id(bird.talent),    bird.talent
print id(bird2.name),     bird2.name
print id(bird2.talent),   bird2.talent
print id(chicken.name),   chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 换个名字看看
bird.name = 'bird name changed!'
print 'after changing name'
print id(bird.name),      bird.name
print id(bird.talent),    bird.talent
print id(bird2.name),     bird2.name
print id(bird2.talent),   bird2.talent
print id(chicken.name),   chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 洗个天赋试试(修改类属性中的元素)
bird.talent[0] = 'walk'
print 'after changing talent(a list)'
print id(bird.name),      bird.name
print id(bird.talent),    bird.talent
print id(bird2.name),     bird2.name
print id(bird2.talent),   bird2.talent
print id(chicken.name),   chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 换个新天赋树(整个类属性全换掉)
bird.talent = ['swim']
print 'after reassign talent'
print id(bird.name),      bird.name
print id(bird.talent),    bird.talent
print id(bird2.name),     bird2.name
print id(bird2.talent),   bird2.talent
print id(chicken.name),   chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 洗掉新天赋树(对新来的类属性中的元素进行修改)
bird.talent[0] = 'dance'
print 'changing element after reassigning talent'
print id(bird.name),      bird.name
print id(bird.talent),    bird.talent
print id(bird2.name),     bird2.name
print id(bird2.talent),   bird2.talent
print id(chicken.name),   chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'

运行结果:

PastgiftMacbookPro:python pastgift$ ./changeAttributeTest.py 
Original attr
bird
['fly']
bird
['fly']
bird
['fly']
----------------------------
after changing name
bird name changed!
['fly']
bird
['fly']
bird
['fly']
----------------------------
after changing talent(a list)
bird name changed!
['walk']
bird
['walk']
bird
['walk']
----------------------------
after reassign talent
bird name changed!
['swim']
bird
['walk']
bird
['walk']
----------------------------
changing element after reassigning talent
bird name changed!
['dance']
bird
['walk']
bird
['walk']
----------------------------

在「Origin」的时候,同类对象,子类对象的相同类属性的地址都是相同的——这就是所谓的「共享」。

修改name 之后,只有被修改的对象name 属性发生变化。这是因为对name的赋值操作实际上就是换了一个字符串,重新引用。字符串本身并没有发生变化。所以并没有在同类和子类之间产生互相影响。

接下来,修改talent 中的元素。这时,情况有所改变:同类及其子类的talent 属性都一起跟着变了——这很好理解,因为它们都引用的内存地址都一样,引用的是同一个对象。

再接下来,给talent 重新赋值,也就是改成引用另外一个对象。结果是只有本实例的talent 属性变化了。从内存地址可以看出,本实例和其他实例的talent 属性已经不再指向相同的对象了。就是说「至此,本实例已经是圈外人士了」。

那么,最后再次修改talent 中元素后,对其他实例无影响的结果也是很好理解了。因为已经是「圈外人士」了嘛,我再怎么折腾也都是自己的事情了。

所以,「类属性在同类及其子类之间互相影响」必须有一个前提条件:实例建立后,其类属性从来没有被重新赋值过,即类属性依然指向最初所指向的内存地址。

最后提一下对象属性

如下代码:

#!/usr/bin/env python
class Bird(object):
    def __init__(self):
        self.talent = ['fly']
bird = Bird()
bird2 = Bird()
# 刚开始的情形
print 'Origin'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'
# 修改其中一个对象的属性
bird.talent[0] = 'walk'
print 'after changing attribute'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'
# 作死:两个对象的属性指向同一个内存地址,再修改
bird.talent = bird2.talent
bird.talent[0] = 'swim'
print 'assign to another attribute and change it'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'

运行结果:

PastgiftMacbookPro:python pastgift$ ./changeAttributeTest2.py 
Origin
['fly']
['fly']
--------------------
after changing attribute
['walk']
['fly']
--------------------
assign to another attribute and change it
['swim']
['swim']
--------------------

由于对象属性就算内容完全一样(刚初始化后的属性内容一般都是一样的),也会分配到完全不同的内存地址上去。所以不存在「同类对象之间影响」的情况。

但如果让一个对象的属性和另一个对象的属性指向同一个地址,两者之间(但也仅限两者之间)便又互相牵连起来。

Python 相关文章推荐
Python将xml和xsl转换为html的方法
Mar 10 Python
HTML中使用python屏蔽一些基本功能的方法
Jul 07 Python
Python使用统计函数绘制简单图形实例代码
May 15 Python
了解不常见但是实用的Python技巧
May 23 Python
python设计微型小说网站(基于Django+Bootstrap框架)
Jul 08 Python
Python 堆叠柱状图绘制方法
Jul 29 Python
wxPython实现分隔窗口
Nov 19 Python
Python3 使用map()批量的转换数据类型,如str转float的实现
Nov 29 Python
Python matplotlib画曲线例题解析
Feb 07 Python
TensorFlow获取加载模型中的全部张量名称代码
Feb 11 Python
使用Python第三方库pygame写个贪吃蛇小游戏
Mar 06 Python
python playwright 自动等待和断言详解
Nov 27 Python
python中xrange和range的区别
May 13 #Python
Python中os和shutil模块实用方法集锦
May 13 #Python
Python中的jquery PyQuery库使用小结
May 13 #Python
Python getopt模块处理命令行选项实例
May 13 #Python
Python random模块(获取随机数)常用方法和使用例子
May 13 #Python
Python自动化测试工具Splinter简介和使用实例
May 13 #Python
Python获取远程文件大小的函数代码分享
May 13 #Python
You might like
一个SQL管理员的web接口
2006/10/09 PHP
去除php注释和去除空格函数分享
2014/03/13 PHP
php实现不通过扩展名准确判断文件类型的方法【finfo_file方法与二进制流】
2017/04/18 PHP
基于PHP实现微信小程序客服消息功能
2019/08/12 PHP
javascript json 新手入门文档
2009/12/03 Javascript
jQuery 表单验证扩展代码(一)
2010/10/11 Javascript
iframe 父窗口和子窗口相互的调用方法集锦
2010/12/15 Javascript
jQuery使用技巧简单汇总
2013/04/18 Javascript
动态读取JSON解析键值对的方法
2014/06/03 Javascript
JavaScript中iframe实现局部刷新的几种方法汇总
2016/01/06 Javascript
Bootstrap与KnockoutJs相结合实现分页效果实例详解
2016/05/03 Javascript
JS实现简易刻度时钟示例代码
2017/03/11 Javascript
基于Vue2.0+ElementUI实现表格翻页功能
2017/10/23 Javascript
实现div内部滚动条滚动到底部和顶部的代码
2017/11/15 Javascript
Vue使用NPM方式搭建项目
2018/10/25 Javascript
微信小程序实现一个简单swiper代码实例
2019/12/30 Javascript
Nodejs + Websocket 指定发送及群聊的实现
2020/01/09 NodeJs
Javascript实现打鼓效果
2021/01/29 Javascript
[00:11]战神迅矛
2019/03/06 DOTA
Python列表(list)常用操作方法小结
2015/02/02 Python
Python简单删除目录下文件以及文件夹的方法
2015/05/27 Python
python+opencv轮廓检测代码解析
2018/01/05 Python
PyCharm+PySpark远程调试的环境配置的方法
2018/11/29 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
2019/01/29 Python
Django Celery异步任务队列的实现
2019/07/24 Python
Spring Cloud Feign高级应用实例详解
2019/12/10 Python
努比亚手机官网:nubia
2016/10/06 全球购物
你常见到的runtime exception
2016/09/05 面试题
char型变量中能不能存贮一个中文汉字
2015/07/08 面试题
如何获得EntityManager
2014/02/09 面试题
机电一体化毕业生求职信
2013/11/02 职场文书
奶茶专卖店创业计划书
2014/01/18 职场文书
《夹竹桃》教学反思
2014/04/20 职场文书
媒矿安全生产承诺书
2014/05/23 职场文书
2015年暑期见闻
2015/07/14 职场文书
微软Win11什么功能最惊艳? Windows11新功能特性汇总
2021/11/21 数码科技