目录 | 上一节 (4.2 继承) | 下一节 (4.4 异常)

4.3 特殊方法

可以通过特殊方法(或者称为"魔术"方法(magic method))自定义 Python 行为的各部分。本节介绍特殊方法的思想。此外,还将讨论动态属性访问和绑定方法。

简介

类可以定义特殊方法。特殊方法对于 Python 解释器而言具有特殊的意义。特殊方法总是以双下划线 __ 开头和结尾,例如 __init__

  1. class Stock(object):
  2. def __init__(self):
  3. ...
  4. def __repr__(self):
  5. ...

虽然有很多特殊方法,但是我们只研究几个具体的例子。

字符串转换的特殊方法

对象有两种字符串表示形式。

  1. >>> from datetime import date
  2. >>> d = date(2012, 12, 21)
  3. >>> print(d)
  4. 2012-12-21
  5. >>> d
  6. datetime.date(2012, 12, 21)
  7. >>>

str() 函数用于创建格式良好的、可打印的输出:

  1. >>> str(d)
  2. '2012-12-21'
  3. >>>

repr() 函数用于创建详细的、面向程序员的输出。

  1. >>> repr(d)
  2. 'datetime.date(2012, 12, 21)'
  3. >>>

str()repr() 函数都是使用类中定义的特殊方法生成要显示的字符串。

  1. class Date(object):
  2. def __init__(self, year, month, day):
  3. self.year = year
  4. self.month = month
  5. self.day = day
  6. # Used with `str()`
  7. def __str__(self):
  8. return f'{self.year}-{self.month}-{self.day}'
  9. # Used with `repr()`
  10. def __repr__(self):
  11. return f'Date({self.year},{self.month},{self.day})'

注意:按照惯例,__repr__() 返回一个字符串,当该字符串被传递给 eval() 函数,将会重新创建底层对象(译注:eval(repr(obj)) == obj)。如果不行,则使用某种易于阅读的表现形式。

数学操作的特殊方法

数学运算符涉及的特殊方法如下:

  1. a + b a.__add__(b)
  2. a - b a.__sub__(b)
  3. a * b a.__mul__(b)
  4. a / b a.__truediv__(b)
  5. a // b a.__floordiv__(b)
  6. a % b a.__mod__(b)
  7. a << b a.__lshift__(b)
  8. a >> b a.__rshift__(b)
  9. a & b a.__and__(b)
  10. a | b a.__or__(b)
  11. a ^ b a.__xor__(b)
  12. a ** b a.__pow__(b)
  13. -a a.__neg__()
  14. ~a a.__invert__()
  15. abs(a) a.__abs__()

元素访问的特殊方法

这些是实现容器的特殊方法:

  1. len(x) x.__len__()
  2. x[a] x.__getitem__(a)
  3. x[a] = v x.__setitem__(a,v)
  4. del x[a] x.__delitem__(a)

你可以在类中使用这些特殊方法。

  1. class Sequence:
  2. def __len__(self):
  3. ...
  4. def __getitem__(self,a):
  5. ...
  6. def __setitem__(self,a,v):
  7. ...
  8. def __delitem__(self,a):
  9. ...

方法调用

调用方法有两个步骤。

​ 1、查找:. 运算符

​ 2、方法调用: () 运算符

  1. >>> s = Stock('GOOG',100,490.10)
  2. >>> c = s.cost # Lookup
  3. >>> c
  4. <bound method Stock.cost of <Stock object at 0x590d0>>
  5. >>> c() # Method call
  6. 49010.0
  7. >>>

绑定方法

尚未被函数调用运算符 () 调用的方法称为绑定方法( 译注:bound method,如果直译应该译作“绑定的方法”,但是,就像“类方法”一样,可以省略“的”这个字,译为“绑定方法”,绑定在这里不是动词,而应理解为形容词“绑定的”)。它对自己生成的实例进行操作:

  1. >>> s = Stock('GOOG', 100, 490.10)
  2. >>> s
  3. <Stock object at 0x590d0>
  4. >>> c = s.cost
  5. >>> c
  6. <bound method Stock.cost of <Stock object at 0x590d0>>
  7. >>> c()
  8. 49010.0
  9. >>>

如果使用绑定方法时有些大意,那么容易导致错误。示例:

  1. >>> s = Stock('GOOG', 100, 490.10)
  2. >>> print('Cost : %0.2f' % s.cost)
  3. Traceback (most recent call last):
  4. File "<stdin>", line 1, in <module>
  5. TypeError: float argument required
  6. >>>

或者:

  1. f = open(filename, 'w')
  2. ...
  3. f.close # Oops, Didn't do anything at all. `f` still open.

在这两种情形中,错误都是由忘记尾部括号引起的。例如:s.cost() or f.close()

属性访问

还有一种访问、操作和管理属性的替代方法。

  1. getattr(obj, 'name') # Same as obj.name
  2. setattr(obj, 'name', value) # Same as obj.name = value
  3. delattr(obj, 'name') # Same as del obj.name
  4. hasattr(obj, 'name') # Tests if attribute exists

示例:

  1. if hasattr(obj, 'x'):
  2. x = getattr(obj, 'x'):
  3. else:
  4. x = None

注意: getattr() 函数的默认参数非常有用。

  1. x = getattr(obj, 'x', None)

练习

