文章目录
- 特殊方法和特殊属性一览
- 官方的文档
- 特殊属性或者方法介绍
- 1. 基本定制
- `object.__new__(cls[, ...])`
- `object.__init__(self[, ...])`
- `object.__del__(self)`
- `object.__repr__(self)`
- `object.__str__(self)`
- `object.__bytes__(self)`
- `object.__format__(self, format_spec)`
- `object.__lt__(self, other)`
- object.__le__(self, other)
- `object.__eq__(self, other)`
- `object.__ne__(self, other)`
- object.__gt__(self, other)
- `object.__ge__(self, other)`
- `object.__hash__(self)`
- `object.__bool__(self)`
- 2. 定制属性访问
特殊方法和特殊属性一览
在系列文章里面也写过了
官方的文档
官方的文档我放在 这两个链接里面了.
特殊方法 | 含义 |
---|---|
obj.__dict__ | 对象的属性字典 |
obj.__class__ | 对象的所属的类 |
class.__bases__ | 类的基类元组(多插`继承) |
class.__base__ | 类的基类 |
class.__mro__ | 类层次结构 |
class.__subclasses__ | 子类列表 |
__init__ | 对象创建 p = Person() |
__del__ | 对象回收 |
__repr__ , __str__ | 打印出来的 对象信息 |
__call__ | func() |
__getattr__ | obj.xxx 获取xxx属性 |
__setattr__ | obj.xxx = value |
__getitem__ | 索引 和 字典 a[key], a[index] |
__len__ | len(a) |
运算符号 太多. 细节大家可以看文档.
函数属性 | 含义 |
---|---|
__doc__ | 函数的文档字符串 |
__name__ | 函数的名称。 |
__qualname__ | 函数的 qualified name . |
__module__ | 定义函数的模块的名称 |
__defaults__ | 包含具有默认值的参数的, 默认参数值的元组 |
__code__ | 表示已编译函数体的代码对象。 |
__globals__ | 对保存函数全局变量的字典的引用——定义函数的模块的全局命名空间。 |
__dict__ | 支持任意函数属性的命名空间。 |
__closure__ | 包含函数自由变量绑定的 cell 的元组。有关 cell_contents 属性。 |
__annotations__ | 一种包含参数注释的字典。dict的键是参数名,以及 ‘return’ 对于返回注释(如果提供)。 |
__kwdefaults__ | 包含仅关键字参数的默认值的dict。 |
cell 对象具有属性 cell_contents . 这可以用于获取 cell 的值,也可以设置该值。
可以从函数的代码对象中检索有关函数定义的其他信息;
请参见下面的内部类型描述。
这个 cell
可以在type
types
模块中访问
实例属性 | 含义 |
---|---|
__self__ | 类实例对象 |
__func__ | 函数对象 |
生成器功能
实例属性 | 含义 |
---|---|
__next__() | 调用迭代器,用于推进迭代器,类似于每次循环会调用一次 __next__ |
__anext__() | 调用异步迭代器 |
模块
函数 | 含义 |
---|---|
__import__() | 函数导入模块 name ,可能使用给定的 globals 和 locals 确定如何解释包上下文中的名称。这个 来自列表 给出从模块导入的对象或子模块的名称 name . 标准实现不使用 locals 参数,并使用它 globals 仅用于确定 import 语句。 |
内部类型
只读属性 | 含义 |
---|---|
co_name | 给出函数名 |
co_argcount | 位置参数的总数 |
co_posonlyargcount | 仅位置参数的数目(包括具有默认值的参数) |
co_kwonlyargcount | 仅关键字参数的数目(包括具有默认值的参数) |
co_nlocals | 函数使用的局部变量数(包括参数); |
co_varnames | 是一个包含局部变量名称的元组(从参数名称开始); |
co_cellvars | 是一个包含被嵌套函数引用的局部变量名称的元组; |
co_freevars | 是包含自由变量名称的元组; |
co_code | 表示字节码指令序列的字符串; |
co_consts | 是一个包含字节码使用的文本的元组; |
co_names | 是一个包含字节码使用的名称的元组; |
co_filename | 是编译代码的文件名; |
co_firstlineno | 是函数的第一个行号; |
co_lnotab | 是一个字符串,用于编码从字节码偏移量到行号的映射(有关详细信息,请参阅解释器的源代码) |
co_stacksize | 是所需的堆栈大小; |
co_flags | 是一个整数,它为解释器编码多个标志。 |
frame 对象
特殊只读属性:
只读属性 | 含义 |
---|---|
f_back | 指向上一个堆栈帧(朝向调用方),或 None 如果这是底层堆栈框架; |
f_code | 是在该帧中执行的代码对象; |
f_locals | 是用来查找局部变量的字典; |
f_globals | 用于全局变量; |
f_builtins | 用于内置(内部)名称; |
f_lasti | 给出精确的指令(这是代码对象的字节码字符串的索引)。 |
特殊可写属性:
只读属性 | 含义 |
---|---|
f_trace | 如果不是 None ,是在代码执行期间为各种事件调用的函数(这由调试器使用)。 |
通常情况下,每个新震源测线都会触发一个事件-可通过设置禁用此功能。 f_trace_lines 到 False .
特殊属性或者方法介绍
类可以通过定义具有特殊名称的方法来实现某些由特殊语法(如算术运算或订阅和切片)调用的操作。
这是python的方法 operator overloading(运算符重载) ,允许类定义自己对语言运算符的行为。
例如,如果一个类定义了一个名为
__getitem__()
和x
是此类的实例
,然后x[i]
大致相当于type(x).__getitem__(x, i)
.
除上述情况外,尝试执行操作会在未定义适当方法时
引发异常
(通常 AttributeError
或 TypeError
)
将特殊方法设置为 None 指示相应的操作不可用。
例如,如果类 设置 _iter__()
到 None
,类不可迭代 ,因此调用 iter()
在它的实例上会引发 TypeError
(没有回到 __getitem__()
)
当实现一个
模拟任何内置类型
的类
时,重要的是,模拟的实现程度只能达到对被建模对象有意义
的程度。
例如,一些序列可以很好地检索单个元素,但提取切片可能没有意义。(其中一个例子是 NodeList W3C
文档对象模型中的接口。)
1. 基本定制
object.__new__(cls[, ...])
调用以创建类的新实例
cls.__new__()
是一个静态方法(特殊情况下,因此不需要将其声明为特殊情况),它将请求实例的类 作为其第一个参数。
其余参数是传递给对象构造函数表达式(对类的调用)的参数。
__new__()
的返回值 应该是新的对象实例(通常是 cls
的实例 )
典型的实现通过调用超类的 __new__()
方法应用 super().new(cls[, …]) 使用适当的参数,然后在返回新创建的实例之前根据需要修改它。
如果 __new__()
在对象构造期间调用,并返回 cls
,则新实例的 __init__()
方法的调用方式如下 __init__(self[, ...])
, self 是新实例,
其余参数与传递给对象构造函数的参数相同。
如果 __new__()
不返回cls
的实例 ,然后新实例的 __init__()
将不调用方法。
__new__()
主要用于允许不可变类型(如int、str或tuple)的子类自定义实例创建。
为了自定义类创建,它通常在 自定义元类中
被重写。
object.__init__(self[, ...])
在创建实例后调用(由 __new__()
,但在返回给调用方之前。这些参数是传递给类构造函数表达式的参数。
如果基类具有 __init__()
方法,派生类的 __init__()
方法(如果有)必须显式调用它,以确保正确初始化实例的基类部分
;
例如: super().init([args…])
因为 __new__()
和 __init__()
共同构建对象 (__new__()
创建它,以及 __init__()
要自定义它),__init__()
不能返回非None值 ;这样做会导致 TypeError
在运行时引发。
类似于这样子
object.__del__(self)
当实例即将被销毁时调用。这也称为终结器或(不正确地)析构函数。
如果基类具有 __del__()
方法,派生类的__del__()
方法(如果有)必须显式调用它,以 确保正确删除实例的基类部分。
这是可能的(尽管不推荐!)对于
__del__()
方法通过创建对实例的新引用来推迟对该实例的销毁。
这称为对象 复活 . 它取决于实现是否
__del__()
当一个复活的物体即将被摧毁时,第二次被称为 CPython 实现只调用一次。
不能保证 对解释器退出时仍存在的对象调用__del__() 方法。
object.__repr__(self)
被调用
repr()
用于计算对象的“正式”字符串表示形式的内置函数。
如果可能的话,这应该看起来像一个有效的python表达式,可以用来重新创建具有相同值的对象(给定适当的环境)。
如果这是不可能的,则为窗体的字符串 <…some useful description…> 应该归还返回值必须是字符串对象。
。
如果类定义 __repr__() 但不是 __str__()
然后 __repr__()
当需要该类实例的“非正式”字符串表示时也使用。
这通常用于调试,因此表示是信息丰富且明确的很重要。
object.__str__(self)
被称为 str(object) 以及内置功能 format() 和 print() 计算对象的“非正式”或良好打印的字符串表示。
返回值必须是 string 对象。
这种方法不同于 object.__repr__()
在这一点上没有期望 __str__()
返回有效的python表达式:
可以使用更方便或更简洁的表示。
由内置类型定义的默认实现 object 调用
object.__repr__() .
object.__bytes__(self)
被称为 bytes 计算对象的字节字符串表示。
这应该返回 bytes 对象。
object.__format__(self, format_spec)
被调用 format()
内置功能,通过扩展,评估 formatted string literals 以及 str.format() 方法生成对象的“格式化”字符串表示形式。
这个 format_spec 参数是包含所需格式选项说明的字符串。解释 format_spec 参数取决于类型实现 __format__()
但是,大多数类要么将格式委托给一个内置类型,要么使用类似的格式选项语法。
见 格式规范小型语言 有关标准格式语法的说明。
返回值必须是字符串对象。
在 3.4 版更改: 这个 __format__
方法 object 本身引发了 TypeError 如果传递了任何非空字符串。
在 3.7 版更改: object.__format__(x, '')
现在等于str(x)
而不是 format(str(x)
‘’) .
其实 就是 为 一个类 定义他的 格式化方法
class F:
def __init__(self, name, age):
self.name = name
self.age = age
def __format__(self, format_spec):
return format_spec.replace('%a',self.name).replace('%b',str(self.age))
f = F('onepisya', 20)
format(f, "my name is %a, age is %b")
# %a 和 %b 是我自己定义的。
# 用来 将 %a 和 %b 替换成 age 和 name
# 当然我也可以定义其他的东西, 比如时间
class Time_:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
self.f_speces = {
'ymd':'{self.year} {self.month} {self.day}',
'y/m/d':'{self.year}/{self.month}/{self.day}',
}
def __format__(self, format_spec):
f_spec = self.f_speces.get(format_spec)
if not f_spec:
return '这里可以返回默认的一种格式'
return f_spec.format(self=self)
t = Time_(2021, 5, 20)
format(t, 'ymd')
# '2021 5 20'
format(t, 'y/m/d')
# '2021/5/20'
format(t, 'xxxx')
# '这里可以返回默认的一种格式'
object.__lt__(self, other)
定义 小于 方法
less than
object.le(self, other)
定义 小于等于 方法
Less than or equal to
object.__eq__(self, other)
定义等于方法
equal to
object.__ne__(self, other)
定义不等于方法
not equal to
object.gt(self, other)
定义类的大于方法
great than
object.__ge__(self, other)
大于等于
great than equal to
这些是所谓的“丰富比较”方法。
运算符符号和方法名称之间的对应关系如下:
x<y 调用x.__lt__(y)
x<=y 调用 x.__le__(y)
,
x==y 调用 x.__eq__(y)
,
x!=y 调用 x.__ne__(y)
,
x>y 调用 x.__gt__(y)
x>=y 调用 x.__ge__(y)
.
富比较方法可能返回单例 NotImplemented 如果它不实现给定参数对的操作。
按照惯例, False 和 True 返回以进行成功的比较。
但是,这些方法可以返回任何值,因此如果比较运算符用于布尔上下文(例如,在 if 语句),python将调用
bool()
在值上确定结果是真还是假。
默认情况下,
__ne__()
代表参加__eq__()
并反转结果,除非 NotImplemented .
比较运算符之间没有其他隐含关系,例如 (x<y or x==y)
并不意味着 x<=y
.
要从单个根操作自动生成排序操作,请参见
functools.total_ordering()
请参阅上的段落 hash() 有关创建 hashable 支持自定义比较操作并可用作字典键的对象。
这些方法没有交换的参数版本(当左参数不支持操作,而右参数支持时使用);
相反, __lt__()
和 __gt__()
是彼此的反映,__le__()
和 __ge__()
是彼此的反映,以及 __eq__()
和 __ne__()
是他们自己的反映。
如果操作数的类型不同,而右操作数的类型是左操作数类型的直接或间接子类,则右操作数的反射方法具有优先级,否则左操作数的方法具有优先级。
不考虑虚拟子类化。
object.__hash__(self)
由内置函数调用 hash()
以及对hash集合成员的操作,包括 set
, frozenset
和 dict.__hash__()
应返回一个整数。
唯一需要的属性是
,比较相等的对象具有相同的hash
值;
建议将对象组件的hash
值混合在一起,这些组件也通过将对象打包成一个元组并对该元组进行hash
处理来参与对象比较。
例子
def __hash__(self):
return hash((self.name, self.nick, self.color))
注解 hash()
截断从对象自定义项返回的值 __hash__()
方法的大小 Py_ssize_t
.
这通常是64位构建上的8个字节,32位构建上的4个字节。
如果一个物体 __hash__()
必须在不同位大小的版本上进行互操作,请确保检查所有支持的版本的宽度。
一个简单的方法就是 python -c "import sys; print(sys.hash_info.width)"
.
如果类没有定义
__eq__()
方法不应定义__hash__()
操作;
如果它定义
__eq__()
但不是__hash__()
,其实例将不能用作hash
集合中的项。
如果类定义可变对象并实现
__eq__()
方法,不应实现__hash__()
,因为可hash
集合的实现要求键的hash
值是不可变的
(如果对象的hash
值更改,它将位于错误的hash桶中)。
用户定义的类具有 __eq__()
和 __hash__()
方法;
使用它们,所有对象都比较不相等(除了它们本身)和 x.__hash__()
, 返回适当的值,
以便
x == y
这两者都意味着x is y
和hash(x) == hash(y)
.
重写的类
__eq__()
并且没有定义__hash__()
会有它的__hash__()
隐式设置为None
.
当 __hash__()
类的方法是 None
,类的实例将引发 TypeError 当程序尝试检索其hash值时,也将在检查时正确标识为不可显示 isinstance(obj, collections.abc.Hashable)
如果一个类重写
__eq__()
需要保持执行__hash__()
在父类中,必须通过设置__hash__ = <ParentClass>.__hash__
如果类不重写
__eq__()
希望取消hash
支持,它应该包括__hash__ = None
在类定义中。
定义自己的类 __hash__()
明确提出了 TypeError
将被错误地标识为可hash
的isinstance(obj, collections.abc.Hashable) 调用
为了防止由于精心选择的输入而导致的拒绝服务,这些输入利用了dict插入的最坏情况性能,o(n^2)复杂性。有关详细信息,请参阅http://www.ocert.org/advisories/ocert-2011-003.html。
更改hash值会影响集合的迭代顺序。python从未保证过这种顺序(它通常在32位和64位构建之间变化)。
也见 PYTHONHASHSEED .
object.__bool__(self)
调用以实现真值测试和内置操作 bool() 应该返回 False 或 True .
如果未定义此方法,
__len__()
如果已定义,则调用,如果结果非零,则认为对象为真。
如果类既没有定义
__len__()
也不__bool__()
,它的所有实例都被认为是真的。
2. 定制属性访问
属性访问相关
object.__getattr__(self, name)
object.__getattribute__(self, name)
__getattribute__
和__getattr__
用于实例访问属性使用,__get__
方法是只能其实例属于类(owner, 另一个类)属性的时候生效- 只要有
__getattribute__
,任何属性访问都是这个的返回值
,从第 3条开始, 以下都是在__getattribute__
不存在或者有 AttributeError 异常发生的情况下描述的 - 访问不存在的属性,
__getattr__
生效 - 访问存在的属性,如果是描述符,描述符生效(描述符, 请看后面)
- 如果通过实例对描述符 进行赋值操作,又有 数据和非数据描述符 的区分,如果定义了
__set__
,那么此方法生效,并且仍然是原始的数据描述符号
,否则被赋值为新对象 描述符赋值如果是通过类的属性方式赋值
,而不是类的实例属性方式赋值,描述器失效- 针对描述符的说明: 描述符被
__getattribute__
调用的,如果重写了这个方法,将会阻止自动调用描述符
,数据描述符
总是覆盖了实例的__dict__
, 非数据描述符可能覆盖实例的__dict__
我使用 的版本是 3.6.8
object.__setattr__(self, name, value)
在尝试属性分配时调用。调用它而不是普通机制(即将值存储在实例字典中)。
name
是属性名,value
是要分配给它的值。
如果__setattr__()
要分配给实例属性,它应该使用相同的名称调用基类方法,例如,object.__setattr__(self, name, value)
.
object.__delattr__(self, name)
类似于
__setattr__()
但属性删除而不是赋值。当del obj.name
对对象有意义的话.
object.dir(self)
何时对象 调用
dir()
。必须返回序列。dir()
将返回的序列转换为列表并对其排序。 列出你的对象的属性
模块属性访问
object.__getattr__(self, name)
object.__dir__(self)
特殊名称 __getattr__
和 __dir__
也可用于自定义对模块属性的访问。这个 __getattr__
模块级的函数应接受一个参数,该参数是属性的名称,并返回计算值或引发 AttributeError
.
调用顺序
如果通过常规查找在模块对象上找不到属性,即 object.__getattribute__()
然后 __getattr__
在模块中搜索 __dict__
在提出 AttributeError
. 如果找到,则使用属性名调用它,并返回结果。
这个 __dir__
函数不应接受任何参数,并返回表示模块上可访问名称的字符串序列。如果存在,此函数将覆盖标准 dir()
搜索模块。
对于模块行为的更细粒度定制(设置属性、属性等),可以设置 __class__
模块对象的属性到的子类 types.ModuleType
. 例如::
import sys
from types import ModuleType
class VerboseModule(ModuleType):
def __repr__(self):
return f'Verbose {self.__name__}'
def __setattr__(self, attr, value):
print(f'Setting {attr}...')
super().__setattr__(attr, value)
sys.modules[__name__].__class__ = VerboseModule
定义模块 __getattr__
和设置模块 __class__
只影响使用属性访问语法进行的查找
直接访问模块全局(无论是通过模块内的代码,还是通过引用模块的全局字典)不受影响。
描述符
实现
以下方法仅
在包含该方法的类的实例
(所谓 描述符 )出现在 owner
类(描述符
必须位于owner类
字典中,或
者位于其父类
的类字典
中)。
在下面的示例中,“属性”是指其名称是 owner类
中属性的键的属性。 __dict__
object.__get__(self, instance, owner=None)
调用以获取owner类
(类属性访问)或该类的实例(实例属性访问)的属性
。可选的 owner
参数是所有者类,而 实例 属性是通过其访问的实例
此方法应返回计算的属性值或引发 AttributeError
异常。
object.__set__(self, instance, value)
调用以设置 instance的属性 , instance 将
owner类
转换为新值,value
.
注释,添加
__set__() 或 __delete__()
将描述符的类型更改为“数据描述符
”。见 调用描述符 了解更多详细信息。
object.__delete__(self, instance)
调用以删除实例上的属性 实例 属于所有者类。
object.__set_name__(self, owner, name)
在拥有类时调用 owner
已创建。描述符已分配给 name .
注解
__set_name__()
仅作为type
构造函数,因此在初始创建后将描述符添加到类时,需要使用适当的参数显式调用它:
class A:
pass
descr = custom_descriptor()
A.attr = descr
descr.__set_name__(A, 'attr')
见 创建类对象 了解更多详细信息。
3.6 新版功能.
属性 __objclass__
被解释为 inspect 模块,用于指定 定义此对象的类
(适当地设置它可以帮助动态类属性的运行时内省
)。
对于可调用文件,它可能指示给定类型(或子类
)的一个实例
应作为第一个位置参数
(例如,cpython为在C中实现的未绑定方法设置此属性)或必需作为第一个位置参数。
调用
通常,描述符是具有“绑定行为”的对象属性,
其属性访问被 描述符协议 中的方法覆盖:
__get__() , __set__() 和 __delete__()
如果这些方法中的任何一个是为对象定义的,则称为 描述符
属性访问的默认行为
是从 对象字典中 获取、设置或删除属性。
例如,
a.x
具有以开头的查找链a.__dict__['x']
然后type(a).__dict__['x']
并继续通过type(a)
不包括元类。
但是,如果查找值是 定义描述符方法之一的对象
,
那么python可以重写默认行为并
调用描述符方法
。
优先级链中出现这种情况的位置取决于定义了哪些描述符方法以及如何调用它们。
描述符调用的起点是绑定, a.x
参数的组合方式取决于 a
:
直拨调用
最简单也是最不常见的调用是当用户代码直接调用描述符方法时: x.get(a) .
实例绑定
如果绑定到对象实例,
a.x
转换为调用:type(a).__dict__['x'].__get__(a, type(a))
类绑定
如果绑定到类,
A.x
转换为调用:A.__dict__['x'].__get__(None, A)
super
如果
a
是super
的实例 ,
然后是绑定super(B, obj).m()
搜索obj.__class__.__mro__
对于基类A
在前面B
然后用调用调用描述符:A.__dict__['m'].__get__(obj, obj.__class__)
例如绑定,描述符调用的优先级取决于定义的描述符方法。
描述符可以定义
__get__() , __set__() 和 __delete__() .
如果它没有定义 __get__()
,然后访问该属性将返回描述符对象本身,除非对象的实例字典中有值。
如果描述符定义
__set__()
和/或__delete__()
,它是一个数据描述符
;
如果它两者都没有定义,则它是一个非数据描述符。
通常,数据描述符定义
__get__() 和 __set__()
,而非数据描述符只有__get__()
方法。
数据描述符 get() 和 set() (和/或 delete() )定义
总是覆盖实例字典中的重新定义。
相反,非数据描述符(没有定义
__set__()
, 仅仅定义了一个__get__())
可以被实例重写。
python方法(包括
staticmethod()
和classmethod()
)实现为非数据描述符
。
因此,实例
可以重新定义和重写方法
。
这允许单个实例获取与同一类的其他实例不同的行为。
这个property()
函数作为数据描述符实现
。因此,实例不能重写属性的行为
实验代码
class Des:
def __init__(self, name, age):
self.name = name
self.age = age
def __get__(self, instance, owner=None):
print('Des_get_instance', instance)
# 这个 instance代表是 Des 是 哪个 实例的属性
print('Des_get_owner', owner)
# owner 就是 一个 类
def __set__(self, instance, value):
print('Des_set_instance', instance)
# 实例 比如
# m = Main()
# m.des = 3
# m 就是 这里的实例
print('Des_set_value', value)
# 3 就是 value
def __getattr__(self, name):
return 'this is "__getattr__"'
def __getattribute__(self, name):
if name == 'pis':
raise AttributeError("error")
print('item', name)
return 'this is "__getattribute__"'
class Main:
# 描述符的关键就是将 实现了 __get__, __set__ 的类 的实例, 作为另外一个 类的属性
des = Des('onepis', 20)
m = Main()
d = Des('onepis', 20)
# d.xx 测试的是 __getattr__ 和 __getattribute__
print(d.name) # 打印存在的属性
print(d.b) # 打印不存在的属性
print(d.pis) # 我在类里面写了, pis 会抛出异常
# 抛出异常 之后, 会 去 __getattr__ 找
# 实例 上进行操作
print(m.des) # 访问 m 的 des 属性 则会 触发 Des 的 __get__ 方法
# class 上进行操作
Main.des
Main.des = 3
# 6. `描述符赋值如果是通过类的属性方式赋值` ,而不是类的实例属性方式赋值,描述器失效
__getattribute__
和__getattr__
用于实例访问属性使用,__get__
方法是只能其实例属于类(owner, 另一个类)属性的时候生效- 只要有
__getattribute__
,任何属性访问都是这个的返回值
,从第 3条开始, 以下都是在__getattribute__
不存在或者有 AttributeError 异常发生的情况下描述的 - 访问不存在的属性,
__getattr__
生效 - 访问存在的属性,如果是描述符,描述符生效
- 如果通过实例对描述符 进行赋值操作,又有 数据和非数据描述符 的区分,如果定义了
__set__
,那么此方法生效,并且仍然是原始的数据描述符号
,否则被赋值为新对象 描述符赋值如果是通过类的属性方式赋值
,而不是类的实例属性方式赋值,描述器失效- 针对描述符的说明: 描述符被
__getattribute__
调用的,如果重写了这个方法,将会阻止自动调用描述符
,数据描述符
总是覆盖了实例的__dict__
, 非数据描述符可能覆盖实例的__dict__
我使用 的版本是 3.6.8
__slot__
__slots__
允许我们显式声明数据成员(如属性)并拒绝创建 __dict__
和 __weakref__
(除非在 __slots__
或在父级中可用。)
节省的空间
__dict__
可能很重要。属性查找速度也可以显著提高。
object.__slots__
这个类变量可以被分配一个字符串、iterable或字符串序列,这些字符串的变量名由实例使用。
__slots__
为声明的变量保留空间,并阻止自动创建 __dict__
和 __weakref__
对于每个实例。
使用注意事项 __slots__
从类继承时 __slots__
, the __dict__
和 __weakref__
实例的属性将始终可访问。
没有__dict__
变量,不能为实例分配未在中列出的新变量 __slots__
定义。
尝试分配给未列出的变量名引发 AttributeError
.
如果需要动态分配新变量,则添加 ‘dict’ 到中的字符串序列 __slots__
声明。
没有 __weakref__
每个实例的变量,类定义 __slots__
不支持对其实例的弱引用。
如果需要弱引用支持,则添加 __weakref__
到中的字符串序列 __slots__
声明
__slots__
通过创建描述符在类级别实现 (实现描述符 )对于每个变量名。
因此,类属性不能用于设置由定义的实例变量的默认值。__slots__
否则,class
属性将覆盖
描述符分配。
A的作用 __slots__
声明不限于定义它的类。 __slots__
在父类中声明的在子类中可用。但是,子类将得到 __dict__
和 __weakref__
除非他们也定义 __slots__
(只应包含 额外的 插槽)。
如果一个类定义了一个同样在基类中定义的槽,那么由基类槽定义的实例变量是不可访问的(除非直接从基类中检索其描述符)。这使得程序的含义未定义。将来,可能会添加一个支票以防止出现这种情况。
非空的 __slots__
不适用于从“可变长度”内置类型派生的类,例如 int , bytes 和 tuple .
任何非字符串iterable都可以分配给 __slots__
. 也可以使用映射;
但是,在将来,可以为每个键对应的值赋予特殊含义。
__class__
只有当两个类具有相同的属性时,赋值才有效 slots .
可以使用具有多个时隙父类的多个继承,但只允许一个父类具有由时隙创建的属性(其他基必须具有空的时隙布局)-冲突引发 TypeError .
如果迭代器用于 __slots__
然后为迭代器的每个值创建一个描述符。然而 __slots__
属性将是空迭代器。
自定义类创建
每当一个类从另一个类继承时, __init_subclass__
在该类上调用。
这样,就可以编写更改子类行为的类。
这与 类修饰符 密切相关,但是类修饰符只影响它们应用到的特定类, __init_subclass__
仅适用于定义方法的类的未来子类。
classmethod object.__init_subclass__(cls)
只要包含类是子类,就调用此方法。
cls
是新的子类。
如果定义为普通实例方法,则此方法将隐式转换为类方法。
为新类提供的关键字参数将传递给父类 __init_subclass__
. 为了与其他类的兼容性,请使用 __init_subclass__
,应该取出所需的关键字参数,并将其他参数传递给基类,
如::
class Philosopher:
def __init_subclass__(cls, /, default_name, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
pass
默认实现 object.__init_subclass__
不执行任何操作,但如果使用任何参数调用它,则会引发错误。
注解 元类提示 metaclass 被其他类型的机器消耗,并且从未传递给 __init_subclass__
实施。实际的元类(而不是显式提示)可以访问为 type(cls)
.
3.6 新版功能.
命名空间
metaclass.__prepare__(name, bases, **kwds)
命名空间钩子
实例和子类检查
注意,这些方法是在class 的类型(元类)上查找的。
它们不能在实际类中定义为类方法。
这与对实例调用的特殊方法的查找是一致的,只有在这种情况下,实例本身是一个类。
class.__instancecheck__(self, instance)
用于重写 isinstance
class.__subclasscheck__(self, subclass)
用于重写 issubclass
classmethod object.__class_getitem__(cls, key)
这个方法是在类对象本身上查找的,当在类体中定义时,这个方法隐式地是一个类方法。
注意,此机制主要保留用于静态类型检查,不鼓励其他用法。 具体可以查看 pep560
List[int] # 这样 就是使用的 __class_getitem__
# List 是一个类
模拟调用 __call__
object.__call__(self[, args...])
模拟可调用对象
当实例作为函数“调用”时调用;如果定义了此方法, x(arg1, arg2, ...)
是 x.__call__(arg1, arg2, ...)
模拟 __len__
自定义自己的 len
object.__len__(self)
调用以实现内置函数 len() . 应返回对象的长度,一个整数 >= 0。另外,一个没有定义 __bool__()
方法和谁的 __len__()
方法返回零
在布尔上下文中被视为假。
CPython implementation detail: 在CPython中,长度要求最多为 sys.maxsize . 如果长度大于 sys.maxsize 一些功能(例如 len() 可以提高 OverflowError . 防止升高 OverflowError 通过真值测试,对象必须定义 __bool__()
方法。
模拟 估计长度
object.__length_hint__(self)
调用以实现 operator.length_hint()
. 应返回对象的估计长度(可能大于或小于实际长度)。长度必须为整数 >= 0个。返回值也可以是 NotImplemented ,其处理方式与 __length_hint__
方法根本不存在。这种方法纯粹是一种优化,不需要正确性。
模拟 self[key]
, 比如 list 和dict
object.__getitem__(self, key)
调用以实现对 self[key]
. 对于序列类型
,接受的键
应该是整数
和切片对象
。
注意负索引的特殊解释(如果类希望模拟序列类型)取决于 __getitem__()
方法。
object.__setitem__(self, key, value)
设置 self[key]
object.__delitem__(self, key)
删除 self[key]
object.__missing__(self, key)
被称为 dict \ __getitem__()
实施 self[key]
当关键字不在字典中时,用于dict
子类。
模拟迭代
object.__iter__(self)
当容器需要迭代器时,调用此方法。此方法应返回一个新的迭代器对象,该对象可以迭代容器中的所有对象。对于映射,它应该迭代容器的键。
模拟 内置的 reversed
object.__reversed__(self)
由 reversed() 实现反向迭代的内置功能。它应该返回一个新的迭代器对象,该对象以相反的顺序遍历容器中的所有对象。
如果__reversed__()
未提供方法, reversed()
内置将返回到使用序列协议 (__len__()
和 __getitem__()
)支持序列协议的对象应该只提供 __reversed__()
如果它们能够提供比 reversed()
模拟 成员测试 in 和 not in
成员资格测试操作员 (in 和 not in )通常通过容器实现为迭代。但是,容器对象可以为下面的特殊方法提供更有效的实现,这也不要求对象是可iterable的。
object.__contains__(self, item)
调用以实现成员资格测试运算符。如果 item 是在 self ,否则为false。对于映射对象,这应该考虑映射的键,而不是值或键项对。
模拟 上下文管理器 with
object.__enter__(self)
输入与此对象相关的运行时上下文。这个 with 语句将此方法的返回值绑定到 as 声明的条款(如果有)。
object.__exit__(self, exc_type, exc_value, traceback)
退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。
如果上下文在没有异常的情况下退出,则所有三个参数都将 None .
class F:
def __init__(self):
self.name = "onepis"
def __enter__(self):
"""__enter__
程序执行顺序
enter => with => exit
"""
print("进入 _enter_")
print(self.name)
# enter 里面return 的就是 with open(xxx) as fp:
# 这个里面的 fp
return self.name
def __exit__(self, exc_type, exc_val, exc_tb):
"""__exit__
在执行 enter 或者 with 语句执行过程中, 如果 出现错误 那么 会自动执行 __exit__ 函数
如果在 __exit__ 里面 return 了 那么 哪怕 报错 , 也会执行 __exit__ 中的 内容,
并且 _exit__ 就相当于 处理了 错误, 所以一般是不会进行 return 的
:param exc_type: 错误类型
:type exc_type: [type]
:param exc_val: 错误值
:type exc_val: [type]
:param exc_tb: 回溯对象 trace_back , 这3 个参数 在 没有 发生错误的时候, 都是None
:type exc_tb: [type]
"""
print("进入 _exit_")
self.name = "exit"
print(
f"exc_type {exc_type}\n exc_val {exc_val}\n exc_tb {exc_tb}\n "
)
print(self.name)
with F() as f:
print(f)
# 这个 f 就是 enter 里面 return 的东西
##############################
## 函数里面使用 自定义 with ##
##############################
from contextlib import contextmanager
class FooError:
def raise_error(self):
raise RuntimeError('this is a runtime error')
@contextmanager
def foo():
try:
print("in foo try")
yield FooError()
except RuntimeError as e:
print(e)
print("in foo except")
finally:
print("in foo finally")
with foo() as ff:
ff.raise_error()
print("in with")
模拟协作对象(协程)
一个 awaitable
对象通常实现 __await__()
方法。 Coroutine objects(协程对象) 从回来 async def
功能正在等待。
object.__await__(self)
必须返回iterator
.应用于实现 awaitable
对象。例如, asyncio.Future
实现此方法以与 await
表达式。
模拟 异步迭代器
object.__aiter__(self)
必须返回 异步迭代器 对象。
object.__anext__(self)
必须返回 可期待的 产生迭代器的下一个值。应该提高 StopAsyncIteration 迭代结束时出错。
模拟 异步 上下文管理器
安 异步上下文管理器 是一个 上下文管理器 能够在其 __aenter__ 和 __aexit__
方法。
异步上下文管理器可用于 async with 语句。
object.__aenter__(self)
语义上类似于 __enter__()
唯一的区别是它必须返回 可期待的 .
object.__aexit__(self, exc_type, exc_value, traceback)
语义上类似于__exit__()
唯一的区别是它必须返回 可期待的 .