转载:Python的神奇方法指南:使操作符在自定义类内工作

使用 Python 神奇方法的优势之一就是它提供了一种简单的方式能让对象的行为像内建类型。这意味着你可以避免用丑陋,反直觉和非标准方法执行基本运算。在某些语言中,通常会这样做:

if instance.equals(other_instance):
    # do something

你也应该在 Python 确实会这样做,但同时它会增加用户的疑惑以及不必要的冗长。不同的库可能会对相同的运算采用不同的命名,这使得用户比平常干了更多的事。依靠神奇方法的力量,你可以定义一个方法(比如 __eq__),然后带代替我们真实的意图:

if instance == other_instance:
    # do something

现在你看到的是神奇方法力量的一部分。绝大多数都允许我们定义为运算符本身的意义,当用在我们自己定义的类上就像它们是内建类型。

神奇方法——比较

Python 有一整套神奇方法被设计用来通过操作符实现对象间直观的比较,而非别扭的方法调用。它们同样提供了一套覆盖 Python 对象比较的默认行为(通过引用)。以下是这些方法的列表以及做法:

__cmp__(self, other)

__cmp__是神奇方法中最基础的一个。实际上它实现所有比较操作符行为(<,==,!=,等),但它有可能不按你想要的方法工作(例如,一个实例是否等于另一个这取决于比较的准则,以及一个实例是否大于其他的这也取决于其他的准则)。如果 self < other,那 __cmp__ 应当返回一个负整数;如果 self == other,则返回 0;如果 self > other,则返回正整数。它通常是最好的定义,而不需要你一次就全定义好它们,但当你需要用类似的准则进行所有的比较时,__cmp__ 会是一个很好的方式,帮你节省重复性和提高明确度。

__eq__(self, other)

定义了相等操作符,==的行为。

__ne__(self, other)

定义了不相等操作符,!= 的行为。

__lt__(self, other)

定义了小于操作符,< 的行为。

__gt__(self, other)

定义了大于操作符,> 的行为。

__le__(self, other)

定义了小于等于操作符,<=的行为。

__ge__(self, other)

定义了大于等于操作符,>= 的行为。

举一个例子,设想对单词进行类定义。我们可能希望能够按内部对 string 的默认比较行为,即字典序(通过字母)来比较单词,也希望能够基于某些其他的准则,像是长度或音节数。在本例中,我们通过单词长度排序,以下给出实现:

class Word(str):
    ‘‘‘单词类,比较定义是基于单词长度的‘‘‘

    def __new__(cls, word):
        # 注意,我们使用了__new__,这是因为str是一个不可变类型,
        # 所以我们必须更早地初始化它(在创建时)
        if ‘ ‘ in word:
            print "单词内含有空格,截断到第一部分"
            word = word[:word.index(‘ ‘)] # 在出现第一个空格之前全是字符了现在
        return str.__new__(cls, word)

    def __gt__(self, other):
        return len(self) > len(other)
    def __lt__(self, other):
        return len(self) < len(other)
    def __ge__(self, other):
        return len(self) >= len(other)
    def __le__(self, other):
        return len(self) <= len(other)

现在,我们可以创建 2 个单词(通过 Word(‘foo‘) 和 Word(‘bar‘))并基于它们的长度进行比较了。注意,我们没有定义 __eq__ 和 __ne__。这是因为这可能导致某些怪异的行为(特别是当比较 Word(‘foo‘) == Word(‘bar‘) 将会得到 True 的结果)。基于单词长度的相等比较会令人摸不清头脑,因此我们就沿用了str 本身的相等比较的实现。

现在可能是一个好时机来提醒你一下,你不必重载每一个比较相关的神奇方法来获得各种比较。标准库已经友好地为我们在模板 functools 中提供了一个装饰(decorator)类,定义了所有比较方法。你可以只重载 __eq__ 和一个其他的方法(比如 __gt__,__lt__,等)。这个特性只在 Python2.7(后?)适用,但当你有机会的话应该尝试一下,它会为你省下大量的时间和麻烦。你可以通过在你自己的重载方法在加上 @total_ordering 来使用。

神奇方法——数字

就像你可以通过重载比较操作符的途径来创建你自己的类实例,你同样可以重载数字操作符。系好你们的安全带,朋友们,还有很多呢。处于本文组织的需要,我会把数字的神奇方法分割成5块:一元操作符,常规算术操作符,反射算术操作符,增量赋值,类型转换。

一元操作符

一元运算和函数仅有一个操作数,比如负数,绝对值等

__pos__(self)

实现一元正数的行为(如:+some_object)

__neg__(self)

实现负数的行为(如: -some_object)

__abs__(self)

实现内建 abs() 函数的行为

__invert__(self)

实现用~操作符进行的取反行为。你可以参考 Wiki:bitwise operations 来解释这个运算符究竟会干什么

常规算术操作符

现在我们涵盖了基本的二元运算符:+,-,* 等等。其中大部分都是不言自明的。

__add__(self, other)

实现加法

