管理属性的几种方式

在python中访问、设置、删除对象属性的时候,有以下几种方式:

  1. 使用内置函数getattr()、setattr()和delattr()
  2. 自己编写getter()setter()deleter()方法
  3. 重载__getattr__()__setattr__()__delattr__()运算符,这决定了x.y的访问、赋值方式以及del x.y的方式
  4. 使用__getattribute__()方法
  5. 使用描述符协议
  6. 使用property协议,它是一种特殊的描述符

本文简单介绍其中的前4种作为基础,后面使用单独的文章解释后2种。

内置函数XXXattr()管理属性

通过内置函数getattr()、setattr()、delattr()能简单访问、设置、删除对象上的属性。

先看看它们的帮助文档:

getattr(...)
getattr(object, name[, default]) -> value
Get a named attribute from an object;
getattr(x, 'y') is equivalent to x.y. setattr(obj, name, value, /)
Sets the named attribute on the given object to the specified value.
setattr(x, 'y', v) is equivalent to ``x.y = v'' delattr(obj, name, /)
Deletes the named attribute from the given object.
delattr(x, 'y') is equivalent to ``del x.y''

用法很简单,给定要操作的对象obj以及要操作的属性名称name。对于getattr()来说,如果要操作的属性不存在默认会报错,可以给定一个default参数表示属性不存在时返回该给定属性值。

例如,下面是一个简单的Person类和对象p:

class Person():
def __init__(self, name):
self.name = name p = Person("malongshuai")

使用getattr()获取name属性和不存在的age属性:

print(getattr(p, "name"))
print(getattr(p, "age", 23))

上面访问age属性时,如果把第三个参数"23"去掉,将抛出异常。

AttributeError: 'Person' object has no attribute 'age'

使用setattr()和delattr()设置和删除属性:

setattr(p, "age", 25)
print(p.__dict__) delattr(p, "age")
print(p.__dict__)

返回结果:

{'name': 'malongshuai', 'age': 25}
{'name': 'malongshuai'}

自己编写accessor方法

一般面向对象的语言都是自己写setter、getter、deleter方法来管理属性的,通用又安全,但是管理起来并不那么方便。

这里仅介绍一下,它们更好的写法参考:python设置对象属性

例如,在Person类中加上name、age这两个属性的accessor方法:

class Person():
def __init__(self, name):
self.name = name def set_name(self,name): self.name = name
def get_name(self): return self.name
def del_name(self): del self.name def set_age(self,age): self.age = age
def get_age(self): return self.age
def del_age(self): del self.age

缺点是很明显的,对于想要管理的每个属性,都得去定义这些属性。也就是说,accessor方法是针对单个属性的。

运算符重载管理属性

通常可以直接使用点号运算来访问、设置属性。例如:

p.name          # (1)访问p对象的name属性
p.name = "abc" # (2)为p对象的name属性赋值
del p.name # (3)删除p对象的name属性

先说对象的赋值和删除操作,也就是上面的(2)和(3)。这两种操作可以直接被__setattr__()__delattr__()这两个方法拦截,或者说只要重写了这两个方法,每当对属性赋值、删除时,都会调用对应的这两个方法。

再说访问属性的操作(1),python提供了两个对应的方法__getattr__()__getattribute__()。前者是在访问不存在的属性时被自动调用的,后者则是访问属性时被调用的,它无视属性是否存在

这里提前说一个稍后要遇到的问题总结:对于适用于所有属性操作的__setattr____delattr____getattribute__方法,要避免它们的无限递归。参考后面的示例即可知。

__getattr__()

__getattr__()是通过点号访问不存在属性时被调用的。它有两个使用标准:要么返回属性值,要么抛出异常。

例如:

class Person():
def __init__(self, name):
self.name = name def __getattr__(self, attrname):
if attrname == "name":
print("in getattr1")
return self.name
elif attrname == "age":
print("in getattr2")
return 25
else:
print("in getattr3")
raise AttributeError(attrname) p = Person("malongshuai")

上面的Person类带有属性name,所以访问name属性的时候不会调用__getattr__(),而访问age或其它属性时会调用该方法,只不过age属性有自定义的返回值,其它属性则报错。

print(p.name)
print(p.age)
print(p.job)

以下是输出结果:

