python魔法方法-属性访问控制详解


Posted in Python onJuly 25, 2016

属性访问控制

所谓的属性访问控制就是控制点号访问属性的行为,而且不仅是类的外部,连类的内部也受控制,代码见真章,边看代码边解释:

•__getattr__(self, item)

定义当访问不存在的属性时的行为,注意是不存在的属性。

class Foo(object):
  def __init__(self, value):
    self.value = value

  def __getattr__(self, item):
    print item # 查看得到的参数是什么
    print type(item)  # 参数的类型是什么
    return 'attr:%s' % item # 最后返回一个东西看其行为如何

a = Foo('scolia')  # 创建一个实例

测试:

print a.value
print type(a.value)

python魔法方法-属性访问控制详解

其行为和没定义前正常,下面看看访问一个不存在的属性时会发生什么:

print a.abc

python魔法方法-属性访问控制详解

按照平常的情况,访问不存在的属性时肯定会抛出异常,但是这里输出了三行,前两个是方法输出的,最后一行是外部的print语句输出的,输出的是方法的return值。方法得到的是我们访问的属性名,而且是以字符串的形式。

知道了以上信息后,我们就可以定制更多:

class Foo(object):
  def __init__(self, value):
    self.value = value

  def __getattr__(self, item):
    if item == 'scolia':
      return 'can not set attr: %s' % item # 访问不存在的scolia属性时,打印一句话而不报错
    else:
      raise AttributeError('not attr name: %s' % item)  # 访问其他不存在的属性时,触发异常。

测试:

a = Foo(123)
print a.value # 访问存在的属性

python魔法方法-属性访问控制详解

print a.scolia # 访问不存在的属性,但我们做了特殊处理的

python魔法方法-属性访问控制详解

没有触发异常,和我们设想的一样。

print a.good # 访问不存在的属性,但应该触发异常的

python魔法方法-属性访问控制详解

触发了我们想要的异常。

这里要再强调一遍,必须是访问不存在的属性时,才会调用这个方法,例如:

a.scolia = 321
print a.scolia

python魔法方法-属性访问控制详解

因为这个属性已经存在了(我们手动添加了),所以访问它的时候并没有调用这个方法,而在方法里所做的任何处理,也不会有效。

更高级的技巧:

class Foo(object):
  def __init__(self, value, defulat=None):
    self.value = value
    self.__defulat = defulat

  def __getattr__(self, item):
    item = item.lower() # 用字符串的方法对其进行小写
    if item in self.__dict__:
      return self.__dict__[item] # 返回相应的属性
    else:
      self.__dict__[item] = self.__defulat  # 若属性不存在则添加这个属性并使用默认值
      return self.__dict__[item]

a = Foo(123)
a.scolia = 321
print a.SCOlia
print a.good

python魔法方法-属性访问控制详解

我们实现了属性的不区分大小写访问和自动添加不存在的属性。

这里的秘诀在于活用 __dict__ 这个属性,我在类的属性中已经讨论过这个属性。这个属性由python自动创建,是一个字典,包含对象的所有属性,字典里的键就是属性名,对应的值就是属性值。所以这里在这个字典中添加了键和值,就相当于为对象添加了属性和属性值。

•__setattr__(self, key, value)

定义了设置属性时的行为,包括在 __init__ 初始化函数中的设置行为:

class Foo(object):
  def __init__(self, value, defulat=None):
    self.value = value

  def __setattr__(self, key, value):
    print key, type(key)
    print value, type(value)

a = Foo('scolia')
b = Foo(123)

python魔法方法-属性访问控制详解

这里可以看到初始化函数中的属性添加的行为也受到了控制,其中 key 得到的是属性名,以字符串的形式;而 value 得到的是属性值,属性值根据输入的不同而不同。

在这里,我们仅仅只是打印了几句话,而没有进行属性的添加,所以当我们试图访问相应的属性时,会发现根本就没有:

print a.value

python魔法方法-属性访问控制详解

触发了异常,表示没有相应的属性。

知道了这些之后我们可以做很多事情,例如将所有的属性名变成小写或大写,控制某些属性名不能添加之类的,就不再举例。不过,这里你总不可能用 self.key = value 来添加属性吧,因为 key 始终是一个字符串。这个时候就要使用 __dict__ 属性了,向这个字典中添加相应的键值对就可以了,具体就不再演示了。