练习 4.9:更好的输出

请修改 stock.py 文件中定义的 Stock 对象,以便 __repr__() 方法生成更有用的输出。示例:

  1. >>> goog = Stock('GOOG', 100, 490.1)
  2. >>> goog
  3. Stock('GOOG', 100, 490.1)
  4. >>>

修改完成后,请查看读取股票投资组合时会发生什么,以及生成什么样的结果。示例:

  1. >>> import report
  2. >>> portfolio = report.read_portfolio('Data/portfolio.csv')
  3. >>> portfolio
  4. ... see what the output is ...
  5. >>>

练习 4.10:使用 getattr() 的例子

getattr() 是读取属性的另一种机制。可以使用它编写极其灵活的代码。首先,请尝试以下示例:

  1. >>> import stock
  2. >>> s = stock.Stock('GOOG', 100, 490.1)
  3. >>> columns = ['name', 'shares']
  4. >>> for colname in columns:
  5. print(colname, '=', getattr(s, colname))
  6. name = GOOG
  7. shares = 100
  8. >>>

仔细观察会发现输出数据完全由 columns 变量中列出的属性名决定。

tableformat.py 文件中,使用该思想将其扩展为通用函数 print_table()print_table()打印一个表格,显示用户指定的任意对象的属性。与早期的 print_report() 函数一样,print_table() 方法还应接受一个 TableFormatter 实例来控制输出格式。它们的工作方式如下:

  1. >>> import report
  2. >>> portfolio = report.read_portfolio('Data/portfolio.csv')
  3. >>> from tableformat import create_formatter, print_table
  4. >>> formatter = create_formatter('txt')
  5. >>> print_table(portfolio, ['name','shares'], formatter)
  6. name shares
  7. ---------- ----------
  8. AA 100
  9. IBM 50
  10. CAT 150
  11. MSFT 200
  12. GE 95
  13. MSFT 50
  14. IBM 100
  15. >>> print_table(portfolio, ['name','shares','price'], formatter)
  16. name shares price
  17. ---------- ---------- ----------
  18. AA 100 32.2
  19. IBM 50 91.1
  20. CAT 150 83.44
  21. MSFT 200 51.23
  22. GE 95 40.37
  23. MSFT 50 65.1
  24. IBM 100 70.44
  25. >>>

目录 | 上一节 (4.2 继承) | 下一节 (4.4 异常)

注:完整翻译见 https://github.com/codists/practical-python-zh

翻译:《实用的Python编程》04_03_Special_methods的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_03_Error_checking

    目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...

  7. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  8. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  9. 翻译:《实用的Python编程》04_01_Class

    目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...

随机推荐

  1. Java进阶专题(二十五) 分布式锁原理与实现

    前言 ​ 现如今很多系统都会基于分布式或微服务思想完成对系统的架构设计.那么在这一个系统中,就会存在若干个微服务,而且服务间也会产生相互通信调用.那么既然产生了服务调用,就必然会存在服务调用延迟或失败 ...

  2. WPF 之命令(七)

    一.前言 ​ 事件的作用是发布和传播一些消息,消息送达接收者,事件的使命也就完成了,至于消息响应者如何处理发送来的消息并不做规定,每个接收者可以使用自己的行为来响应事件.即事件不具有约束力. ​ 命令 ...

  3. Dapr微服务应用开发系列1:环境配置

    题记:上篇Dapr系列文章简要介绍了Dapr,这篇来谈一下开发和运行环境配置 本机开发环境配置 安装Docker 为了方便进行Dapr开发,最好(其实不一定必须)首先在本机(开发机器)上安装Docke ...

  4. 网络之一次http请求的完整过程

    关于网络的知识平时可能真正用的比较少,但是有一些点还是需要总结的: 完成一次http请求要大致可以分为7个步骤: 一.TCP三次握手 第一次握手:建立连接.客户端发送连接请求报文段,将SYN位置为1, ...

  5. 1.配置gitblit

    作者 微信:tangy8080 电子邮箱:914661180@qq.com 更新时间:2019-06-21 14:38:43 星期五 欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程 ...

  6. LeetCode6 Z字形排列

    题目描述是从上到下,从左到右Z字形排列. 找规律.这种形式一般都是mod x 余数有规律.然后写的时候围绕x构造,而非判断,代码会简单一些. 设行数为r 先观察r=5的情况 发现第0行的字符原始ind ...

  7. μC/OS-III---I笔记4---软件定时器

    软件定时器是在硬件定时器的基础上开发的,通过将一个硬件定时器进行分频及管理就可以的到多个软件定时器.他和时间管理共同组成了系统的时间管理大部分的内容.系统一开始的系统初始化函数OSInit函数内调用了 ...

  8. ASP.Net MVP Framework had been dead !

    ASP.Net MVP Framework Project Description A project to get you started with creating and designing w ...

  9. SCSS 复用 class 样式

    SCSS 复用 class 样式 @mixin & @include 复用的变量名称,不可以是 .class 开头️ css-varibale-name .css-class-name Mix ...

  10. 读写 LED 作业 台灯的 频闪研究 2 评测&对比!

    0. 读写 LED 作业 台灯的 频闪研究 2 评测&对比! 评测&对比图:  1. 日光:(中午12点) 2. Philips: (天猫 15元 5w E27白) 3. FSL: ( ...