只想回答一个问题: 当编译器要读取obj.field时, 发生了什么?

看似简单的属性访问, 其过程还蛮曲折的. 总共有以下几个step:

1. 如果obj 本身(一个instance )有这个属性, 返回. 如果没有, 执行 step 2

2. 如果obj 的class 有这个属性, 返回. 如果没有, 执行step 3.

3. 如果在obj class 的父类有这个属性, 返回. 如果没有, 继续执行3, 直到访问完所有的父类. 如果还是没有, 执行step 4.

4. 执行obj.__getattr__方法.

通过以下代码可以验证:

class A(object):

a = 'a'

class B(A):

b = 'b'

class C(B):

class_field = 'class field'

def getattr(self, f):

print('Method {}.getattr has been called.'.format(

self.class.name))

return f

c = C()

print c.a

print c.b

print c.class_field

print c.c

输出:

a

b

class field

Method C.__getattr__ has been called.

c

PS: python里的attribute与property不同, 当使用了property里, property的解析优先级最高. 详见blog:从attribute到property.

补充知识:深入理解python对象及属性

类属性和实例属性

首先来看看类属性和类实例的属性在python中如何存储,通过__dir__方法来查看对象的属性

>>> class Test(object):

pass

>>> test = Test()

# 查看类属性

>>> dir(Test)

['__class__','__delattr__','__dict__','__doc__','__format__',

'__getattribute__', '__hash__', '__init__', '__module__',

'__new__', '__reduce__', '__reduce_ex__', '__repr__',

'__setattr__', '__sizeof__', '__str__', '__subclasshook__',

'__weakref__']

# 查看实例属性

>>> dir(test)

['__class__', '__delattr__', '__dict__', '__doc__', '__format__',

'__getattribute__', '__hash__', '__init__', '__module__',

'__new__', '__reduce__', '__reduce_ex__', '__repr__',

'__setattr__', '__sizeof__', '__str__', '__subclasshook__',

'__weakref__']

我们主要看一个属性__dict__,因为 __dict__保存的对象的属性,看下面一个例子

>>> class Spring(object):

... season = "the spring of class"

...

查看Spring类保存的属性

>>> Spring.dict

dict_proxy({'dict': ,

'season': 'the spring of class',

'module': 'main',

'weakref': ,

'doc': None})

通过两种方法访问类属性

>>> Spring.dict['season']

'the spring of class'

>>> Spring.season

'the spring of class'

发现__dict__有个'season'键,这就是这个类的属性,其值就是类属性的数据.

接来看,看看它的实例属性

>>> s = Spring()

# 实例属性的__dict__是空的

>>> s.__dict__

{}

# 其实是指向的类属性

>>> s.season

'the spring of class'

建立实例属性

>>> s.season = "the spring of instance"

这样,实例属性里面就不空了。这时候建立的实例属性和类属性重名,并且把它覆盖了

>>> s.dict

{'season': 'the spring of instance'}

>>> s.dict['season']

'the spring of instance'

>>> s.season

'the spring of instance'

类属性没有受到实例属性的影响

>>> Spring.dict['season']

'the spring of class'

>>> Spring.dict

dict_proxy({'dict': , 'season': 'the spring of class', 'module': 'main', 'weakref': , 'doc': None})

如果将实例属性删除,又会调用类属性

>>> del s.season

>>> s.dict

{}

>>> s.season

'the spring of class'

自定义实例属性,对类属性没有影响

>>> s.lang = "python"

>>> s.dict

{'lang': 'python'}

>>> s.dict['lang']

'python'

修改类属性

>>> Spring.flower = "peach"

>>> Spring.dict

dict_proxy({'module': 'main',

'flower': 'peach',

'season': 'the spring of class',

'dict': , 'weakref': , 'doc': None})

>>> Spring.dict['flower']

'peach'

实例中的__dict__并没有变化

>>> s.dict

{'lang': 'python'}

实例中找不到flower属性,调用类属性

>>> s.flower

'peach'

下面看看类中包含方法,__dict__如何发生变化

# 定义类

>>> class Spring(object):

... def tree(self, x):

... self.x = x

... return self.x

...

# 方法tree在__dict__里面

>>> Spring.__dict__

