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. 参考资料

  1. Python __dict__属性详解

  1. python 自定义属性访问 __setattr__, __getattr__,__getattribute__, __call__

  1. Python中__setattr__, __getattr__和__getattribute__