malongshuai
in getattr2
25
in getattr3
Traceback (most recent call last):
File "g:/pycode/b.py", line 21, in <module>
print(p.job)
File "g:/pycode/b.py", line 14, in __getattr__
raise AttributeError(attrname)
AttributeError: job

__getattribute__()

__getattribute__()__getattr__()类似,不同的是它前者适用于所有属性的访问,而不管目标属性是否存在。

需要注意的是,__getattribute__()适用于所有属性访问操作,所以要避免无限递归。例如,下面是错误的写法:

def __getattribute__(self, attr):
return self.attr

因为这个方法中的self.attr会继续触发__getattribute__的调用,从而出现无限递归问题。

解决办法是通过父类来访问,比如super()或object类。

super().__getattribute__(attr)
object.__getattribute__(self, attr)

__getattribute__()的优先级高于__getattr__(),前者存在的时候不会调用到后者,除非前者的代码中调用了后者,或者前者抛出了异常。

例如:

class Person():
def __init__(self, name):
self.name = name def __getattribute__(self, attr):
print("in getattribute")
return object.__getattribute__(self, attr)
# return super.__getattribute__(attr) def __getattr__(self, attrname):
if attrname == "name":
print("in getattr1")
return self.name
elif attrname == "age":
print("in getattr2")
return 25
else:
print("in getattr3")
raise AttributeError(attrname) p = Person("malongshuai") print(p.name)
print(p.age)

返回结果:

in getattribute
malongshuai
in getattribute
in getattr2
25

上面输出了name和age两个属性,但是输出"p.age"的时候该属性不存在,于是__getattribute__抛出异常,然后触发__getattr__

需要注意的是,在解决无限递归问题上,后面的__setattr____delattr__还会有一种访问__dict__的方式,这不适合于这里的__getattribute__,因为访问这个字典也会触发__getattribute__从而继续导致无限递归。

__setattr__()

__setattr__()用来拦截对象属性赋值操作。例如:

p.name = "long"

会转换为调用p.__setattr__(self,name,"long")

唯一需要注意的是避免赋值时的无限递归问题。因为在__setattr__()中的赋值语句self.attr = value会继续调用该方法,最终导致无限递归。

所以在__setattr__()方法中,必须使用__dict__来获取属性并进行赋值,或者访问父类同名属性。所以,有下面几种方式避免无限递归调用。

self.__dict__[attr] = value
super().__setattr__(attr, value)
object.__setattr__(self, attr, value)

参考下面的示例。

class Person():
def __init__(self, name):
self.name = name def __setattr__(self, attr, value):
print("in setattr")
#self.__dict__[attr] = value
#super().__setattr__(attr, value)
object.__setattr__(self, attr, value) p = Person("malongshuai") p.age = 33 # 自动调用__setattr__()
print(p.age)

执行结果:

in setattr
in setattr
33

可能已经发现问题所在了,上面输出了两次in setter,原因是__init__()中的赋值操作也会触发__setattr__()

__delattr__()

当调用del x.y的时候会自动触发__delattr__()的调用。

同样需要注意的是避免赋值时的无限递归问题。因为在__delattr__()中的del语句可能会继续调用该方法,最终导致无限递归。所以在__delattr__()方法中,必须使用__dict__来获取属性并进行赋值,或者访问父类同名属性。所以,有下面几种方式避免无限递归调用。

del self.__dict__[attr]
super().__delattr__(attr)
object.__delattr__(self, attr)

例如:

class Person():
def __init__(self, name):
self.name = name def __delattr__(self, attr):
print("%s deleting" % (attr)) #del self.__dict__[attr]
#super().__delattr__(attr)
object.__delattr__(self, attr) print("%s deleted" % (attr)) p = Person("malongshuai")
p.age = 33
del p.age