dict_proxy({'__dict__': ,

'__weakref__': ,

'__module__': '__main__',

'tree': ,

'__doc__': None})

>>> Spring.__dict__['tree']

建立实例,但是__dict__中没有方法

>>> t = Spring()

>>> t.dict

{}

执行方法

>>> t.tree("xiangzhangshu")

'xiangzhangshu'

实例方法(t.tree('xiangzhangshu'))的第一个参数(self,但没有写出来)绑定实例 t,透过 self.x 来设定值,即给 t.__dict__添加属性值。

>>> t.dict

{'x': 'xiangzhangshu'}

如果没有将x 赋值给 self 的属性,而是直接 return,结果发生了变化

>>> class Spring(object):

... def tree(self, x):

... return x

>>> s = Spring()

>>> s.tree("liushu")

'liushu'

>>> s.dict

{}

需要理解python中的一个观点,一切都是对象,不管是类还是实例,都可以看成是对象,符合object.attribute ,都会有自己的属性

使用__slots__优化内存使用

默认情况下,python在各个实例中为名为__dict__的字典里存储实例属性,而字典会消耗大量内存(字典要使用底层散列表提升访问速度), 通过__slots__类属性,在元组中存储实例属性,不用字典,从而节省大量内存

# 在类中定义__slots__属性就是说这个类中所有实例的属性都在这儿了,如果几百万个实例同时活动,能节省大量内存

>>> class Spring(object):

... __slots__ = ("tree", "flower")

...

# 仔细看看 dir() 的结果,还有__dict__属性吗?没有了,的确没有了。也就是说__slots__把__dict__挤出去了,它进入了类的属性。

>>> dir(Spring)

['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flower', 'tree']

>>> Spring.__slots__

('tree', 'flower')

# 实例化

>>> t = Spring()

>>> t.__slots__

('tree', 'flower')

通过类赋予属性值

>>> Spring.tree = "liushu"

tree这个属性是只读的, 实例不能修改

>>> t.tree = "guangyulan"

Traceback (most recent call last):

File "", line 1, in

AttributeError: 'Spring' object attribute 'tree' is read-only

>>> t.tree

'liushu'

对于用类属性赋值的属性,只能用来修改

>>> Spring.tree = "guangyulan"

>>> t.tree

'guangyulan'

对于没有用类属性赋值的属性,可以通过实例来修改

>>> t.flower = "haitanghua"

>>> t.flower

'haitanghua'

实例属性的值并没有传回到类属性,你也可以理解为新建立了一个同名的实例属性

>>> Spring.flower

如果再给类属性赋值

>>> Spring.flower = "ziteng"

>>> t.flower

'ziteng'

如果使用的当,__slots__可以显著节省内存,按需要注意一下问题

在类中定义__slots__之后,实例不能再有__slots__所列名称之外的其他属性

每个子类都要定义__slots__熟悉,因为解释器会忽略继承__slots__属性

如果不把__werkref__加入__slots__,实例不能作为弱引用的目标

属性的魔术方法

来看几个魔术方法

__setattr__(self,name,value):如果要给 name 赋值,就调用这个方法。

__getattr__(self,name):如果 name 被访问,同时它不存在的时候,此方法被调用。

__getattribute__(self,name):当 name被访问时自动被调用(注意:这个仅能用于新式类),无论 name 是否存在,都要被调用。

__delattr__(self,name):如果要删除 name,这个方法就被调用。

>>> class A(object):

... def __getattr__(self, name):

... print "You use getattr"

... def __setattr__(self, name, value):

... print "You use setattr"

... self.__dict__[name] = value

# a.x,按照本节开头的例子,是要报错的。但是,由于在这里使用了__getattr__(self, name) 方法,当发现 x 不存在于对象的__dict__中的时候,就调用了__getattr__,即所谓“拦截成员”。

>>> a = A()

>>> a.x

You use getattr

给对象的属性赋值时候,调用了__setattr__(self, name, value)方法,这个方法中有一句 self.dict[name] = value,通过这个语句,就将属性和数据保存到了对象的__dict__中

>>> a.x = 7

You use setattr

测试__getattribute__(self,name)

>>> class B(object):

... def getattribute(self, name):

... print "you are useing getattribute"

... return object.getattribute(self, name)

