1.类属性和实例属性
class Foo(object):
a = 10 # 类属性
@classmethod
def foo_cls(cls):
pass
def __init__(self):
self.b = 12 # 实例属性
def mf(self):
pass
print(dir(Foo))
print(Foo.__dict__)
foo = Foo()
print(dir(foo))
print(foo.__dict__)
foo.a = 100
print(foo.__dict__)
运行结果
['__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__', 'a', 'foo_cls', 'mf']
{'__module__': '__main__', 'a': 10, 'foo_cls': <classmethod object at 0x104bef048>, '__init__': <function Foo.__init__ at 0x104beb620>, 'mf': <function Foo.mf at 0x104beb598>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
['__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__', 'a', 'b', 'foo_cls', 'mf']
{'b': 12}
{'b': 12, 'a': 100}
注意:
- 类不可以调用实例属性
- 对象调用属性的时候,先从对象的
__dict__
中寻找,如果有,直接返回;如果没有,则从类的__dict__
中去找,如果有直接返回,如果没有,则报错 - 每个对象创建都会有自己的
__dict__
,因此创建多个对象就会占用很大的内存空间,可以使用solt解决
2.__getattr__
和__getattribute__
的使用
当我们调用对象属性的时候,其实内部是隐式的调用魔法函数__getattr__
和__getattribute__
。执行顺序:
- 先执行
__getattribute__(self,item)
函数。该函数内部定义了属性的查找顺序,从对象.__dict__
再到类.__dict__
,一旦找到了就直接返回,不会执行__getattr__
。一般不用重新定义,否则使用不当会出现循环递归。 - 如果
__getattribute__
没有找到属性,再执行__getattr__(self,item)
函数。
class Foo(object):
a = 10
def __init__(self):
self.b = 12
def __getattr__(self, item):
print(item)
return 120
# def __getattribute__(self, item):
# print(item)
# return 20 # 一旦这么定义,所有的属性值都是20
foo = Foo()
print(foo.a) # 10。首先执行 __getattribute__函数,类.__dict__中包含了b属性,直接返回
print(foo.b) # 12。首先执行 __getattribute__函数,对象.__dict__中包含了b属性,直接返回
print(foo.m) # m 120。__getattribute__函数没有找到m属性,所以继续调用__getattr__函数
3.循环递归
如果__getattribute__
中调用了对象的属性,那么又会去执行__getattribute__
魔法方法,这样就陷入了无穷无尽的递归当中。
class Foo(object):
a = 10
def __init__(self):
self.b = 12
def __getattr__(self, item):
print(item)
return 120
def __getattribute__(self, item):
print(item)
return self.b # 等价于self.__getattribute__(item),陷入循环递归当中
foo = Foo()
print(foo.b)
结果输出
b
b
b
.
.
.
Traceback (most recent call last):
File "/Users/Liang/Documents/PyProject/demo1.py", line 18, in <module>
print(foo.b)
File "/Users/Liang/Documents/PyProject/demo1.py", line 14, in __getattribute__
return self.b
File "/Users/Liang/Documents/PyProject/demo1.py", line 14, in __getattribute__
return self.b
File "/Users/Liang/Documents/PyProject/demo1.py", line 14, in __getattribute__
return self.b
[Previous line repeated 328 more times]
File "/Users/Liang/Documents/PyProject/demo1.py", line 13, in __getattribute__
print(item)
RecursionError: maximum recursion depth exceeded while calling a Python object
4.__setattr__
的使用
__setattr__方法
会拦截所有属性的的赋值语句。如果定义了这个方法,self.arrt = value
就会变成self.__setattr__("attr", value).这个需要注意。当在__setattr__方法内对属性进行赋值是,不可使用self.attr = value
,因为他会再次调用self.__setattr__("attr", value)
,则会形成无穷递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.__dict__['name'] = value
。
class Foo(object):
def __init__(self):
print('enter init')
self.m = 20
def __setattr__(self, key, value):
print('set',key,value)
return self.__dict__.setdefault(key,value)
foo = Foo() # 打印 ‘enter init’和‘set m 20’。是因为self.m=10会调用__setattr__
foo.n = 10 # 打印 ‘set m 10’
print(foo.n) # 10
网友评论