浅谈python类属性的访问、设置和删除方法


Posted in Python onJuly 25, 2016

类属性和对象属性

我们把定义在类中的属性称为类属性,该类的所有对象共享类属性,类属性具有继承性,可以为类动态地添加类属性。

对象在创建完成后还可以为它添加额外的属性,我们把这部分属性称为对象属性,对象属性仅属于该对象,不具有继承性。

类属性和对象属性都会被包含在dir()中,而vars()是仅包含对象属性。vars()跟__dict__是等同的。

类属性和对象属性可类比于Java中的static成员和非static成员,只不python中的类属性和对象属性都是可以动态添加(和删除)的。

class A(object):

  name='orisun'

  def __init__(self):
    self.age=10

class B(A):

  city='bei jing'

  def __init__(self):
    self.tempurature=20

if __name__ == '__main__':
  a=A()
  print dir(A)
  print dir(a)
  print a.__dict__
  print vars(a)

  print 
  b=B()
  print dir(B)
  print dir(b)
  print b.__dict__
  print vars(b)

输出

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
{'age': 10}
{'age': 10}

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'city', 'name']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'city', 'name', 'tempurature']
{'tempurature': 20}
{'tempurature': 20}

动态地为类添加类属性后,该类的所有对象也都添加了该属性(即使是动态添加类属性之前创建的对象)。通过实例修改属性,并不会影响其他实例的同名属性和类上的同名属性。

class A(object):

  name='orisun'

  def __init__(self):
    self.age=10

if __name__ == '__main__':
  a=A()
  print dir(a)
  A.city='BeiJing'  #动态添加类属性,会反应到所有对象上
  b=A()
  A.name='zcy'    #动态修改类属性,会反应到所有对象上
  print dir(b)
  print dir(a)
  print a.name    
  b.name='tom'    #通过实例修改属性,并不会影响其他实例的同名属性和类上的同名属性
  print a.name
  print A.name
  print b.name

输出

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'city', 'name']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'city', 'name']
zcy
zcy
zcy
tom

下文中讨论的全部是类属性,不涉及对象属性。

对属性的访问、设置和删除又分为2种情况:

1.通过对象(实例)访问、设置和删除属性,即obj.attr、obj.attr=val、del obj.attr

2.通过类访问、设置和删除属性,即Cls.attr、Cls.attr=val、del Cls.attr

本文将针对这2种情况分别讨论。

Descriptor

一个Descriptor是指实现了__get__的类,实现__set__和__delete__是可选的。同时实现了__get__和__set__则称为Data Descriptor,如果只实现了__get__则称为Non-data Descriptor。

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__
  def __set__(self,obj,val):
    pass
  def __delete__(self,obj):
    pass

先给一个Descriptor的示例,__get__、__set__、__delete__的作用后文再细讲。

通过实例访问属性

__getattribute__、__getattr__、__get__和__dict__[attr]都是跟属性访问相关的方法,它们的优先级:

1.当类中定义了__getattribute__方法时,则调用__getattribute__。

2.如果访问的属性存在,且

2.1  属性是个Descriptor,是调用这个属性的__get__

2.2 属性不是Descriptor,则调用__dict__[attr]

3.如果类中没有定义该属性,则调用__getattr__

4.否则,抛出异常AttributeError 

验证4

class A(object):
  pass

if __name__ == '__main__':
  a=A()
  print a.d

输出:

AttributeError: 'A' object has no attribute 'd'

验证3

 

class A(object):
  def __getattr__(self,name):
    return name+" not found in "+self.__class__.__name__+" object"


if __name__ == '__main__':
  a=A()
  print a.d

 

输出:

d not found in A object

 验证2.1

 

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__
  

class A(object):
  d=Descriptor()
  def __getattr__(self,name):
    return name+" not found in "+self.__class__.__name__+" object"


if __name__ == '__main__':
  a=A()
  print a.d

 

输出:

Descriptor in A

__getattr__并没有被调用。

验证2.2

class A(object):
  d=10
  def __getattr__(self,name):
    return name+" not found in "+self.__class__.__name__+" object"


if __name__ == '__main__':
  a=A()
  print a.d

输出:

10

__getattr__并没有被调用。

验证1

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__
  

class A(object):
  d=Descriptor()
  def __getattribute__(self,name):
    return '__getattribute__ '
  def __getattr__(self,name):
    return name+" not found in "+self.__class__.__name__+" object"


if __name__ == '__main__':
  a=A()

输出:

__getattribute__

__get__和__getattr__并没有被调用。

通过实例设置属性

跟属性设置相关的方法有3个:__setattr__、__set__和__dict__[attr]=val。它们的优先级跟get正好反过来:

1.如果类中定义了__setattr__方法,则直接调用__setattr__

2.如果赋值的属性是个Descriptor,且

2.1  该Descriptor中定义了__set__,则直接调用__set__

