22. 内建属性
22.1. __name__
__name__
标识模块的名字,可以显示一个模块的某功能是 被自己执行还是被别的文件调用执行 。
如果被自己执行,那么 __name__== "__main__"
;
如果被调用执行,那么 __name__
值为被调用模块的名字。
22.2. __doc__
__doc__
是模块的文档字符串(docstring),是由三引号包围的字符串,定义在文件/类/函数的头部。
定义 Module.py 内容如下:
1""" An example module."""
2
3class A(object):
4 """
5 class A.
6 """
7 pass
8
9def func():
10 """ function f. """
11 pass
1>>> import Module
2>>> print(Module.__doc__)
3An example module.
4>>> print(Module.A.__doc__)
5
6 class A.
7
8>>> print(Module.func.__doc__)
9
10 function f.
22.3. __call__
如果在类中实现了 __call__
方法,那么实例对象将成为一个可调用对象。自定义函数、内建函数和类都属于可调用对象,但凡是可以把一对括号 ()
应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable
。
1class A(object):
2 def __init__(self, a):
3 self._a = a
4
5 def __call__(self, b):
6 return self._a * b
1>>> obj = A(5)
2>>> callable(A)
3True
4>>> callable(obj)
5True
6>>> obj(10)
750
22.4. __dict__
__dict__
存储了类和实例的一些属性。
在 __init__
中声明的变量( self.xxx
),会存到实例的 __dict__
中;类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类的 __dict__
中。
1class A(object):
2""" class A. """
3 Aa = 10
4 def __init__(self, a):
5 self._a = a
6
7 def __call__(self, b):
8 return self._a * b
1>>> from Module import *
2>>> A.__dict__
3mappingproxy({'__module__': 'Module', '__doc__': ' class A. ', 'Aa': 10, '__init__': <function A.__init__ at 0x000001F1D9E2CEE0>, '__call__': <function A.__call__ at 0x000001F1D9E2CF70>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>})
4>>> obj = A(0)
5>>> obj.__dict__
6{'_a': 0}
1class A(object):
2 Aa = 10
3 def __init__(self, **kwargs):
4 self.__dict__.update(kwargs)
5
6 def func(self, b):
7 pass
1>>> kwargs = {"a":1, "b":2, "c": 3}
2>>> obj = A(**kwargs)
3>>> obj.__dict__
4{'a': 1, 'b': 2, 'c': 3}
5>>> obj.c
63
7>>> A.__dict__
8mappingproxy({'__module__': 'Module', 'Aa': 10, '__init__': <function A.__init__ at 0x0000024E2C82CEE0>, 'func': <function A.func at 0x0000024E2C82CF70>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
22.5. __setattr__
默认情况下,实例属性赋值,被赋值的属性和值会存入实例属性字典 __dict__
中。
如果类自定义了 __setattr__
方法,当通过实例获取属性(instance.attr)并尝试赋值时,就会调用 __setattr__
。
1class A(object):
2 def __init__(self, a):
3 self._a = a
1>>> obj = A(0)
2>>> obj._b = 1
3>>> obj.__dict__
4{'_a': 0, '_b': 1}
1class A(object):
2 def __init__(self, a):
3 self._a = a
4 def __setattr__(self, name, value):
5 print("set attr.", name)
6 self.__dict__[name] = value
1>>> obj = A(0) ## 初始化会调用 __setattr__
2set attr. _a
3>>> obj._b = 1
4set attr. _b
5>>> obj.__dict__
6{'_a': 0, '_b': 1}
Warning
如果在 __setattr__
中试图通过 self.xxx
来访问其他属性,容易出现错误。比如,初始化之前, __dict__
中还没有插入属性,是无法访问的。
22.6. __getattr__
实例通过 点号 访问属性(instance.attr),只有当属性没有在实例的 __dict__
或类的 __dict__
中没有找到,才会调用 __getattr__
。当属性可以通过正常机制追溯到时,__getattr__
是不会被调用的。
1class A(object):
2 def __init__(self, a):
3 self._a = a
4 self.dic = {"_b": 1, "_c": 2}
5
6 def __getattr__(self, attr):
7 print("get attr.", attr)
8 if attr in self.dic:
9 return self.dic[attr]
10 return -1
1>>> obj = A(0)
2>>> obj._a
30
4>>> obj._b
5get attr._b
61
Note
多进程 multiprocessing 为了实现数据共享,会对数据进行序列化(pickling),其他进程读的时候再反序列化(unpickling)。反序列化时,数据所对应的类的 __init__
方法不会被调用,因而上例中 self.dic
对执行反序列化的进程是不可见的,会造成 __getattr__
无限循环调用:
>>> import pickle
>>> obj = A(0)
>>> new_obj = pickle.loads(pickle.dumps(obj))
...
RecursionError: maximum recursion depth exceeded while calling a Python object.
解决方法:直接用 self.__dict__
来存放原本 self.dic
的数据。
参考:https://stackoverflow.com/questions/62331047/why-am-i-receiving-a-recursion-error-with-multiprocessing
22.7. __getattribute__
实例通过 点号 访问属性(instance.attr), __getattribute__
方法始终会被调用,无论属性是否能通过 __dict__
追溯到。如果类还定义了 __getattr__
方法,除非它被 __getattribute__
显式调用,或者 __getattribute__
方法出现 AttributeError
异常,否则 __getattr__
方法永远不会被调用。
1class A(object):
2 def __init__(self, a):
3 self._a = a
4 self.dic = {"_b": 1, "_c": 2}
5
6 def __getattribute__(self, attribute):
7 if attribute == "_a":
8 return -1
9 else:
10 raise AttributeError("no attribute {}".format(attribute))
11
12 def __getattr__(self, attr):
13 print("get attr.", attr)
14 return 2
1>>> obj =A(0)
2>>> obj._a
3-1
4>>> obj._b
5get attr. _b
62
Warning
在抛出 AttributeError
异常之后,如果此时在 __getattr__
中试图通过 self.xxx
来访问其他属性(如 self.dic
)时,会出现死循环。
22.8. 参考资料
Python __dict__属性详解
python 自定义属性访问 __setattr__, __getattr__,__getattribute__, __call__
Python中__setattr__, __getattr__和__getattribute__