从零学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基础教程之python消息摘要算法使用示例
Feb 10 Python
Python中用于转换字母为小写的lower()方法使用简介
May 19 Python
Django框架中render_to_response()函数的使用方法
Jul 16 Python
python利用rsa库做公钥解密的方法教程
Dec 10 Python
Python之reload流程实例代码解析
Jan 29 Python
解决新版Pycharm中Matplotlib图像不在弹出独立的显示窗口问题
Jan 15 Python
python使用Plotly绘图工具绘制柱状图
Apr 01 Python
Python 实现一行输入多个数字(用空格隔开)
Apr 29 Python
Python填充任意颜色,不同算法时间差异分析说明
May 16 Python
Python实现一个简单的毕业生信息管理系统的示例代码
Jun 08 Python
python字典key不能是可以是啥类型
Aug 04 Python
python字典进行运算原理及实例分享
Aug 02 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
Yii学习总结之数据访问对象 (DAO)
2015/02/22 PHP
php无法连接mysql数据库的正确解决方法
2016/07/01 PHP
Yii中的relations数据关联查询及统计功能用法详解
2016/07/14 PHP
php基于Redis消息队列实现的消息推送的方法
2018/11/28 PHP
php根据命令行参数生成配置文件详解
2019/03/15 PHP
javascript 获取图片尺寸及放大图片
2013/09/04 Javascript
jquery上传插件fineuploader上传文件使用方法(jquery图片上传插件)
2013/12/05 Javascript
js实现用户离开页面前提示是否离开此页面的方法(包括浏览器按钮事件)
2015/07/18 Javascript
AngularJS向后端ASP.NET API控制器上传文件
2016/02/03 Javascript
vue绑定class与行间样式style详解
2017/08/16 Javascript
判断滚动条滑到底部触发事件(实例讲解)
2017/11/15 Javascript
vue项目部署上线遇到的问题及解决方法
2018/06/10 Javascript
JavaScript面试技巧之数组的一些不low操作
2019/03/22 Javascript
javascript实现视频弹幕效果(两个版本)
2019/11/28 Javascript
JS+HTML5本地存储Localstorage实现注册登录及验证功能示例
2020/02/10 Javascript
如何修改Vue打包后文件的接口地址配置的方法
2020/04/22 Javascript
极简的Python入门指引
2015/04/01 Python
Python实现可获取网易页面所有文本信息的网易网络爬虫功能示例
2018/01/15 Python
Python读取excel中的图片完美解决方法
2018/07/27 Python
Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】
2019/03/18 Python
python pytest进阶之conftest.py详解
2019/06/27 Python
docker-py 用Python调用Docker接口的方法
2019/08/30 Python
python 经典数字滤波实例
2019/12/16 Python
Python和Sublime整合过程图示
2019/12/25 Python
Python如何在main中调用函数内的函数方式
2020/06/01 Python
使用sublime text3搭建Python编辑环境的实现
2021/01/12 Python
HTML5新增的表单元素和属性实例解析
2014/07/07 HTML / CSS
亿企通软件测试面试题
2012/04/10 面试题
金融专业个人求职信
2013/09/22 职场文书
期末自我鉴定
2014/01/23 职场文书
优秀体育委员自荐书
2014/01/31 职场文书
材料会计岗位职责
2014/03/06 职场文书
房屋买卖协议书范本
2014/04/10 职场文书
优秀创业计划书分享
2019/07/19 职场文书
手把手教你怎么用Python实现zip文件密码的破解
2021/05/27 Python
JavaScript设计模式之原型模式详情
2022/06/21 Javascript