Python基础 -- Task11.掌握 魔法方法

Python 基础语法

1.构造和析构

构造方法

我们最为熟知的基本的魔法方法就是 init ,我们可以用它来指明一个对象初始化的行为。然而,当我们调用 x = SomeClass() 的时候, init 并不是第一个被调用的方法。事实上,第一个被调用的是 new ,这个 方法才真正地创建了实例。当这个对象的生命周期结束的时候, del 会被调用。让我们近一步理解这三个方法:
1:new(cls,[…)
new 是对象实例化时第一个调用的方法,它只取下 cls 参数,并把其他参数传给 init 。 new 很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。
2:init(self,[…])
类的初始化方法。它获取任何传给构造器的参数(比如我们调用 x = SomeClass(10, ‘foo’) , init 就会接到参数 10 和 ‘foo’ 。 init 在Python的类定义中用的最多。
类对象支持两种操作:属性引用和实例化。
属性引用使用和 Python 中所有的属性引用一样的标准语法:obj.name。

类有一个名为 init() 的特殊方法(构造方法),该方法在类实例化时会自动调用,像下面这样:

def init(self):
self.data = []

类定义了 init() 方法,类的实例化操作会自动调用 init() 方法。如下实例化类 MyClass,对应的 init() 方法就会被调用:

x = MyClass()

当然, init() 方法可以有参数,参数通过 init() 传递到类的实例化操作上。例如:
在这里插入图片描述
self代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
在这里插入图片描述
从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:
在这里插入图片描述
3:del(self)
new 和 init 是对象的构造器, del 是对象的销毁器。它并非实现了语句 del x (因此该语句不等同于 x.del())。而是定义了当对象被垃圾回收时的行为。 当对象需要在销毁时做一些处理的时候这个方法很有用,比如 socket 对象、文件对象。但是需要注意的是,当Python解释器退出但对象仍然存活的时候, del 并不会 执行。 所以养成一个手工清理的好习惯是很重要的,比如及时关闭连接。
在这里插入图片描述

C# 中的析构函数

类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
下面的实例说明了析构函数的概念:
在这里插入图片描述
当上面的代码被编译和执行时,它会产生下列结果:
在这里插入图片描述

2.操作符

使用Python魔法方法的一个巨大优势就是可以构建一个拥有Python内置类型行为的对象。这意味着你可以避免使用非标准的、丑陋的方式来表达简单的操作。
注:运用魔法方法的魔力,我们可以定义方法 eq。

1):比较操作符

1:Python包含了一系列的魔法方法,用于实现对象之间直接比较,而不需要采用方法调用。同样也可以重载Python默认的比较方法,改变它们的行为。
2:下面是这些方法的列表:
(1)cmp(self, other)
cmp 是所有比较魔法方法中最基础的一个,它实际上定义了所有比较操作符的行为(<,=,!=,等等),但是它可能不能按照你需要的方式工作(例如,判断一个实例和另一个实例是否相等采用一套标准,而与判断一个实例是否大于另一实例采用另一套)。 cmp 应该在 self < other 时返回一个负整数,在 self == other 时返回0,在 self > other 时返回正整数。最好只定义你所需要的比较形式,而不是一次定义全部。 如果你需要实现所有的比较形式,而且它们的判断标准类似,那么 cmp 是一个很好的方法,可以减少代码重复,让代码更简洁。
(2)eq`(self, other)
定义等于操作符(==)的行为。
(3)ne(self, other)
定义不等于操作符(!=)的行为。
(4)lt(self, other)
定义小于操作符(<)的行为。
(5)gt(self, other)
定义大于操作符(>)的行为。
(6)le(self, other)
定义小于等于操作符(<)的行为。
(7)ge(self, other)
定义大于等于操作符(>)的行为。
在这里插入图片描述

2):数值操作符

例:一元操作符,常见算数操作符,反射算数操作符(后面会涉及更多),增强赋值操作符,和类型转换操作符。

3):一元操作符

(1)pos(self)
实现取正操作,例如 +some_object。
(2)neg(self)
实现取负操作,例如 -some_object。
(3)abs(self)
实现内建绝对值函数 abs() 操作。
(4)invert(self)
实现取反操作符 ~。
(5)round(self, n)
实现内建函数 round() ,n 是近似小数点的位数。
(6)floor(self)
实现 math.floor() 函数,即向下取整。
(7)ceil(self)
实现 math.ceil() 函数,即向上取整。
(8)trunc(self)
实现 math.trunc() 函数,即距离零最近的整数。

4)常用算术操作符

(1)add(self, other)
实现加法操作。
(2)sub(self, other)
实现减法操作。
(3)mul(self, other)
实现乘法操作。
(4)floordiv(self, other)
实现使用 // 操作符的整数除法。
(5)div(self, other)
实现使用 / 操作符的除法。
(6)truediv(self, other)
实现 true 除法,这个函数只有使用 from future import division 时才有作用。
(7)mod(self, other)
实现 % 取余操作。
(8)divmod(self, other)
实现 divmod 内建函数。
(9)pow
实现 ** 操作符。
(10)lshift(self, other)
实现左移位运算符 << 。
(11)rshift(self, other)
实现右移位运算符 >> 。
(12)and(self, other)
实现按位与运算符 & 。
(13)or(self, other)
实现按位或运算符 | 。
(14)xor(self, other)
实现按位异或运算符 ^ 。

5)反射算术运算符

1:所有反射运算符魔法方法和它们的常见版本做的工作相同,只不过是处理交换连个操作数之后的情况。绝大多数情况下,反射运算和正常顺序产生的结果是相同的。
注:操作符左侧的对象(也就是上面的 other )一定不要定义(或者产生 NotImplemented 异常) 操作符的非反射版本。
2:例如
(1)radd(self, other)
实现反射加法操作。
(2)rsub(self, other)
实现反射减法操作。
(3)rmul(self, other)
实现反射乘法操作。
(4)rfloordiv(self, other)
实现使用 // 操作符的整数反射除法。
(5)rdiv(self, other)
实现使用 / 操作符的反射除法。
(6)rtruediv(self, other)
实现 true 反射除法,这个函数只有使用 from future import division 时才有作用。
(7)rmod(self, other)
实现 % 反射取余操作符。
(8)rdivmod(self, other)
实现调用 divmod(other, self) 时 divmod 内建函数的操作。
(9)rpow
实现 ** 反射操作符。
(10)rlshift(self, other)
实现反射左移位运算符 << 的作用。
(11)rshift(self, other)
实现反射右移位运算符 >> 的作用。
(12)rand(self, other)
实现反射按位与运算符 & 。
(13)ror(self, other)
实现反射按位或运算符 | 。
(14)rxor(self, other)
实现反射按位异或运算符 ^ 。

6)增强赋值运算符

1:下面是方法列表:
(1)iadd(self, other)
实现加法赋值操作。
(2)isub(self, other)
实现减法赋值操作。
(3)imul(self, other)
实现乘法赋值操作。
(4)ifloordiv(self, other)
实现使用 //= 操作符的整数除法赋值操作。
(5)idiv(self, other)
实现使用 /= 操作符的除法赋值操作。
(6)itruediv(self, other)
实现 true 除法赋值操作,这个函数只有使用 from future import division 时才有作用。
(7)imod(self, other)
实现 %= 取余赋值操作。
(8)ipow
实现 **= 操作。
(9)ilshift(self, other)
实现左移位赋值运算符 <<= 。
(10)irshift(self, other)
实现右移位赋值运算符 >>= 。
(11)iand(self, other)
实现按位与运算符 &= 。
(12)ior(self, other)
实现按位或赋值运算符 | 。
(13)ixor(self, other)
实现按位异或赋值运算符 ^= 。
在这里插入图片描述

7)类型转换操作符

(1)int(self)
实现到int的类型转换。
(2)long(self)
实现到long的类型转换。
(3)float(self)
实现到float的类型转换。
(4)complex(self)
实现到complex的类型转换。
(5)oct(self)
实现到八进制数的类型转换。
(6)hex(self)
实现到十六进制数的类型转换。
(7)index(self)
实现当对象用于切片表达式时到一个整数的类型转换。如果你定义了一个可能会用于切片操作的数值类型,你应该定义 index。
(8)trunc(self)
当调用 math.trunc(self) 时调用该方法, trunc 应该返回 self 截取到一个整数类型(通常是long类型)的值。
(9)coerce(self)
该方法用于实现混合模式算数运算,如果不能进行类型转换, coerce 应该返回 None 。反之,它应该返回一个二元组 self 和 other ,这两者均已被转换成相同的类型。

3.类的表示

在Python中有一些内建方法可以返回类的表示,相对应的,也有一系列魔法方法可以用来自定义在使用这些内建函数时类的行为。
(1)str(self)
定义对类的实例调用 str() 时的行为。
(2)repr(self)
定义对类的实例调用 repr() 时的行为。 str() 和 repr() 最主要的差别在于“目标用户”。 repr() 的作用是产生机器可读的输出(大部分情况下,其输出可以作为有效的Python代码),而 str() 则产生人类可读的输出。
(3)unicode(self)
定义对类的实例调用 unicode() 时的行为。 unicode() 和 str() 很像,只是它返回unicode字符串。注意,如果调用者试图调用 str() 而你的类只实现了 unicode() ,那么类将不能正常工作。所有你应该总是定义 str() ,以防有些人没有闲情雅致来使用unicode。
(4)format(self)
定义当类的实例用于新式字符串格式化时的行为,例如, “Hello, 0:abc!”.format(a) 会导致调用 a.format(“abc”) 。当定义你自己的数值类型或字符串类型时,你可能想提供某些特殊的格式化选项,这种情况下这个魔法方法会非常有用。
(5)hash(self)
定义对类的实例调用 hash() 时的行为。它必须返回一个整数,其结果会被用于字典中键的快速比较。同时注意一点,实现这个魔法方法通常也需要实现 eq ,并且遵守如下的规则: a == b 意味着 hash(a) == hash(b)。
(6)nonzero(self)
定义对类的实例调用 bool() 时的行为,根据你自己对类的设计,针对不同的实例,这个魔法方法应该相应地返回True或False。
(7)dir(self)
定义对类的实例调用 dir() 时的行为,这个方法应该向调用者返回一个属性列表。一般来说,没必要自己实现 dir 。但是如果你重定义了 getattr 或者 getattribute (下个部分会介绍),乃至使用动态生成的属性,以实现类的交互式使用,那么这个魔法方法是必不可少的。

4.访问控制

Python不是通过显式定义的字段和方法修改器,而是通过魔法方法实现了一系列的封装。
(1)getattr(self, name)
当用户试图访问一个根本不存在(或者暂时不存在)的属性时,你可以通过这个魔法方法来定义类的行为。这个可以用于捕捉错误的拼写并且给出指引,使用废弃属性时给出警告(如果你愿意,仍然可以计算并且返回该属性),以及灵活地处理AttributeError。只有当试图访问不存在的属性时它才会被调用,所以这不能算是一个真正的封装的办法。
(2)setattr(self, name, value)
和 getattr 不同, setattr 可以用于真正意义上的封装。它允许你自定义某个属性的赋值行为,不管这个属性存在与否,也就是说你可以对任意属性的任何变化都定义自己的规则。然后,一定要小心使用 setattr ,这个列表最后的例子中会有所展示。
(3)delattr(self, name)
这个魔法方法和 setattr 几乎相同,只不过它是用于处理删除属性时的行为。和 setattr_ 一样,使用它时也需要多加小心,防止产生无限递归(在 delattr 的实现中调用 del self.name 会导致无限递归)。
(3)getattribute(self, name)

getattribute` 看起来和上面那些方法很合得来,但是最好不要使用它。 getattribute 只能用于新式类。在最新版的Python中所有的类都是新式类,在老版Python中你可以通过继承 object 来创建新式类。 getattribute 允许你自定义属性被访问时的行为,它也同样可能遇到无限递归问题(通过调用基类的 getattribute 来避免)。 getattribute 基本上可以替代 getattr 。只有当它被实现,并且显式地被调用,或者产生 AttributeError 时它才被使用。 这个魔法方法可以被使用(毕竟,选择权在你自己),我不推荐你使用它,因为它的使用范围相对有限(通常我们想要在赋值时进行特殊操作,而不是取值时),而且实现这个方法很容易出现Bug。
在这里插入图片描述

5.自定义序列

1:既然讲到创建自己的序列类型,就不得不说一说协议了。协议类似某些语言中的接口,里面包含的是一些必须实现的方法。在Python中,协议完全是非正式的,也不需要显式的声明,事实上,它们更像是一种参考标准。
2:因为在Python中实现自定义容器类型需要用到一些协议。首先,不可变容器类型有如下协议:想实现一个不可变容器,你需要定义 len 和 getitem (后面会具体说明)。可变容器的协议除了上面提到的两个方法之外,还需要定义 setitem 和 delitem 。最后,如果你想让你的对象可以迭代,你需要定义 iter ,这个方法返回一个迭代器。迭代器必须遵守迭代器协议,需要定义 iter (返回它自己)和 next 方法。
3:容器背后的魔法方法
(1)len(self)
返回容器的长度,可变和不可变类型都需要实现。
(2)getitem(self, key)
定义对容器中某一项使用 self[key] 的方式进行读取操作时的行为。这也是可变和不可变容器类型都需要实现的一个方法。它应该在键的类型错误式产生 TypeError 异常,同时在没有与键值相匹配的内容时产生 KeyError 异常。
(3)setitem(self, key)
定义对容器中某一项使用 self[key] 的方式进行赋值操作时的行为。它是可变容器类型必须实现的一个方法,同样应该在合适的时候产生 KeyError 和 TypeError 异常。
(4)iter(self, key)
它应该返回当前容器的一个迭代器。迭代器以一连串内容的形式返回,最常见的是使用 iter() 函数调用,以及在类似 for x in container: 的循环中被调用。迭代器是他们自己的对象,需要定义 iter 方法并在其中返回自己。
(5)reversed(self)
定义了对容器使用 reversed() 内建函数时的行为。它应该返回一个反转之后的序列。当你的序列类是有序时,类似列表和元组,再实现这个方法,
(6)contains(self, item)
contains 定义了使用 in 和 not in 进行成员测试时类的行为。你可能好奇为什么这个方法不是序列协议的一部分,原因是,如果 contains 没有定义,Python就会迭代整个序列,如果找到了需要的一项就返回 True 。
(7)missing(self ,key)
missing 在字典的子类中使用,它定义了当试图访问一个字典中不存在的键时的行为(目前为止是指字典的实例,例如我有一个字典 d , “george” 不是字典中的一个键,当试图访问 d[“george’] 时就会调用 d.missing(“george”) )。

6.反射

你可以通过定义魔法方法来控制用于反射的内建函数 isinstance 和 issubclass 的行为。下面是对应的魔法方法:
(1)instancecheck(self, instance)
检查一个实例是否是你定义的类的一个实例(例如 isinstance(instance, class) )。
(2)subclasscheck(self, subclass)
检查一个类是否是你定义的类的子类(例如 issubclass(subclass, class) )。
在这里插入图片描述

7.可调用的对象

Python中一个特殊的魔法方法允许你自己类的对象表现得像是函数,然后你就可以“调用”它们,把它们传递到使用函数做参数的函数中,等等等等。这是另一个强大而且方便的特性,让使用Python编程变得更加幸福。
(1)call(self, [args…])
允许类的一个实例像函数那样被调用。本质上这代表了 x() 和 x.call() 是相同的。注意 call 可以有多个参数,这代表你可以像定义其他任何函数一样,定义 call ,喜欢用多少参数就用多少。
(2)call 在某些需要经常改变状态的类的实例中显得特别有用。“调用”这个实例来改变它的状态,是一种更加符合直觉,也更加优雅的方法。
在这里插入图片描述

8.上下文管理器

1:在Python 2.5中引入了一个全新的关键词,随之而来的是一种新的代码复用方法—— with 声明。上下文管理的概念在Python中并不是全新引入的(之前它作为标准库的一部分实现),直到PEP 343被接受,它才成为一种一级的语言结构。
2:上下文管理器的行为由下面两个魔法方法所定义:
(1)enter(self)
定义使用 with 声明创建的语句块最开始上下文管理器应该做些什么。注意 enter 的返回值会赋给 with 声明的目标,也就是 as 之后的东西。
(2)exit(self, exception_type, exception_value, traceback)
定义当 with 声明语句块执行完毕(或终止)时上下文管理器的行为。它可以用来处理异常,进行清理,或者做其他应该在语句块结束之后立刻执行的工作。如果语句块顺利执行, exception_type , exception_value 和 traceback 会是 None 。否则,你可以选择处理这个异常或者让用户来处理。如果你想处理异常,确保 exit 在完成工作之后返回 True 。如果你不想处理异常,那就让它发生吧。

9.创建描述符对象

描述符是一个类,当使用取值,赋值和删除 时它可以改变其他对象。描述符不是用来单独使用的,它们需要被一个拥有者类所包含。描述符可以用来创建面向对象数据库,以及创建某些属性之间互相依赖的类。描述符在表现具有不同单位的属性,或者需要计算的属性时显得特别有用(例如表现一个坐标系中的点的类,其中的距离原点的距离这种属性)。
(1)get(self, instance, owner)
定义当试图取出描述符的值时的行为。 instance 是拥有者类的实例, owner 是拥有者类本身。
(2)set(self, instance, owner)
定义当描述符的值改变时的行为。 instance 是拥有者类的实例, value 是要赋给描述符的值。
(3)delete(self, instance, owner)
定义当描述符的值被删除时的行为。 instance 是拥有者类的实例

10.拷贝

有些时候,特别是处理可变对象时,你可能想拷贝一个对象,改变这个对象而不影响原有的对象。这时就需要用到Python的 copy 模块了。然而(幸运的是),Python模块并不具有感知能力, 因此我们不用担心某天基于Linux的机器人崛起。但是我们的确需要告诉Python如何有效率地拷贝对象。
(1)copy(self)
定义对类的实例使用 copy.copy() 时的行为。 copy.copy() 返回一个对象的浅拷贝,这意味着拷贝出的实例是全新的,然而里面的数据全都是引用的。也就是说,对象本身是拷贝的,但是它的数据还是引用的(所以浅拷贝中的数据更改会影响原对象)。
(2)deepcopy(self, memodict=)
定义对类的实例使用 copy.deepcopy() 时的行为。 copy.deepcopy() 返回一个对象的深拷贝,这个对象和它的数据全都被拷贝了一份。 memodict 是一个先前拷贝对象的缓存,它优化了拷贝过程,而且可以防止拷贝递归数据结构时产生无限递归。当你想深拷贝一个单独的属性时,在那个属性上调用 copy.deepcopy() ,使用 memodict 作为第一个参数。

11.Pickling

Pickling是Python数据结构的序列化过程,当你想存储一个对象稍后再取出读取时,Pickling会显得十分有用。
1:Pickle不仅仅可以用于内建类型,任何遵守pickle协议的类都可以被pickle。Pickle协议有四个可选方法,可以让类自定义它们的行为(这和C语言扩展略有不同,那不在我们的讨论范围之内)。
(1)getinitargs(self)
如果你想让你的类在反pickle时调用 init ,你可以定义 getinitargs(self) ,它会返回一个参数元组,这个元组会传递给 init 。注意,这个方法只能用于旧式类。
(2)getnewargs(self)
对新式类来说,你可以通过这个方法改变类在反pickle时传递给 new 的参数。这个方法应该返回一个参数元组。
(3)getstate(self)
你可以自定义对象被pickle时被存储的状态,而不使用对象的 dict 属性。 这个状态在对象被反pickle时会被 setstate 使用。
(4)setstate(self)
当一个对象被反pickle时,如果定义了 setstate ,对象的状态会传递给这个魔法方法,而不是直接应用到对象的 dict 属性。这个魔法方法和 getstate 相互依存:当这两个方法都被定义时,你可以在Pickle时使用任何方法保存对象的任何状态。
(5)reduce(self)
当定义扩展类型时(也就是使用Python的C语言API实现的类型),如果你想pickle它们,你必须告诉Python如何pickle它们。 reduce 被定义之后,当对象被Pickle时就会被调用。它要么返回一个代表全局名称的字符串,Pyhton会查找它并pickle,要么返回一个元组。这个元组包含2到5个元素,其中包括:一个可调用的对象,用于重建对象时调用;一个参数元素,供那个可调用对象使用;被传递给 setstate 的状态(可选);一个产生被pickle的列表元素的迭代器(可选);一个产生被pickle的字典元素的迭代器(可选);

reduce_ex(self)
reduce_ex 的存在是为了兼容性。如果它被定义,在pickle时 reduce_ex 会代替 reduce 被调用。 reduce 也可以被定义,用于不支持 reduce_ex 的旧版pickle的API调用。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值