特殊方法和特殊属性一览

特殊方法和特殊属性一览

在系列文章里面也写过了

官方的文档

官方的文档我放在 这两个链接里面了.

96_特殊方法(魔术方法)和运算符重载
93_特殊属性

特殊方法含义
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) .

除上述情况外,尝试执行操作会在未定义适当方法时
引发异常(通常 AttributeErrorTypeError

将特殊方法设置为 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集合成员的操作,包括 setfrozensetdict.__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 yhash(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 将被错误地标识为可hashisinstance(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)

  1. __getattribute____getattr__ 用于实例访问属性使用,__get__ 方法是只能其实例属于类(owner, 另一个类)属性的时候生效
  2. 只要有__getattribute__ ,任何属性访问都是这个的返回值
    ,从第 3条开始, 以下都是在__getattribute__ 不存在或者有 AttributeError 异常发生的情况下描述的
  3. 访问不存在的属性,__getattr__生效
  4. 访问存在的属性,如果是描述符,描述符生效(描述符, 请看后面)
  5. 如果通过实例对描述符 进行赋值操作,又有 数据和非数据描述符 的区分,如果定义了__set__ ,那么此方法生效,并且仍然是原始的 数据描述符号 ,否则被赋值为新对象
  6. 描述符赋值如果是通过类的属性方式赋值 ,而不是类的实例属性方式赋值,描述器失效
  7. 针对描述符的说明: 描述符被__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

如果 asuper 的实例 ,

然后是绑定 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. `描述符赋值如果是通过类的属性方式赋值` ,而不是类的实例属性方式赋值,描述器失效
  1. __getattribute____getattr__ 用于实例访问属性使用,__get__ 方法是只能其实例属于类(owner, 另一个类)属性的时候生效
  2. 只要有__getattribute__ ,任何属性访问都是这个的返回值
    ,从第 3条开始, 以下都是在__getattribute__ 不存在或者有 AttributeError 异常发生的情况下描述的
  3. 访问不存在的属性,__getattr__生效
  4. 访问存在的属性,如果是描述符,描述符生效
  5. 如果通过实例对描述符 进行赋值操作,又有 数据和非数据描述符 的区分,如果定义了__set__ ,那么此方法生效,并且仍然是原始的 数据描述符号 ,否则被赋值为新对象
  6. 描述符赋值如果是通过类的属性方式赋值 ,而不是类的实例属性方式赋值,描述器失效
  7. 针对描述符的说明: 描述符被__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__()唯一的区别是它必须返回 可期待的 .

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值