•__delattr__(self, item)

定义了删除一个属性时的行为,item 得到的也是一个字符串形式的属性名,具体细节也无序多说,只要 del 掉 __dict__ 字典中对应是键和值就行了。另外,删除不存在的属性时调用的也是这个方法。

•__getattribute__(self, name)

这个方法定义了所有属性访问的行为,注意是所有,而不是 __getattr__ 中的不存在。当实现了这个方法之后,将会覆盖 __getattr__ 方法,毕竟所有涵盖了不存在。

这个方法只在新式类中有效。

但是,你也可以显式的调用 __getattr__,例如 a.__getattr__ 来使用这个被掩盖的方法,或者是触发 AttributeError 异常时也会自动调用它。

然而,非常不建议使用这个方法,因为可能会很多不可预知的异常情况,最常见的就是无尽的递归调用,例如:

class Foo(object):
  def __init__(self, value):
    self.value = value

  def __getattribute__(self, item):
    return self.__dict__[item]

a = Foo('scolia')
print a.value

这段代码看起来很正常,但是这里有一个陷阱,因为类中的所有的属性访问都是受这几个魔法方法控制的,包括上面介绍的几个魔法方法。它们似乎比普通的魔法方法拥有更高的权限一般。

但这就导致了一个问题,例如这里的 self.__dict__[item] ,这句话也受属性访问的控制,尽管这个属性是 python 为我们创建的。

也就是说获取 self.__dict__ 时,会再次调用 __getattribute__ 方法,然后方法内又调用了 self.__dict__ 。这样无限循环下去,最终会抛出一个异常。

python魔法方法-属性访问控制详解

异常信息非常长,这里我是拉到最后才截的图。

其实不仅这个魔法方法会导致这样异常,上面讨论的几种魔法方法可能都会出现这个问题,只不过这个魔法方法的权限更大,所以异常出现的可能性更高一些。

这也就是不推荐这个魔法方法的原因,而使用其他的属性控制方法的时候也要小心。

而到目前为止,我们所学到的属性访问的方法只有两种,一是直接用点号访问,还有就是先通过点号访问__dict__ 属性,然后在这个字典中获取相应的键值对。而这两种方法都受到了 __getattribute__ 的控制,调用它们就相当于没有终点的自调用(有终点的自调用有时能提升效率),那么这个方法到底要怎么用呢?

技巧就是调用父类的这个方法:

class Foo(object):
  def __init__(self, value):
    self.value = value

  def __getattribute__(self, item):
    return object.__getattribute__(self, item) # 非绑定方法要显式传递self

a = Foo('scolia')
print a.value

python魔法方法-属性访问控制详解

这里调用的是object的这个方法,如果是涉及到继承的话:

class Boo(Foo):
  def __init__(self, value):
    self.value = value

  def __getattribute__(self, item):
    return Foo.__getattribute__(self, item)
    # return super(Boo, self).__getattribute__(item)  也可以使用super函数让python自动在其父类们寻找这个方法。
a = Foo('scolia')
print a.value
b = Boo(123)
print b.value

python魔法方法-属性访问控制详解

访问正常。

其实最后调用了还是 object 或其他内置类型的方法。

而我们姑且不起探究object到底是怎么实现的,因为这可能是用 C 所写的,只要会用就可以,虽然这个方法用的也不多。

最后附上一个完整的例子:

class Foo(object):
  def __init__(self, value):
    self.value = value

  def __getattr__(self, item):
    if item == 'scolia':
      return 'no attr:%s' % item
    elif item in self.__dict__:
      return self.__dict__[item]
    else:
      raise AttributeError('no attr:%s' % item)

  def __setattr__(self, key, value):
    if key == 'good':
      print 'can not set the attr: good'
    else:
      self.__dict__[key] = value

  def __delattr__(self, item):
    if item == 'a':
      print 'no attr: good'
    else:
      del self.__dict__[item]

  def __getattribute__(self, item):
    if item == 'a':
      raise AttributeError('not a')
    return object.__getattribute__(self, item)


a = Foo('scolia')
print a.value  # 正常访问
a.a = 123  # __getattribute__会触发AttributeError异常,此时调用__getattr__
      # 而__getattr__添加了这个属性,所以最后异常没有触发,属性也添加了