python属性管理(1):基础的更多相关文章

  1. Python中的属性管理

    Python管 理属性的方法一般有三种:操作符重载(即,__getattr__.__setattr__.__delattr__和 __getattribute__,有点类似于C++中的重载操作符).p ...

  2. python对象属性管理(2):property管理属性

    使用Property管理属性 python提供了一种友好的getter.setter.deleter类方法的属性管理工具:property. property()是一个内置函数,它返回一个Proper ...

  3. [读书]Python学习手冊--属性管理1

    属性管理-特性 一般开发这不必关心属性的实现.对工具的构建这来说,了解这一块对API的灵活性有帮助. 大多数情况下,属性位于对象自身之中.或者继承自对象所派生自的一个类. ----python学习手冊 ...

  4. Python环境安装与基础语法(4)——内存管理、if分支

    Python内存管理 python中有自动清理内存垃圾的功能,当变量的引用计数为0,则可以被有计划的垃圾回收GC 常量会在系统中被多次引用,所以常量的引用计数无法确定 程序控制 顺序:按照先后顺序逐条 ...

  5. python 小白(无编程基础,无计算机基础)的开发之路 day1

    本节内容 Python介绍 发展史 Python 2 or 3? 安装 Hello World程序 变量 用户输入 模块初识 .pyc是个什么鬼? 数据类型初识 数据运算 表达式if ...else语 ...

  6. Python属性描述符(一)

    描述符是对多个属性运用相同存取逻辑的一种方式,,是实现了特性协议的类,这个协议包括了__get__.__set__和__delete__方法.property类实现了完整的描述符协议.通常,可以只实现 ...

  7. Python进阶----数据库的基础,关系型数据库与非关系型数据库(No SQL:not only sql),mysql数据库语言基础(增删改查,权限设定)

    day37 一丶Python进阶----数据库的基础,mysql数据库语言基础(增删改查,权限设定) 什么是数据库:    简称:DataBase ---->DB    数据库即存放数据的仓库, ...

  8. Python学习笔记之基础篇(-)python介绍与安装

    Python学习笔记之基础篇(-)初识python Python的理念:崇尚优美.清晰.简单,是一个优秀并广泛使用的语言. python的历史: 1989年,为了打发圣诞节假期,作者Guido开始写P ...

  9. Python 包管理工具解惑

    Python 包管理工具解惑 本文链接:http://zengrong.net/post/2169.htm python packaging 一.困惑 作为一个 Python 初学者,我在包管理上感到 ...

随机推荐

  1. Sqlalchemy python经典第三方orm

    Ⅰ. 安装 pip install sqlalchemy Ⅱ. 起步链接 import time import threading import sqlalchemy from sqlalchemy ...

  2. 大数据解实例决topn问题

    做大数据开发经常遇上在众多数据中统计前几的问题,比如王者荣耀每个区的富豪排行榜(腾讯可以做个刺激消费,

  3. 微信小程序-自定义下拉刷新

    最近给别个公司做技术支持,要实现微信小程序上拉刷新与下拉加载更多 微信给出的接口不怎么友好,最终想实现效果类似QQ手机版 ,一共3种下拉刷新状态变化,文字+图片+背景颜色 最终实现后的效果(这里提示有 ...

  4. web安全学习规划指南

    课程信息 难易程度:中级 学习人数:5078人 已完结:共1节 时长:14分钟 课程介绍 黑客进攻真的会像电影里一样敲几行命令屏幕闪动过后就可以控制目标服务器的么?其实大部分时间黑客是在寻找网站应用的 ...

  5. myEclipse配置jdk1.7

    第一步:下载jdk1.7 下载地址:http://download.csdn.net/download/chun201010/7824469 第二步:安装jdk1.7 将下载的压缩包进行解压,得到一个 ...

  6. 微信小程序实现显示和隐藏控件-头像-取值-bindblur事件

    微信小程序实现显示和隐藏控件 .wxml: <view class=" {{showOrHidden?'show':'hidden'}}"></view> ...

  7. php过滤&nbsp;字符

    今天在抓取页面中得到字符串:"卡牌 ",使用str_replace . preg_replace 和 strip_tags过滤都无解. 最后google到2种方式,如下: str_ ...

  8. 传参导出Excel表乱码问题解决方法

    业务场景 先描述一下业务场景,要实现的功能是通过搜索框填写参数,然后点击按钮搜索数据,将搜索框的查询参数获取,附加在链接后面,调导Excel表接口,然后实现导出Excel功能.其实做导Excel表功能 ...

  9. Python中的注释和解注释

    注释 目标 注释的作用 单行注释(行注释) 多行注释(块注释) 01. 注释的作用 使用用自己熟悉的语言,在程序中对某些代码进行标注说明,增强程序的可读性 02. 单行注释(行注释) 以 # 开头,# ...

  10. 性能瓶颈之System

    如果Source,Target,Mapping和Session都不存在性能上的瓶颈,则问题可能会出在System 因为Integration Service运行时,它使用了System的资源去运行组件 ...