返回的内容用的是 return object.getattribute(self, name),而没有使用 return self.dict[name]。因为如果用这样的方式,就是访问 self.dict,只要访问这个属性,就要调用`getattribute``,这样就导致了无限递归

访问不存在的成员,可以看到,已经被__getattribute__拦截了,虽然最后还是要报错的。

>>> b = B()

>>> b.y

you are useing getattribute

Traceback (most recent call last):

File "", line 1, in

File "", line 4, in getattribute

AttributeError: 'B' object has no attribute 'y'

Property函数

porperty可以作为装饰器使用把方法标记为特性

class Vector(object):

def __init__(self, x, y):

# 使用两个前导下划线,把属性标记为私有

self.__x = float(x)

self.__y = float(y)

porperty装饰器把读值方法标记为特性

@property

def x(self):

return self.__x

@property

def y(self):

return self.__y

vector = Vector(3,4)

print(vector.x, vector.y)

使用property可以将函数封装为属性

class Rectangle(object):

"""

the width and length of Rectangle

"""

def __init__(self):

self.width = 0

self.length = 0

def setSize(self, size):

self.width, self.length = size

def getSize(self):

return self.width, self.length

if name == "main":

r = Rectangle()

r.width = 3

r.length = 4

print r.getSize() # (3,4)

r.setSize( (30, 40) )

print r.width # 30

print r.length # 40

这段代码可以正常运行,但是属性的调用方式可以改进,如下:

class Rectangle(object):

"""

the width and length of Rectangle

"""

def __init__(self):

self.width = 0

self.length = 0

def setSize(self, size):

self.width, self.length = size

def getSize(self):

return self.width, self.length

使用property方法将函数封装为属性,更优雅

size = property(getSize, setSize)

if name == "main":

r = Rectangle()

r.width = 3

r.length = 4

print r.size # (30, 40)

r.size = 30, 40

print r.width # 30

print r.length # 40

使用魔术方法实现:

class NewRectangle(object):

def __init__(self):

self.width = 0

self.length = 0

def setattr(self, name, value):

if name == 'size':

self.width, self, length = value

else:

self.dict[name] = value

def getattr(self, name):

if name == 'size':

return self.width, self.length

else:

raise AttrubuteErrir

if name == "main":

r = Rectangle()

r.width = 3

r.length = 4

print r.size # (30, 40)

r.size = 30, 40

print r.width # 30

print r.length # 40

属性的获取顺序

最后我们来看看熟悉的获得顺序:通过实例获取其属性,如果在__dict__中有相应的属性,就直接返回其结果;如果没有,会到类属性中找。

看下面一个例子:

class A(object):

author = "qiwsir"

def __getattr__(self, name):

if name != "author":

return "from starter to master."

if name == "main":

a = A()

print a.author # qiwsir

print a.lang # from starter to master.

当 a = A() 后,并没有为实例建立任何属性,或者说实例的__dict__是空的。但是如果要查看 a.author,因为实例的属性中没有,所以就去类属性中找,发现果然有,于是返回其值 “qiwsir”。但是,在找 a.lang的时候,不仅实例属性中没有,类属性中也没有,于是就调用了__getattr__()方法。在上面的类中,有这个方法,如果没有__getattr__()方法呢?如果没有定义这个方法,就会引发 AttributeError,这在前面已经看到了。

以上这篇Python对象的属性访问过程详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持菜鸟教程www.piaodoo.com。

python属性使用教程_Python对象的属性访问过程详解相关推荐

  1. python布尔类型运算_Python对象类型及其运算方法(详解)

    基本要点: 程序中储存的所有数据都是对象(可变对象:值可以修改 不可变对象:值不可修改) 每个对象都有一个身份.一个类型.一个值 例: >>> a1 = 'abc' >> ...

  2. python如何制作脚本_用python给自己做一款小说阅读器过程详解

    前言 前一段时间书荒的时候,在喜马拉雅APP发现一个主播播讲的小说-大王饶命.听起来感觉很好笑,挺有意思的,但是只有前200张是免费的,后面就要收费.一章两毛钱,本来是想要买一下,发现说的进度比较慢而 ...

  3. python深拷贝一个对象_Python对象的深拷贝和浅拷贝详解

    本文内容是在<Python核心编程2>上看到的,感觉很有用便写出来,给大家参考参考! 浅拷贝 首先我们使用两种方式来拷贝对象,一种是切片,另外一种是工厂方法.然后使用id函数来看看它们的标 ...

  4. python中exchange函数使用_python基于exchange函数发送邮件过程详解

    python基于exchange函数发送邮件过程详解 作者: shuzihua 更新时间:2020-11-06 10:40:35 原文链接 1.Python hasattr() 函数 描述 hasat ...

  5. python查看模块功能_Python进阶之inspect模块使用详解

    前几篇内容我们详细探讨了如何从Python中获取帮助信息: 前情回顾 1.查看模块.类提供了哪些接口: 需要帮助吗?dir函数的孪生兄弟,Python中魔法方法__dir__详解 2.查看对象内部属性 ...

  6. python zipfile教程_Python中的zipfile模块使用详解

    zip文件格式是通用的文档压缩标准,在ziplib模块中,使用ZipFile类来操作zip文件,下面具体介绍一下: class zipfile.ZipFile(file[, mode[, compre ...

  7. python模拟登录详细教程_Python模拟登录requests.Session应用详解

    最近由于某些原因,需要用到Python模拟登录网站,但是以前对这块并不了解,而且目标网站的登录方法较为复杂, 所以一下卡在这里了,于是我决定从简单的模拟开始,逐渐深入地研究下这块. 注:本文仅为交流学 ...

  8. python封装的方法_Python封装原理与实现方法详解

    本文实例讲述了Python封装原理与实现方法.分享给大家供大家参考,具体如下: [封装] 隐藏对象的属性和实现细节,仅对外提供公共访问方式. [好处] 1. 将变化隔离: 2. 便于使用: 3. 提高 ...

  9. python迭代器创建序列_Python 中迭代器与生成器实例详解

    Python 中迭代器与生成器实例详解 本文通过针对不同应用场景及其解决方案的方式,总结了Python中迭代器与生成器的一些相关知识,具体如下: 1.手动遍历迭代器 应用场景:想遍历一个可迭代对象中的 ...

最新文章

  1. 6、WHERE:条件查询数据
  2. 前端框架-Bootstrap
  3. 浅谈疫情下的就业形势
  4. java反码算术运算求和,位运算的妙用,运算妙用
  5. (JAVA) * 使用正则表达式,给字符串排序 * 使用数组排序
  6. python中的文件I/O
  7. 【java基础知识】linux运行或停止jar包程序
  8. csharp语言_电脑绝技教你22天学精Csharp之第五天
  9. 为tomcat分配内存
  10. 996.ICU凉凉了!
  11. DRY(Don't Repeat Yourself )原则
  12. 局域网电话软件系统功能与应用
  13. 数据库原理第三章习题作业
  14. 【模拟电路】温度对器件特性的影响
  15. 原理 | 分布式链路跟踪组件 SOFATracer 和 Zipkin 模型转换
  16. 赋值运算和赋值表达式
  17. 计算机处理器哪个最好,电脑处理器,哪个比较好
  18. matlab示波器导出csv数据,示波器CSV波形数据导入Matlab进行FFT分析
  19. 尚硅谷Java零基础极速入门七天版笔记
  20. 网页视频播放速度修改器,亲测可用

热门文章

  1. 2-Qt6命令行控制台项目
  2. java竞拍系统代码,网上拍卖系统的设计与实现(源代码及全套资料).doc
  3. 鼠标键盘唤醒计算机,除了按下电源按钮唤醒计算机,WIN10也可以使用鼠标或键盘来唤醒...
  4. python2安装pyyaml_Python3安装Pyyaml
  5. onuninitialized和ajax,12.3 Prototype对Ajax的支持
  6. arm linux考勤,定稿毕业论文_基于ARM与Linux的员工刷卡考勤系统喜欢就下吧(范文1)...
  7. 中职计算机专业选修课程,中职学校计算机专业选修课开设的实践与研究
  8. lr与svm如何选择-面试
  9. cocos2d实现语音_Cocos2d-x 3.2 Lua示例CocosDenshionTest(音频测试)
  10. php 发送表格,PHP邮件表格,带有使用AJAX发送的单选按钮