print a.a  # 结果能够访问
del a.a # 试图删除这个属性
print a.a # 删除行为被阻止了,所以该属性还在
a.good = 'good' # 因为添加被阻止了
print a.good # 所以访问失败了

结果:

python魔法方法-属性访问控制详解

以上这篇python魔法方法-属性访问控制详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python基础教程之简单入门说明(变量和控制语言使用方法)
Mar 25 Python
Django集成百度富文本编辑器uEditor攻略
Jul 04 Python
python字典键值对的添加和遍历方法
Sep 11 Python
简单实现python画圆功能
Jan 25 Python
浅谈Tensorflow由于版本问题出现的几种错误及解决方法
Jun 13 Python
Python Selenium截图功能实现代码
Apr 26 Python
python如何保存文本文件
Jun 07 Python
Python生成器generator原理及用法解析
Jul 20 Python
浅析Python 序列化与反序列化
Aug 05 Python
Python实现自动签到脚本功能
Aug 20 Python
python 多线程死锁问题的解决方案
Aug 25 Python
记一次python 爬虫爬取深圳租房信息的过程及遇到的问题
Nov 24 Python
python魔法方法-属性转换和类的表示详解
Jul 22 #Python
wxpython中自定义事件的实现与使用方法分析
Jul 21 #Python
wxpython中Textctrl回车事件无效的解决方法
Jul 21 #Python
Python实现Sqlite将字段当做索引进行查询的方法
Jul 21 #Python
python装饰器初探(推荐)
Jul 21 #Python
python魔法方法-自定义序列详解
Jul 21 #Python
浅谈Python 字符串格式化输出(format/printf)
Jul 21 #Python
You might like
php中批量修改文件后缀名的函数代码
2011/10/23 PHP
php Ubb代码编辑器函数代码
2012/07/05 PHP
php对包含html标签的字符串进行截取的函数分享
2014/06/19 PHP
php根据日期显示所在星座的方法
2015/07/13 PHP
PHP计算近1年的所有月份
2017/03/13 PHP
基于Laravel5.4实现多字段登录功能方法示例
2017/08/11 PHP
Javascript Ajax异步读取RSS文档具体实现
2013/12/12 Javascript
JavaScript中的cacheStorage使用详解
2015/07/29 Javascript
smartcrop.js智能图片裁剪库
2015/10/14 Javascript
Node.js模块封装及使用方法
2016/03/06 Javascript
jquery轮播的实现方式 附完整实例
2016/07/28 Javascript
KVM虚拟化技术之使用Qemu-kvm创建和管理虚拟机的方法
2016/10/05 Javascript
微信小程序-图片、录音、音频播放、音乐播放、视频、文件代码实例
2016/11/22 Javascript
Vue.js如何优雅的进行form validation
2017/04/07 Javascript
Vue组件通信的几种实现方法
2019/04/25 Javascript
vue前端框架—Mint UI详解(更适用于移动端)
2019/04/30 Javascript
nodejs简单抓包工具使用详解
2019/08/23 NodeJs
如何在vue-cli中使用css-loader实现css module
2021/01/07 Vue.js
[32:26]EG vs IG 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
[00:52]玛尔斯技能全介绍
2019/03/06 DOTA
[34:10]Secret vs VG 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.24
2019/09/10 DOTA
使用Pyrex来扩展和加速Python程序的教程
2015/04/13 Python
Python使用reportlab将目录下所有的文本文件打印成pdf的方法
2015/05/20 Python
python实现字符串连接的三种方法及其效率、适用场景详解
2017/01/13 Python
Python发送http请求解析返回json的实例
2018/03/26 Python
python DataFrame 取差集实例
2019/01/30 Python
Matplotlib.pyplot 三维绘图的实现示例
2020/07/28 Python
利用Python pandas对Excel进行合并的方法示例
2020/11/04 Python
AC Lens:购买隐形眼镜
2017/02/26 全球购物
澳大利亚最大的女装零售商:Millers
2017/09/10 全球购物
美国第一个网上卖鞋零售商:OnlineShoes.com
2017/09/24 全球购物
英语专业毕业生自我鉴定
2013/11/09 职场文书
初中三好学生自我鉴定
2014/04/07 职场文书
工程服务质量承诺书
2015/04/29 职场文书
寻衅滋事罪辩护词
2015/05/21 职场文书
2016年师德学习心得体会
2016/01/12 职场文书