__dict__
- Python 中,一切皆对象
- 不管是类还是实例的属性和方法,都符合
object.attribute
格式。并且属性类型
class A(object):
pass
a = A()
dir(a)
输出:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__init_subclass__','__le__','__lt__', '__module__','__ne__','__new__', '__reduce__', '__reduce_ex__','__repr__','__setattr__', '__sizeof__', '__str__', '__subclasshook__','__weakref__']
class Spring(object):
season = 'the spring of class'
Spring.__dict__
输出:
mappingproxy({'__dict__': <attribute '__dict__' of 'Spring' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring' objects>,
'season': 'the spring of class'})
#访问的是类属性
print(Spring.__dict__['season'])
print(Spring.season)
输出:
'the spring of class'
#访问的是实例属性,因为class中没有定义实例属性所以打印为空
s = Spring()
s.__dict__
输出:
{}
s.season #访问的类属性
输出:
'the spring of class'
#为class 的实例 中添加一个 season 属性
s.season = "instance";
s.__dict__
输出:
{'season': 'instance'}
#当类属性和实例属性重名时,实例属性会覆盖掉类属性
s.__dict__['season']
输出:
'instance'
#但是类属性不会变
Spring.__dict__['season']
输出:
'the spring of class'
Spring.season
输出:
'the spring of class'
#删除实例属性后,就回到了实例化类的时候的状态,没有实例属性,只有类属性
del s.season
s.__dict__
输出:
{}
s.season #类属性
输出:
'the spring of class'
#属性和方法是同样的
class Spring_def(object):
def tree(self,x):
self.x= x
return self.x
Spring_def.__dict__
输出:
mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_def' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring_def' objects>,
'tree': <function __main__.Spring_def.tree>})
Spring_def.__dict__['tree']
输出:
<function __main__.Spring_def.tree>
t = Spring_def()
t.__dict__
输出:
{}
t.tree('value')
t.__dict__
输出:
{}
class Spring_no(object):
def tree(self,x):
return x
ts = Spring_no()
ts.tree('No arg')
ts.__dict__
输出:
{}
Spring_no.__dict__
输出:
mappingproxy({'__dict__': <attribute '__dict__' of 'Spring_no' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Spring_no' objects>,
'tree': <function __main__.Spring_no.tree>})
__slots__
- 能够限制属性的定义
- 优化内存使用
- 把
__dict__
替换成__slots__
- 用类给一个属性赋值后,实例就不能给这个属性赋值了。
- 也不能新增实例属性,这样就把属性管控了起来。内存得到优化
- 大量实例的话会显现出效果
class Spring_s(object):
__slots__ = ("tree","flows")
dir(Spring_s)
输出:
['__class__','__delattr__','__dir__','__doc__','__eq__', '__format__','__ge__','__getattribute__', '__gt__','__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'flows', 'tree']
Spring_s.__dict__
输出:
mappingproxy({'__doc__': None,
'__module__': '__main__',
'__slots__': ('tree', 'flows'),
'flows': <member 'flows' of 'Spring_s' objects>,
'tree': <member 'tree' of 'Spring_s' objects>})
Spring_s.__slots__
输出:
('tree', 'flows')
ts = Spring_s()
ts.__slots__
输出:
('tree', 'flows')
#使用__slots__后就没有__dict__ 属性了
ts.__dict__
输出:
#错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-f3db88226dd5> in <module>()
1 #使用__slots__后就没有__dict__ 属性了
----> 2 ts.__dict__
AttributeError: 'Spring_s' object has no attribute '__dict__'
Spring_s.tree = "liushu"
Spring_s.tree
输出:
'liushu'
# 报错中显示 实例属性 tree 只是制度的,不能修改
ts.tree = "yangshu"
输出:
#错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-42-d6b9cb191c20> in <module>()
1 # 报错中显示 实例属性 tree 只是制度的,不能修改
----> 2 ts.tree = "yangshu"
AttributeError: 'Spring_s' object attribute 'tree' is read-only
ts.tree
输出:
'liushu'
- 前面已经通过类给属性赋值了,就不能用实例属性来修改。
- 当使用实例给属性赋值后,还是可以再用实例来给属性赋值,但是当这个属性被类赋值后,就不能再用实例给属性赋值了,只能用类来给属性赋值.
#第一次实例赋值
ts.flows = "meigui"
ts.flows
输出:
'meigui'
#第二次实例赋值
ts.flows = "yueji"
ts.flows
输出:
'yueji'
#使用类赋值
Spring_s.flows = "shuixianhua"
Spring_s.flows
输出:
'shuixianhua'
#实例的属性的值会变成类属性的值
ts.flows
输出:
'shuixianhua'
#再用实例给属性赋值就会报只读错误
ts.flows = "qianniuhua"
输出:
#错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-48-726c06affe50> in <module>()
1 #再用实例给属性赋值就会报只读错误
----> 2 ts.flows = "qianniuhua"
AttributeError: 'Spring_s' object attribute 'flows' is read-only
Spring_s.flows = "baihe"
Spring_s.flows
输出:
'baihe'
ts.flows
输出:
'baihe'
- 实例中添加属性
# 新增实例失败
ts.water = "green"
输出:
#错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-52-cde7efd2f5b8> in <module>()
----> 1 ts.water = "green"
AttributeError: 'Spring_s' object has no attribute 'water'
__getattr__
、__setattr__
和其他类似方法
-
__setattr__(self,name,value):
如果要给name赋值,就调用这个方法 -
__getattr__(self,name):
如果name被访问,同事它不存在,此方法被调用 -
__getattribute__(self,name):
当name被访问时自动被调用(注意:这个仅能用于新式类),无论name是否存在,都要被调用 -
__delattr__(self,name):
如果要删除name,这个方法就被调用。
class B(object):
pass
b = B()
b.x
输出:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-42-f8f5c58b62c7> in <module>()
2 pass
3 a = A()
----> 4 a.x
AttributeError: 'A' object has no attribute 'x'
x 不是势力的成员(属性和方法)所以报错。也就是说,如果访问 b.x ,它不存在,那么就要转向到某个操作 ,我们把这种情况叫做“拦截”。
class C(object):
def __getattr__(self,name):
print('you use getattr')
def __setattr__(self,name,value):
print('you use setattr')
self.__dict__[name] = value
类C是新式类,出来2个方法,没有别的属性
c = C()
c.x
输出:
you use getattr
本来按照开头class B
是要报错的。但是由于这里使用了 __getattr__(self,name)
当发现x不存在与对象__dict__
时,就会调用__getattr
“拦截成员” 。
c.x = 7
输出:
you use setattr
给对象的属性赋值是,调用了__setattr__(self,name,value)
方法,这个方法有一句 self.__dict__[name] = value
通过这个语句,就将属性和数据保存到了对象的 __dict__
中。如果再调用这个属性:
c.x # x 已经存在于对象的 __dict__中
输出:
7
使用__getattribute__(self,name):
,只要访问属性就会调用它
class getAt(object):
def __getattribute__(self,name):
print('使用了getattribute')
return object.__getattribute__(self,name)
at = getAt()
at.y
输出:
使用了getattribute
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-54-6e81b53b32de> in <module>()
1 at = getAt()
----> 2 at.y
<ipython-input-53-a6779a4b213a> in __getattribute__(self, name)
2 def __getattribute__(self,name):
3 print('使用了getattribute')
----> 4 return object.__getattribute__(self,name)
AttributeError: 'getAt' object has no attribute 'y'
- 访问一个不存在的属性,虽然拦截了但还是报错了。
- 但是只要赋值后就可以访问了,因为这样就意味着做个属性已经存在于
__dict__
中了。虽然依然被拦截,但是会把结果返回。
at.y = 9
at.y
输出:
使用了getattribute
9
注意:这里没有
return self.__dict__[name]
,因为如果用这样的方法访问self.__dict__
只要访问这个方法,就会去调用__getattribute__
,这样就会无限递归下去,造成死循环。
示例:
''' study __getattr__ and __setattr__'''
class Rectangle(object):
''' the width and length of Rectangle'''
def __init__(self):
self.width = 0
self.height = 0
def setSize(self,size):
self.width,self.height = size
def getSize(self):
return self.width,self.height
if __name__ == "__main__":
r = Rectangle()
r.width = 3
r.height = 4
print (r.getSize())
r.setSize((30,40))
print(r.width)
print(r.height)
输出:
(3, 4)
30
40
长宽赋值的时候必须是一个元组,里面包含长宽。改进。。。
class Rectangle_2(object):
''' 使用property改进调用方法传参'''
def __init__(self):
self.width = 0
self.height = 0
def setSize(self,size):
self.width,self.height = size
def getSize(self):
return self.width,self.height
#---------新加---------#
size = property(getSize,setSize)
#---------结束---------#
if __name__ == "__main__":
r = Rectangle_2()
r.width = 3
r.length = 4
print(r.size)
r.size = 30, 40
print (r.width)
print (r.length)
输出:
(3, 0)
30
4
虽然方法调用就像属性一样,但是没有用上特殊方法。继续改进。。。
class Rectangle_3(object):
'''使用特殊方法'''
def __init__(self):
self.width = 0
self.height = 0
def __setattr__(self, name, value):
if name == "size":
self.width, self.height = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
if name == "size":
return self.width,self.height
else:
return AttributeError
if __name__ == "__main__":
nr = Rectangle_3()
nr.width = 3
nr.height = 4
print(nr.size)
nr.size = 30,40
print(nr.width)
print(nr.height)
输出:
(3, 4)
30
40
获得属性顺序
- 通过实例获取其属性,如果在
__dict__
中有,就直接返回其结果,如果没有,就会到类属性中找。
class search(object):
author = 'Python'
def __getattr__(self , name):
if name != 'author':
return '查询的不是author属性'
if __name__ == '__main__':
a = search()
print(a.author)
print(a.none)
print(a.__dict__)
输出:
Python
查询的不是author属性
{}
- 初始化后 a 没有建立任何实例属性。实例属性
__dict__
是空,没有属性值,a.author
则有值,因为是类属性,而实例中又没有,所以就去类属性中去找,所以就返回了'Python'
- 当a.none 的时候不仅实例中没有,类中也没有,所以就调用了
__getattr__
方法。如果不写__getattr__
方法就会报错了。 - 这就是 通过实例查找特性的顺序
参考:《跟着老齐学Python:从入门到精通》 作者:齐伟 电子工业出版社