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类的基础入门知识
Nov 24 Python
Python三元运算实现方法
Jan 12 Python
PyQt5实现简单数据标注工具
Mar 18 Python
Python将文字转成语音并读出来的实例详解
Jul 15 Python
windows上安装python3教程以及环境变量配置详解
Jul 18 Python
Python操作Sonqube API获取检测结果并打印过程解析
Nov 27 Python
python实现飞行棋游戏
Feb 05 Python
不到20行实现Python代码即可制作精美证件照
Apr 24 Python
Django 用户登陆访问限制实例 @login_required
May 13 Python
哪些是python中web开发框架
Jun 17 Python
python中rc1什么意思
Jun 19 Python
Pytorch 如何加速Dataloader提升数据读取速度
May 28 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
Banner程序
2006/10/09 PHP
php输入数据统一类实例
2015/02/23 PHP
标准PHP的AES加密算法类
2015/03/12 PHP
js jquery做的图片连续滚动代码
2008/01/06 Javascript
Firefox+FireBug使JQuery的学习更加轻松愉快
2010/01/01 Javascript
Jquery AutoComplete自动完成 的使用方法实例
2010/03/19 Javascript
弹出层之1:JQuery.Boxy (一) 使用介绍
2011/10/06 Javascript
seajs1.3.0源码解析之module依赖有序加载
2012/11/07 Javascript
jquery插件开发之实现md5插件
2014/03/17 Javascript
浅谈轻量级js模板引擎simplite
2015/02/13 Javascript
javascript实现自动输出文本(打字特效)
2015/08/27 Javascript
基于jquery实现全屏滚动效果
2015/11/26 Javascript
Vue.js基础知识汇总
2016/04/27 Javascript
JS实现添加,替换,删除节点元素的方法
2016/06/30 Javascript
微信小程序开发一键登录 获取session_key和openid实例
2016/11/23 Javascript
jQuery实现简单漂亮的Nav导航菜单效果
2017/03/29 jQuery
jQuery Pagination分页插件_动力节点Java学院整理
2017/07/17 jQuery
原生JS写Ajax的请求函数功能
2017/12/22 Javascript
微信小程序画布圆形进度条显示效果
2020/11/17 Javascript
vue 父组件给子组件传值子组件给父组件传值的实例代码
2019/04/15 Javascript
Vue3.0中的monorepo管理模式的实现
2019/10/14 Javascript
Javascript执行上下文顺序的深入讲解
2020/11/04 Javascript
在Python的Flask框架下收发电子邮件的教程
2015/04/21 Python
详解pyqt5 动画在QThread线程中无法运行问题
2018/05/05 Python
Python基本数据结构与用法详解【列表、元组、集合、字典】
2019/03/23 Python
python破解同事的压缩包密码
2020/10/14 Python
全球工业:Global Industrial
2020/02/01 全球购物
品恩科技软件测试面试题
2014/10/26 面试题
厨师岗位职责
2013/11/12 职场文书
成品仓管员岗位职责
2013/12/11 职场文书
2014个人四风对照检查材料思想汇报
2014/09/18 职场文书
幼儿园小班见习报告
2014/10/31 职场文书
2015年预备党员自我评价
2015/03/04 职场文书
离婚起诉书范文2015
2015/05/19 职场文书
PHP控制循环操作的时间
2021/04/01 PHP
CSS 实现Chrome标签栏的技巧
2021/08/04 HTML / CSS