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 12 Python
python实现给字典添加条目的方法
Sep 25 Python
Python 将pdf转成图片的方法
Apr 23 Python
Django Admin实现三级联动的示例代码(省市区)
Jun 22 Python
对Python捕获控制台输出流的方法详解
Jan 07 Python
OpenCV3.0+Python3.6实现特定颜色的物体追踪
Jul 23 Python
详解在python操作数据库中游标的使用方法
Nov 12 Python
TensorFlow 显存使用机制详解
Feb 03 Python
解决python 虚拟环境删除包无法加载的问题
Jul 13 Python
解决Django响应JsonResponse返回json格式数据报错问题
Aug 09 Python
Python爬虫设置Cookie解决网站拦截并爬取蚂蚁短租的问题
Feb 22 Python
Python requests用法和django后台处理详解
Mar 19 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
WordPress中编写自定义存储字段的相关PHP函数解析
2015/12/25 PHP
CI框架支持$_GET的两种实现方法
2016/05/18 PHP
js 截取或者替换字符串中的数字实现方法
2016/06/13 Javascript
AngularJS基础 ng-show 指令简单示例
2016/08/03 Javascript
Angularjs CURD 详解及实例代码
2016/09/14 Javascript
jQuery电话号码验证实例
2017/01/05 Javascript
jQuery实现的浮动层div浏览器居中显示效果
2017/02/03 Javascript
详解Angular2 之 结构型指令
2017/06/21 Javascript
用vue的双向绑定简单实现一个todo-list的示例代码
2017/08/03 Javascript
Angular2 http jsonp的实例详解
2017/08/31 Javascript
解决在vue项目中,发版之后,背景图片报错,路径不对的问题
2018/03/06 Javascript
vue webpack开发访问后台接口全局配置的方法
2018/09/18 Javascript
Echart折线图手柄触发事件示例详解
2018/12/16 Javascript
JS实现点击li标签弹出对应的索引功能【案例】
2019/02/18 Javascript
微信小程序调用微信支付接口的实现方法
2019/04/29 Javascript
layui文件上传控件带更改后数据传值的方法
2019/09/23 Javascript
JS中类的静态方法,静态变量,实例方法,实例变量区别与用法实例分析
2020/03/14 Javascript
Python读写Redis数据库操作示例
2014/03/18 Python
python json.dumps() json.dump()的区别详解
2020/07/14 Python
python实现简单文件读写函数
2021/02/25 Python
Ann Taylor官方网站:美国最大的女性产品制造商之一
2016/09/14 全球购物
Boda Skins皮衣官网:奢侈皮夹克,全球配送
2016/12/15 全球购物
里程积分管理买卖交换平台:Points.com
2017/01/13 全球购物
印度在线内衣和时尚目的地:Zivame
2017/09/28 全球购物
微软马来西亚官方网站:Microsoft马来西亚
2019/11/22 全球购物
计算机本科生自荐信
2013/10/15 职场文书
大学生求职自荐信
2013/12/12 职场文书
运动会广播稿100字
2014/01/11 职场文书
2014年收银工作总结
2014/11/13 职场文书
小班上学期个人总结
2015/02/12 职场文书
军训个人总结
2015/03/03 职场文书
2015年幼儿园保育员工作总结
2015/04/23 职场文书
大学生就业指导课心得体会
2016/01/15 职场文书
5分钟教你docker安装启动redis全教程(全新方式)
2021/05/29 Redis
排查MySQL生产环境索引没有效果
2022/04/11 MySQL
nginx配置指令之server_name的具体使用
2022/08/14 Servers