Python获取对象属性的⼏种⽅式⼩结本⽂将简单介绍四种获取对象的⽅法。
假如有以下的类:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return 'name=%s, age=%s' % (self.name, self.age)
⽅法⼀:使⽤属性运算符
print(xmr.name)
⽅法⼆:通过属性字典__dict__
print(xmr.__dict__['name'])
⽅法三:通过getattr函数
print(getattr(xmr, 'name'))
⽅法四:operator.attrgetter
import operator
op = operator.attrgetter('name')
print(op(xmr))
⽅法四可以⽤于对象的排序,⽐如需要根据年龄age来排序Person对象:
import operator
p_list = [Person('xiemanR', 18), Person('zhangshan', 17), Person('lisi', 20), Person('wangwu', 25)]
r = sorted(p_list, key=operator.attrgetter('age'))
for i in r:
print(i)
输出结果:
Person(name=zhangshan, age=17)
Person(name=xiemanR, age=18)
Person(name=lisi, age=20)
Person(name=wangwu, age=25)
PS:其实第四种⽅法是调⽤第三种⽅法的,只是简单封装了⼀下,我们看看operator.attrgetter实现就知道了:
class attrgetter:
def __init__(self, attr, *attrs):
if not attrs:
if not isinstance(attr, str):
raise TypeError('attribute name must be a string')
names = attr.split('.')
def func(obj):
for name in names:
obj = getattr(obj, name)
return obj
self._call = func
else:
getters = tuple(map(attrgetter, (attr,) + attrs))
def func(obj):
return tuple(getter(obj) for getter in getters)
self._call = func
def __call__(self, obj):
return self._call(obj)
完。
补充知识:深⼊理解python对象及属性
类属性和实例属性
⾸先来看看类属性和类实例的属性在python中如何存储,通过__dir__⽅法来查看对象的属性
>>> class Test(object):
pass
>>> test = Test()
# 查看类属性
>>> dir(Test)
['__class__','__delattr__','__dict__','__doc__','__format__',
'__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__']
# 查看实例属性
>>> dir(test)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
getattribute方法返回类型'__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__']
我们主要看⼀个属性__dict__,因为 __dict__保存的对象的属性,看下⾯⼀个例⼦
>>> class Spring(object):
...  season = "the spring of class"
...
# 查看Spring类保存的属性
>>> Spring.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
'season': 'the spring of class',
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring' objects>,
'__doc__': None})
# 通过两种⽅法访问类属性
>>> Spring.__dict__['season']
'the spring of class'
>>> Spring.season
'the spring of class'
发现__dict__有个'season'键,这就是这个类的属性,其值就是类属性的数据.
接来看,看看它的实例属性
>>> s = Spring()
# 实例属性的__dict__是空的
>>> s.__dict__
{}
# 其实是指向的类属性
>>> s.season
'the spring of class'
# 建⽴实例属性
>>> s.season = "the spring of instance"
# 这样,实例属性⾥⾯就不空了。这时候建⽴的实例属性和类属性重名,并且把它覆盖了
>>> s.__dict__
{'season': 'the spring of instance'}
>>> s.__dict__['season']
'the spring of instance'
>>> s.season
'the spring of instance'
# 类属性没有受到实例属性的影响
>>> Spring.__dict__['season']
'the spring of class'
>>> Spring.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>, 'season': 'the spring of class', '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None})
# 如果将实例属性删除,⼜会调⽤类属性
>>> del s.season
>>> s.__dict__
{}
>>> s.season
'the spring of class'
# ⾃定义实例属性,对类属性没有影响
>>> s.lang = "python"
>>> s.__dict__
{'lang': 'python'}
>>> s.__dict__['lang']
'python'
# 修改类属性
>>> Spring.flower = "peach"
>>> Spring.__dict__
dict_proxy({'__module__': '__main__',
'flower': 'peach',
'season': 'the spring of class',
'__dict__': <attribute '__dict__' of 'Spring' objects>, '__weakref__': <attribute '__weakref__' of 'Spring' objects>, '__doc__': None})
>>> Spring.__dict__['flower']
'peach'
# 实例中的__dict__并没有变化
>>> s.__dict__
{'lang': 'python'}
# 实例中不到flower属性,调⽤类属性
>>> s.flower
'peach'
下⾯看看类中包含⽅法,__dict__如何发⽣变化
# 定义类
>>> class Spring(object):
...  def tree(self, x):
...    self.x = x
...    return self.x
...
# ⽅法tree在__dict__⾥⾯
>>> Spring.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
'__weakref__': <attribute '__weakref__' of 'Spring' objects>,
'__module__': '__main__',
'tree': <function tree at 0xb748fdf4>,
'__doc__': None})
>>> Spring.__dict__['tree']
<function tree at 0xb748fdf4>
# 建⽴实例,但是__dict__中没有⽅法
>>> t = Spring()
>>> t.__dict__
{}
# 执⾏⽅法
>>> t.tree("xiangzhangshu")
'xiangzhangshu'
# 实例⽅法(t.tree('xiangzhangshu'))的第⼀个参数(self,但没有写出来)绑定实例 t,透过 self.x 来设定值,即给 t.__dict__添加属性值。
>>> t.__dict__
{'x': 'xiangzhangshu'}
# 如果没有将x 赋值给 self 的属性,⽽是直接 return,结果发⽣了变化
>>> class Spring(object):
...  def tree(self, x):
...    return x
>>> s = Spring()
>>> s.tree("liushu")
'liushu'
>>> s.__dict__
{}
需要理解python中的⼀个观点,⼀切都是对象,不管是类还是实例,都可以看成是对象,符合object.attribute ,都会有⾃⼰的属性
使⽤__slots__优化内存使⽤
默认情况下,python在各个实例中为名为__dict__的字典⾥存储实例属性,⽽字典会消耗⼤量内存(字典要使⽤底层散列表提升访问速度), 通过__slots__类属性,在元组中存储实例属性,不⽤
字典,从⽽节省⼤量内存
# 在类中定义__slots__属性就是说这个类中所有实例的属性都在这⼉了,如果⼏百万个实例同时活动,能节省⼤量内存
>>> class Spring(object):
...  __slots__ = ("tree", "flower")
...
# 仔细看看 dir() 的结果,还有__dict__属性吗?没有了,的确没有了。也就是说__slots__把__dict__挤出去了,它进⼊了类的属性。
>>> dir(Spring)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flower', 'tree'] >>> Spring.__slots__
('tree', 'flower')
# 实例化
>>> t = Spring()
>>> t.__slots__
('tree', 'flower')
# 通过类赋予属性值
>>> = "liushu"
# tree这个属性是只读的, 实例不能修改
>>> t.tree = "guangyulan"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Spring' object attribute 'tree' is read-only
>>> t.tree
'liushu'
# 对于⽤类属性赋值的属性,只能⽤来修改
>>> = "guangyulan"
>>> t.tree
'guangyulan'
# 对于没有⽤类属性赋值的属性,可以通过实例来修改
>>> t.flower = "haitanghua"
>>> t.flower
'haitanghua'
# 实例属性的值并没有传回到类属性,你也可以理解为新建⽴了⼀个同名的实例属性
>>> Spring.flower
<member 'flower' of 'Spring' objects>
# 如果再给类属性赋值
>>> Spring.flower = "ziteng"
>>> t.flower
'ziteng'
如果使⽤的当,__slots__可以显著节省内存,按需要注意⼀下问题
在类中定义__slots__之后,实例不能再有__slots__所列名称之外的其他属性
每个⼦类都要定义__slots__熟悉,因为解释器会忽略继承__slots__属性
如果不把__werkref__加⼊__slots__,实例不能作为弱引⽤的⽬标
属性的魔术⽅法
来看⼏个魔术⽅法
__setattr__(self,name,value):如果要给 name 赋值,就调⽤这个⽅法。
__getattr__(self,name):如果 name 被访问,同时它不存在的时候,此⽅法被调⽤。
__getattribute__(self,name):当 name被访问时⾃动被调⽤(注意:这个仅能⽤于新式类),⽆论 name 是否存在,都要被调⽤。
__delattr__(self,name):如果要删除 name,这个⽅法就被调⽤。
>>> class A(object):
...  def __getattr__(self, name):
...    print "You use getattr"
...  def __setattr__(self, name, value):
...    print "You use setattr"
...    self.__dict__[name] = value
# a.x,按照本节开头的例⼦,是要报错的。但是,由于在这⾥使⽤了__getattr__(self, name) ⽅法,当发现 x 不存在于对象的__dict__中的时候,就调⽤了__getattr__,即所谓“拦截成员”。
>>> a = A()
>>> a.x
You use getattr
# 给对象的属性赋值时候,调⽤了__setattr__(self, name, value)⽅法,这个⽅法中有⼀句 self.__dict__[name] = value,通过这个语句,就将属性和数据保存到了对象的__dict__中
>>> a.x = 7
You use setattr
# 测试__getattribute__(self,name)
>>> class B(object):
...  def __getattribute__(self, name):
...    print "you are useing getattribute"
...    return object.__getattribute__(self, name)
# 返回的内容⽤的是 return object.__getattribute__(self, name),⽽没有使⽤ return self.__dict__[name]。因为如果⽤这样的⽅式,就是访问 self.__dict__,只要访问这个属性,就要调⽤`getattribute``,这样就导致了⽆限递归# 访问不存在的成员,可以看到,已经被__getattribute__拦截了,虽然最后还是要报错的。
>>> b = B()
>>> b.y
you are useing getattribute
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getattribute__
AttributeError: 'B' object has no attribute 'y'
Property函数
porperty可以作为装饰器使⽤把⽅法标记为特性
class Vector(object):
def __init__(self, x, y):
# 使⽤两个前导下划线,把属性标记为私有
self.__x = float(x)
self.__y = float(y)
# porperty装饰器把读值⽅法标记为特性
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
vector = Vector(3,4)
print(vector.x, vector.y)
使⽤property可以将函数封装为属性
class Rectangle(object):
"""
the width and length of Rectangle
"""
def __init__(self):
self.width = 0
self.length = 0
def setSize(self, size):
self.width, self.length = size
def getSize(self):
return self.width, self.length
if __name__ == "__main__":
r = Rectangle()
r.width = 3
r.length = 4
Size()  # (3,4)
r.setSize( (30, 40) )
print r.width  # 30
print r.length  # 40
这段代码可以正常运⾏,但是属性的调⽤⽅式可以改进,如下:
class Rectangle(object):
"""
the width and length of Rectangle
"""
def __init__(self):
self.width = 0
self.length = 0
def setSize(self, size):
self.width, self.length = size
def getSize(self):
return self.width, self.length
# 使⽤property⽅法将函数封装为属性,更优雅
size = property(getSize, setSize)
if __name__ == "__main__":
r = Rectangle()
r.width = 3
r.length = 4
print r.size  # (30, 40)
r.size = 30, 40
print r.width  # 30
print r.length  # 40
使⽤魔术⽅法实现:
class NewRectangle(object):
def __init__(self):
self.width = 0
self.length = 0
def __setattr__(self, name, value):
if name == 'size':
self.width, self, length = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
if name == 'size':
return self.width, self.length
else:
raise AttrubuteErrir
if __name__ == "__main__":
r = Rectangle()
r.width = 3
r.length = 4
print r.size  # (30, 40)
r.size = 30, 40
print r.width  # 30
print r.length  # 40
属性的获取顺序
最后我们来看看熟悉的获得顺序:通过实例获取其属性,如果在__dict__中有相应的属性,就直接返回其结果;如果没有,会到类属性中。
看下⾯⼀个例⼦:
class A(object):
author = "qiwsir"
def __getattr__(self, name):
if name != "author":
return "from starter to master."
if __name__ == "__main__":
a = A()
print a.author # qiwsir
print a.lang # from starter to master.
当 a = A() 后,并没有为实例建⽴任何属性,或者说实例的__dict__是空的。但是如果要查看 a.author,因为实例的属性中没有,所以就去类属性中,发现果然有,于是返回其值 “qiwsir”。但是,在 a.lang的时候,不仅实例属性中没有,类属性中也没有,于是就调⽤了__getattr__()⽅法。在上⾯的类中,有这个⽅法,如果没有__getattr__()⽅法呢?如果没有定义这个⽅法,就会引发AttributeError,这在前⾯已经看到了。
以上这篇Python获取对象属性的⼏种⽅式⼩结就是⼩编分享给⼤家的全部内容了,希望能给⼤家⼀个参考,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。