__sub__(self, other)

实现减法

__mul__(self, other)

实现乘法

__floordiv__(self, other)

实现地板除法,使用 // 操作符

__div__(self, other)

实现传统除法,使用 / 操作符

__truediv__(self, other)

实现真正除法。注意,只有当你 from __future__ import division 时才会有效

__mod__(self, other)

实现求模,使用 % 操作符

__divmod__(self, other)

实现内建函数 divmod() 的行为

__pow__(self, other)

实现乘方,使用 ** 操作符

__lshift__(self, other)

实现左按位位移,使用 << 操作符

__rshift__(self, other)

实现右按位位移,使用 >> 操作符

__and__(self, other)

实现按位与,使用 & 操作符

__or__(self, other)

实现按位或,使用 | 操作符

__xor__(self, other)

实现按位异或,使用 ^ 操作符

反射算术操作符

你知道我会如何解释反射算术操作符?你们中的有些人或许会觉得它很大,很可怕,是国外的概念。但它实际上很简单,下面给一个例子:

some_object + other

这是“常规的”加法。而反射其实相当于一回事,除了操作数改变了改变下位置:

other + some_object

因此,所有这些神奇的方法会做同样的事等价于常规算术操作符,除了改变操作数的位置关系,比如第一个操作数和自身作为第二个。此外没有其他的操作方式。在大多数情况下,反射算术操作的结果等价于常规算术操作,所以你尽可以在刚重载完 __radd__就调用 __add__。干脆痛快:

__radd__(self, other)

实现反射加法

__rsub__(self, other)

实现反射减法

__rmul__(self, other)

实现反射乘法

__rfloordiv__(self, other)

实现反射地板除,用 // 操作符

__rdiv__(self, other)

实现传统除法,用 / 操作符

__rturediv__(self, other)

实现真实除法,注意,只有当你 from __future__ import division 时才会有效

__rmod__(self, other)

实现反射求模,用 % 操作符

__rdivmod__(self, other)

实现内置函数 divmod() 的长除行为,当调用 divmod(other,self) 时被调用

__rpow__(self, other)

实现反射乘方,用 ** 操作符

__rlshift__(self, other)

实现反射的左按位位移,使用 << 操作符

__rrshift__(self, other)

实现反射的右按位位移,使用 >> 操作符

__rand__(self, other)

实现反射的按位与,使用 & 操作符

__ror__(self, other)

实现反射的按位或,使用 | 操作符

__rxor__(self, other)

实现反射的按位异或,使用 ^ 操作符

增量赋值

Python 也有各种各样的神奇方法允许用户自定义增量赋值行为。你可能已经熟悉增量赋值,它结合了“常规的”操作符和赋值。如果你仍不明白我在说什么,下面有一个例子:

x = 5
x += 1 # 等价 x = x + 1

这些方法都不会有返回值,因为赋值在 Python 中不会有任何返回值。反而它们只是改变类的状态。列表如下:

__iadd__(self, other)

实现加法和赋值

__isub__(self, other)

实现减法和赋值

__imul__(self, other)

实现乘法和赋值

__ifloordiv__(self, other)

实现地板除和赋值,用 //= 操作符

__idiv__(self, other)

实现传统除法和赋值,用 /= 操作符

__iturediv__(self, other)

实现真实除法和赋值,注意,只有当你 from __future__ import division 时才会有效

__imod__(self, other)

实现求模和赋值,用 %= 操作符

__ipow__(self, other)

实现乘方和赋值,用 **= 操作符

__ilshift__(self, other)

实现左按位位移和赋值,使用 <<= 操作符

__irshift__(self, other)

实现右按位位移和赋值,使用 >>= 操作符

__iand__(self, other)

实现按位与和赋值,使用 &= 操作符

__ior__(self, other)

实现按位或和赋值,使用 |= 操作符

__ixor__(self, other)

实现按位异或和赋值,使用 ^= 操作符

类型转换的神奇方法

Python 也有一组神奇方法被设计用来实现内置类型转换函数的行为,如 float()

__int__(self)

实现到 int 的类型转换

__long__(self)

实现到 long 的类型转换

__float__(self)

实现到 float 的类型转换

__complex__(self)

实现到复数的类型转换

__oct__(self)

实现到 8 进制的类型转换

__hex__(self)

实现到 16 进制的类型转换

__index__(self)

实现一个当对象被切片到 int 的类型转换。如果你自定义了一个数值类型,考虑到它可能被切片,所以你应该重载__index__

__trunc__(self)

当 math.trunc(self) 被调用时调用。__trunc__ 应当返回一个整型的截断,(通常是 long)

__coerce__(self, other)

该方法用来实现混合模式的算术。如果类型转换不可能那 __coerce__ 应当返回 None。 否则,它应当返回一对包含 self 和 other(2 元组),且调整到具有相同的类型

时间: 2024-11-13 19:11:40

转载:Python的神奇方法指南:使操作符在自定义类内工作的相关文章

[转]JNI:使用RegisterNatives方法传递和使用Java自定义类