2.2  该Descriptor中没有定义__set__,则调用__dict__[attr]=val

3.如果赋值的属性不是Descriptor,则直接调用__dict__[attr]=val

4.如果该属性不存在,则动态地添加该属性,然后调用__dict__[attr]=val进行赋值

验证4

class A(object):
  pass

if __name__ == '__main__':
  a=A()
  a.d='hello'
  print a.d

输出:

hello

验证3

class A(object):
  d=10

if __name__ == '__main__':
  a=A()
  a.d=30
  print a.d

输出:

30

验证2.2

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__

class A(object):
  d=Descriptor()

if __name__ == '__main__':
  a=A()
  a.d=30
  print a.d

输出:

30

验证2.1

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__

  def __set__(self,instance,value):
    pass

class A(object):
  d=Descriptor()

if __name__ == '__main__':
  a=A()
  a.d=30
  print a.d

输出

Descriptor in A

因为代码“a.d=30”调用了__set__,而__set__又什么都没做,所以属性d还是Descriptor对象(而非30),那么在执行"print a.d"时自然就调到了__get__

验证1

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__

  def __set__(self,instance,value):
    print '__set__'

class A(object):
  d=Descriptor()

  def __setattr__(self,name,value):
    print '__setattr__'

if __name__ == '__main__':
  a=A()
  a.d=30
  print a.d

输出

__setattr__
Descriptor in A

调用了__setattr__,而__set__并没有被调到。

通过实例删除属性

调用del instance.attr进行属性删除时可能会调到__delattr__或__delete__,它们的优先级跟set雷同。

1.如果类中定义了__delattr__方法,则直接调用__delattr__

2.如果赋值的属性是个Descriptor,且该Descriptor中定义了__delete__,则直接调用__delete__

3.如果赋值的属性是个Descriptor,且该Descriptor中没有定义__delete__,则会报异常AttributeError:属性是只读的

4.如果赋值的属性不是Descriptor,也会报异常AttributeError:属性是只读的

5.如果该属性不存在,则报异常AttributeError

验证5

class A(object):
  pass

if __name__ == '__main__':
  a=A()
  del a.d

输出

AttributeError: 'A' object has no attribute 'd'

验证4

class A(object):
  d=10

if __name__ == '__main__':
  a=A()
  del a.d

输出

AttributeError: 'A' object attribute 'd' is read-only

验证3

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__


class A(object):
  d=Descriptor()

if __name__ == '__main__':
  a=A()
  del a.d

输出

AttributeError: 'A' object attribute 'd' is read-only

验证2

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__

  def __delete__(self,instance):
    print '__delete__'

class A(object):
  d=Descriptor()

if __name__ == '__main__':
  a=A()
  del a.d

输出

__delete__

验证1

class Descriptor(object):
  
  def __get__(self,instance,owner):
    return 'Descriptor in '+owner.__name__

  def __delete__(self,instance):
    print '__delete__'

class A(object):
  d=Descriptor()

  def __delattr__(self,name):
    print '__delattr__'

if __name__ == '__main__':
  a=A()
  del a.d

输出

__delattr__

__delete__并没有被调用。

__get__  __set__  __delete__参数说明

class Descriptor(object):
  
  def __get__(self,obj,owner):
    return '__get__',self,obj,owner

  def __set__(self,obj,val):
    print '__set__',self,obj,val

  def __delete__(self,obj):
    print '__delete__',self,obj
  

class A(object):
  d=Descriptor()

if __name__ == '__main__':
  a=A()
  print a.d
  a.d=3
  del a.d

输出

('__get__', <__main__.Descriptor object at 0x100481c10>, <__main__.A object at 0x1004a0fd0>, <class '__main__.A'>)
__set__ <__main__.Descriptor object at 0x100481c10> <__main__.A object at 0x1004a0fd0> 3
__delete__ <__main__.Descriptor object at 0x100481c10> <__main__.A object at 0x1004a0fd0>

可见,3个方法参数中的obj是Descriptor属性所在的对象,而owner参数(__get__中的owner参数)是该对象所属的类。

在上面的讨论中我们是通过实例操作属性,如果你作一下对应转换:"实例转换到类,类转换到MetaClass",那就是通过类操作属性的规则。这种对应转换也是容易理解的,应该类是用于创建对象的,而MetaClass是用于创建类的。

class MetaClass(object):
  pass 

class A(object):
  __metaclass__=MetaClass

通过类访问属性

通过A.attr访问属性的规则为:

1.如果MetaClass中有__getattribute__,则直接返回该__getattribute__的结果。

2.如果attr是个Descriptor,则直接返回Descriptor的__get__的结果。

3.如果attr是通过属性,则直接返回attr的值

4.如果类中没有attr,且MetaClass中定义了__getattr__,则调用MetaClass中的__getattr__

5.如果类中没有attr,且MetaClass中没有定义__getattr__,则抛出异常AttributeError

通过类设置属性

