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 获取et和excel的版本号
Apr 09 Python
Python之eval()函数危险性浅析
Jul 03 Python
Python使用scrapy采集数据过程中放回下载过大页面的方法
Apr 08 Python
使用Python进行二进制文件读写的简单方法(推荐)
Sep 12 Python
python selenium UI自动化解决验证码的4种方法
Jan 05 Python
tensorflow实现简单的卷积网络
May 24 Python
python画一个玫瑰和一个爱心
Aug 18 Python
python实现三次样条插值
Dec 17 Python
Pytorch基本变量类型FloatTensor与Variable用法
Jan 08 Python
在Keras中利用np.random.shuffle()打乱数据集实例
Jun 15 Python
flask开启多线程的具体方法
Aug 02 Python
详解python的异常捕获
Mar 03 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
Yii操作数据库的3种方法
2014/03/11 PHP
php类声明和php类使用方法示例分享
2014/03/29 PHP
(转载)JavaScript中匿名函数,函数直接量和闭包
2007/05/08 Javascript
本地对象Array的原型扩展实现代码
2010/12/04 Javascript
简单选项卡 js和jquery制作方法分享
2014/02/26 Javascript
使用jquery.upload.js实现异步上传示例代码
2014/07/29 Javascript
jQuery事件绑定和委托实例
2014/11/25 Javascript
jQuery使用removeClass方法删除元素指定Class的方法
2015/03/26 Javascript
由浅入深剖析Angular表单验证
2016/07/14 Javascript
js实现的光标位置工具函数示例
2016/10/03 Javascript
JQuery 获取Dom元素的实例讲解
2017/07/08 jQuery
js的函数的按值传递参数(实例讲解)
2017/11/16 Javascript
Angular5.1新功能分享
2017/12/21 Javascript
JavaScript创建对象方式总结【工厂模式、构造函数模式、原型模式等】
2018/12/19 Javascript
Vue指令之 v-cloak、v-text、v-html实例详解
2019/08/08 Javascript
JS中类的静态方法,静态变量,实例方法,实例变量区别与用法实例分析
2020/03/14 Javascript
vue基础知识--axios合并请求和slot
2020/06/04 Javascript
[03:36]DOTA2完美大师赛coL战队趣味视频——我演你猜
2017/11/23 DOTA
[57:53]Secret vs Pain 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
如何处理Python3.4 使用pymssql 乱码问题
2016/01/08 Python
socket + select 完成伪并发操作的实例
2017/08/15 Python
一些Centos Python 生产环境的部署命令(推荐)
2018/05/07 Python
一看就懂得Python的math模块
2018/10/21 Python
Django中的FBV和CBV用法详解
2019/09/15 Python
python/golang 删除链表中的元素
2020/09/14 Python
CSS3 :not()选择器实现最后一行li去除某种css样式
2016/10/19 HTML / CSS
canvas简易绘图的实现(海绵宝宝篇)
2018/07/04 HTML / CSS
美国领先的在线邮轮旅游公司:CruiseDirect
2018/06/07 全球购物
阿姆斯特丹杜莎夫人蜡像馆官方网站:Madame Tussauds Amsterdam
2019/03/12 全球购物
中学生操行评语
2014/04/24 职场文书
茶楼服务员岗位职责
2015/02/09 职场文书
消防宣传语大全
2015/07/13 职场文书
图书馆义工感想
2015/08/07 职场文书
基于Golang 高并发问题的解决方案
2021/05/08 Golang
MySQL系列之一 MariaDB-server安装
2021/07/02 MySQL
python实现双向链表原理
2022/05/25 Python