JNI:使用RegisterNatives方法传递和使用Java自定义类 2012-03-04 13:30:22|  分类: Java |  标签:jni:registernatives  |举报|字号 订阅 转载地址:http://techbook.blog.163.com/blog/static/304885102012235613945/ 除了使用传统方法实现JNI外,也可以使用RegisterNatives实现JNI.和传统方法相比,使用RegisterNatives的好处有三点:1.C

实现JNI的另一种方法:使用RegisterNatives方法传递和使用Java自定义类 (转)

原帖地址:http://blog.csdn.net/qiuxiaolong007/article/details/7860610 除了使用传统方法实现JNI外,也可以使用RegisterNatives实现JNI.和传统方法相比,使用RegisterNatives的好处有三点: 1.C++中函数命名自由,不必像javah自动生成的函数声明那样,拘泥特定的命名方式: 2.效率高.传统方式下,Java类call本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),

Python 的一些方法

有关 Python 内编写类的各种技巧和方法(构建和初始化.重载操作符.类描述.属性访问控制.自定义序列.反射机制.可调用对象.上下文管理.构建描述符对象.Pickling). 你可以把它当作一个教程,进阶,或者使用参考:我希望它能够成为一份针对 Python 方法的用户友好指南. 1.介绍 这份指南是几个月内最有价值的 Blog 投稿精华.它的主题是向大家讲述 Python 中的神奇方法. 何为神奇方法呢?它们是面向 Python 中的一切,是一些特殊的方法允许在自己的定义类中定义增加"神奇&

[转载]Python方法绑定——Unbound/Bound method object的一些梳理

本篇主要总结Python中绑定方法对象(Bound method object)和未绑定方法对象(Unboud method object)的区别和联系.主要目的是分清楚这两个极容易混淆的概念,顺便将Python的静态方法,类方法及实例方法加以说明 OK,下面开始 1. 一个方法引发的"血案" 类中所定义的函数称为方法举例: >>>class Foo(object): ... def foo(): ... print 'call foo' 然后令人困惑的地方就来了:当

python_面向对象魔法方法指南

原文: http://www.rafekettler.com/magicmethods.html 原作者: Rafe Kettler 翻译: hit9 原版(英文版) Repo: https://github.com/RafeKettler/magicmethods Contents (译)Python魔法方法指南 简介 构造方法 操作符 比较操作符 数值操作符 一元操作符 常见算数操作符 反射算数运算符 增强赋值运算符 类型转换操作符 类的表示 访问控制 自定义序列 预备知识 容器背后的魔法方

Python学习_12_方法和类定制

方法 在上一篇随笔中,简单提到了类的某些方法:__init__()等的调用,并简要说明方法和函数的区别. 方法是在类内部定义的函数,方法也是对象,所以方法是类的属性,这就是为什么说实例的方法存在于类定义中.而在ruby中,方法肯定是存在于类中的,实例的单件方法就存在于单件类中,python中并没有单件类,并且方法本质也是属性,所以实例的方法也可以存在于自身,而在调用方法时,同样遵循命名空间的查找顺序.但是方法和一般是属性任然存在区别: from types import MethodTypecl

Python 3程序开发指南(第2版 修订版)笔记

Python 3程序开发指南(第2版 修订版) 目录 1 过程型程序设计快速入门 2 数据类型 3 组合类型 4 控制结构与函数 5 模块 6 OOP 7 文件处理 8 高级程序设计技术 8.1 FP 9 调试.测试与Profiling 10 进程与线程 11 网络 12 数据库 13 正则表达式 14 分析简介 14.1 PyParsing 14.2 PLY 15 GUI 过程型程序设计快速入门 for in try except as 数据类型 sys.float_info.epsilon

MySQL 实现双向复制的方法指南

MySQL 实现双向复制的方法指南 投稿:hebedich 字体:[增加 减小] 类型:转载 这篇文章主要介绍了MySQL 实现双向复制的方法指南,本文包括:主机配置,从机配置,建立主-从复制,建立双向复制,需要的朋友可以参考下 简介 我知道有很多文章和指南介绍在互联网上实现主-从复制.在主-从复制中,主机影响从机.但从数据库中的任何更改不会影响主数据库,这篇文章将帮助你实现双向复制.(即,无论是主机还是从机的更改都将影响这两个服务器). 背景 你能参考Aadhar Joshi的这篇文章实现主从

atitit.api设计&#160;方法 指南 手册 v2 q929.docx

atitit.api设计 方法 指南 手册 v2 q929.docx atitit.api设计原则与方法 1. 归一化(锤子钉子理论)1 1.1. 链式方法2 1.2. 规则5:建立返回值类型2 1.3. 参数接收 JSON 对象2 1.4. 参数默认值2 1.5. 命名参数 support by map2 1.6.  处理类型 类型自动转换4 1.7.  处理 undefined null  empty5 1.8. .使用结构化语法5 1.9. 设置和获取操作,可以合二为一:方法越多,文档可能