通过A.attr=val给属性赋值时:

1.如果MetaClass中定义了__setattr__,则执行该__setattr__

2.如果该属性是Descriptor,且定义了__set__,则执行Descriptor的__set__

3.如果是普通属性或None-data Descriptor,则直接令attr=val

4.如果属性不存在,则动态给类添加该属性,然后进行赋值

通过类删除属性

通过del A.attr删除属性时:

1.如果MetaClass中定义了__delattr__,则执行该__delattr__

2.如果该属性是Descriptor,且定义了__delete__,则执行Descriptor的__delete__

3.如果是普通属性,或虽是Descriptor但是没有定义__delete__,则直接从A.__dict__中删除该属性

4.如果属性不存在,则抛出异常AttributeError

以上这篇浅谈python类属性的访问、设置和删除方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中合并两个文本文件并按照姓名首字母排序的例子
Apr 25 Python
python实现读取命令行参数的方法
May 22 Python
Python和Perl绘制中国北京跑步地图的方法
Mar 03 Python
python logging 日志轮转文件不删除问题的解决方法
Aug 02 Python
解决Django数据库makemigrations有变化但是migrate时未变动问题
May 30 Python
pandas修改DataFrame列名的实现方法
Feb 22 Python
Python:Numpy 求平均向量的实例
Jun 29 Python
TensorFlow车牌识别完整版代码(含车牌数据集)
Aug 05 Python
关于阿里云oss获取sts凭证 app直传 python的实例
Aug 20 Python
python dataframe NaN处理方式
Dec 26 Python
python使用paramiko实现ssh的功能详解
Mar 06 Python
python实现自定义日志的具体方法
May 28 Python
python直接访问私有属性的简单方法
Jul 25 #Python
python类:class创建、数据方法属性及访问控制详解
Jul 25 #Python
python实现汉诺塔方法汇总
Jul 25 #Python
python魔法方法-属性访问控制详解
Jul 25 #Python
python魔法方法-属性转换和类的表示详解
Jul 22 #Python
wxpython中自定义事件的实现与使用方法分析
Jul 21 #Python
wxpython中Textctrl回车事件无效的解决方法
Jul 21 #Python
You might like
php smarty截取中文字符乱码问题?gb2312/utf-8
2011/11/07 PHP
thinkphp3.2.2前后台公用类架构问题分析
2014/11/25 PHP
PHP接收App端发送文件流的方法
2016/09/23 PHP
laravel 5.3中自定义加密服务的方案详解
2017/05/09 PHP
PHP使用pdo连接access数据库并循环显示数据操作示例
2018/06/05 PHP
laravel 解决路由除了根目录其他都404的问题
2019/10/18 PHP
jQuery中noconflict函数的实现原理分解
2015/02/03 Javascript
jQuery选择器源码解读(三):tokenize方法
2015/03/31 Javascript
jquery带下拉菜单和焦点图代码分享
2015/08/24 Javascript
JS实现弹性菜单效果代码
2015/09/07 Javascript
JS实现的最简Table选项卡效果
2015/10/14 Javascript
JS实现选项卡实例详解
2015/11/17 Javascript
jQuery实现简单的DIV拖动效果
2016/02/19 Javascript
Java框架SSH结合Easyui控件实现省市县三级联动示例解析
2016/06/12 Javascript
详解Vue中一种简易路由传参办法
2017/09/15 Javascript
Vue使用zTree插件封装树组件操作示例
2019/04/25 Javascript
node.js如何根据URL返回指定的图片详解
2020/10/21 Javascript
微信小程序实现电影App导航和轮播
2020/11/30 Javascript
python3.5仿微软计算器程序
2020/03/30 Python
python实现逻辑回归的方法示例
2017/05/02 Python
Flask框架URL管理操作示例【基于@app.route】
2018/07/23 Python
一百行python代码将图片转成字符画
2021/02/19 Python
Python3 SSH远程连接服务器的方法示例
2018/12/29 Python
如何使用Python实现自动化水军评论
2019/06/26 Python
使用Python的Turtle库绘制森林的实例
2019/12/18 Python
pycharm 快速解决python代码冲突的问题
2021/01/15 Python
CSS3 :nth-child()伪类选择器实现奇偶行显示不同样式
2013/11/05 HTML / CSS
精选鞋类、服装和配饰的全球领先目的地:Bodega
2021/02/27 全球购物
用友笔试题目
2016/10/25 面试题
设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
2014/12/30 面试题
发展部经理职责规定
2014/02/22 职场文书
幼儿园父亲节活动方案
2014/03/11 职场文书
陈安之励志演讲稿
2014/08/21 职场文书
给校长的建议书作文400字
2015/09/14 职场文书
巧用 -webkit-box-reflect 倒影实现各类动效(小结)
2021/04/22 HTML / CSS
Java Optional<Foo>转换成List<Bar>的实例方法
2021/06/20 Java/Android