# 更多
迄今为止我们已经学习了Python中的大多数常用知识。本章中我们会接触到更多的知识,使得我们更全面的掌握Python。
## 传递元组
你是否希望过从函数返回两个不同的值?做到这点使用元组即可。
```python
>>> def get_error_details():
... return (2, 'details')
...
>>> errnum, errstr = get_error_details()
>>> errnum
2
>>> errstr
'details'
```
注意,我们使用`a, b = <some expression>`这个表达式把元组的两个字段分别赋给两个变量。
这也意味着在Python中最快速的交换两个变量的值得方法是:
```python
>>> a = 5; b = 8
>>> a, b
(5, 8)
>>> a, b = b, a
>>> a, b
(8, 5)
```
## 特殊方法
有一些诸如__intit__和__del__的方法在类中拥有特殊的含义。
特殊方法用于模拟某些内建类型的行为。例如,你希望为你的类使用`x[key]`索引操作(就像在列表和元组中那样),那么你仅仅需要实现`__getitem__`方法就可以了。顺便提一句,Python正是这样实现`list`类的!
一些有用的特殊方法列在下表中。如果你想了解所有的特殊方法,详见[帮助文档](http://docs.python.org/3/reference/datamodel.html#special-method-names).
- `__init__(self, ...)`
- 在对象第一次被创建后,返回之前调用。
- `__del__(self)`
- 在对象被销毁前调用(我们无法预期这个函数什么时候被调用,因此尽量避免使用它)。
- `__str__(self)`
- 在使用`print`函数或`str()`时调用。
- `__lt__(self, other)`
- 在使用_小于_运算符时(<)调用。类似的其它运算符也存在对象的特殊方法(+, >等)。
- `__getitem__(self, key)`
- 当使用`x[key]`索引操作时调用。
- `__len__(self)`
- 当使用内建`len()`函数时调用,一般用于序列的对象。
## 单语句块
我们已经看到每个语句块都根据它的缩进级别将彼此区分开。不过有一个例外,如果某语句块只包含单条语句,你可以把它放到同一行,例如条件语句或循环语句。下面的例子清楚的说明了这点:
```python
>>> flag = True
>>> if flag: print('Yes')
...
Yes
```
注意上面的单条语句被放置到同一行而没有作为单独的块。虽然你利用这点可以让程序变的_更短_,但我强烈建议你避免使用这个快捷方式(除了错误检测),主要原因是使用适当的缩进可以更方便的添加额外的语句。
## Lambda表达式
`lambda`语句用于在运行时创建新的函数对象。通常情况下`lambda`语句带有一个参数,后面跟着一个简单的表达式作为函数体,把参数代入函数得到的返回值就是新的函数的返回值。
例如 (保存为`more_lambda.py`):
```python
points = [{'x': 2, 'y': 3},
{'x': 4, 'y': 1}]
points.sort(key=lambda i: i['y'])
print(points)
```
输出:
```
$ python more_lambda.py
[{'y': 1, 'x': 4}, {'y': 3, 'x': 2}]
```
**它是如何工作的:**
注意,`list`对象的`sort`函数有一个名为`key`的参数决定了这个列表是怎样被排序的(通常情况下为升序或者降序)。在我们的例子中,我们想要有一个自己的排序规则,我们需要写一个比较函数,而不是使用`def`定义一个只在这里使用一次的函数,因此我们使用lambda表达式创建一个新的函数。
## 列表解析
列表解析用于从一个现有的列表派生出一个新的列表。 假设你有一个数字列表,你想让其中所有大于2的元素乘以2并组成一个新的列表。类似问题正是使用列表解析的理想场合。
例子 (保存为`more_list_comprehension.py`):
```python
listone = [2, 3, 4]
listtwo = [2*i for i in listone if i > 2]
print(listtwo)
```
输出:
```
$ python more_list_comprehension.py
[6, 8]
```
**它是如何工作的:**
当某些条件满足时(`if i > 2`)我们执行某些操作(`2 * i`),由此产生一个新列表。注意原始列表并不会被改变。
使用列表解析的好处在于,当我们使用循环遍历元素并将其存储到新列表时可以减少样板代码量。
## 函数接收元组和列表
这里有一种特殊的方法可以将函数的形参当做元组或字典,那就是分别使用`*`和`**`前缀。
当需要在函数内得到可变数量的实参时这个方法很有用。
```python
>>> def powersum(power, *args):
... '''Return the sum of each argument raised to the specified power.'''
... total = 0
... for i in args:
... total += pow(i, power)
... return total
...
>>> powersum(2, 3, 4)
25
>>> powersum(2, 10)
100
```
因为`args`变量带有`*`前缀,因此所有额外的实参都会被当做一个元组存入`args`中并传给函数。
如果把这里的`*`换成`**`,则所有额外的形参都会被当做一个字典的键/值对。
## assert语句
`assert`用于断言一个表达式为真。例如,你需要确保正在使用的列表至少有一个元素,否则引发一个错误,这种情况很适合使用`assert`语句。
当assert语句断言失败,则引发一个`AssertError`。
```python
>>> mylist = ['item']
>>> assert len(mylist) >= 1
>>> mylist.pop()
'item'
>>> assert len(mylist) >= 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
```
`assert`应当慎重使用。多数时候用于捕获异常,处理问题或是向用户显示错误后随即终止程序。
## 装饰器(decorator)
装饰器是包装函数的一种快捷方式。如果你有很多函数使用了同样的一段代码,使用装饰器对函数进行包装会非常方便。例如,我创建了一个`retry`装饰器,可以用在任何函数中,当触发任何异常的时候,他会让这个函数不断重复执行,每次执行之间有一个固定的间隔,最多执行5次。这种设计在需要通过网络进行远程调用的时候非常有用:
```python
from time import sleep
from functools import wraps
import logging
logging.basicConfig()
log = logging.getLogger("retry")
def retry(f):
@wraps(f)
def wrapped_f(*args, **kwargs):
MAX_ATTEMPTS = 5
for attempt in range(1, MAX_ATTEMPTS + 1):
try:
return f(*args, **kwargs)
except:
log.exception("Attempt %s/%s failed : %s",
attempt,
MAX_ATTEMPTS,
(args, kwargs))
sleep(10 * attempt)
log.critical("All %s attempts failed : %s",
MAX_ATTEMPTS,
(args, kwargs))
return wrapped_f
counter = 0
@retry
def save_to_database(arg):
print("Write to a database or make a network call or etc.")
print("This will be automatically retried if exception is thrown.")
global counter
counter += 1
# This will throw an exception in the first call
# And will work fine in the second call (i.e. a retry)
if counter < 2:
raise ValueError(arg)
if __name__ == '__main__':
save_to_database("Some bad value")
```
输出:
```
$ python more_decorator.py
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {})
Traceback (most recent call last):
File "more_decorator.py", line 14, in wrapped_f
return f(*args, **kwargs)
File "more_decorator.py", line 39, in save_to_database
raise ValueError(arg)
ValueError: Some bad value
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
```
**它是如何工作的:**
请参考:
- http://www.ibm.com/developerworks/linux/library/l-cpdecor.html
- http://toumorokoshi.github.io/dry-principles-through-python-decorators.html
## Python 2 与Python 3的区别
请参考:
- ["Six" library](http://pythonhosted.org/six/)
- [Porting to Python 3 Redux by Armin](http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/)
- [Python 3 experience by PyDanny](http://pydanny.com/experiences-with-django-python3.html)
- [Official Django Guide to Porting to Python 3](https://docs.djangoproject.com/en/dev/topics/python3/)
- [Discussion on What are the advantages to python 3.x?](http://www.reddit.com/r/Python/comments/22ovb3/what_are_the_advantages_to_python_3x/)
## Summary
本章我们探讨了Python语言更多的特性。虽然我们仍然没有覆盖到Python语言的全部特性,但基本上已经可以应付在实践中的绝大多数情况。对于你即将创建的任何应用程序来说这就已经足够了。
接下来,我们来看看阅读完本书之后怎样继续学习Python。
--------------------------------------------------
### 继续阅读[继续学习](what_next.md)
- 开始学习
- 搭建Python开发环境
- 简明Python教程
- 致敬
- 前言
- 关于Python
- 安装
- 第一步
- 基础
- 运算符和表达式
- 控制流
- 函数
- 模块
- 数据结构
- 实战案例
- 面向对象编程
- 输入与输出
- 异常处理
- 标准库
- 更多
- 继续学习
- 附录:免费/自由和开放源码软件
- 附录: 关于
- 附录: 版本历史
- 附录: 翻译
- 附录: 参与翻译工作
- 反馈
- Django Step Sy Step
- 第一讲 从简单到复杂
- 第二讲 做加法的例子
- 第三讲 使用Template
- 第四讲 生成csv格式文件
- 第五讲 session示例
- 第六讲 wiki的例子
- 第七讲 通讯录的例子
- 第八讲 文件导入和导出
- 第九讲 通讯录的美化
- 第十讲 扩展django的模板
- 第十一讲 用户管理
- 第十二讲 搜索和部署
- 第十三讲 Ajax的实现(一)
- 第十四讲 Ajax的实现(二)
- 第十五讲 i18n的一个简单实现
- 第十六讲 自定义Calendar Tag
- 第十七讲 View,Template和Tag
- Django开发实战
- Python开发规范
- Django项目的gitignore
- 怎样配置开发环境的settings
- 如何使用Django和Vue.js构建项目
- 使用WebSocket开发网页聊天室
- 怎样使Django Admin显示中文
- 怎样使Model在Admin界面中显示中文
- 使用Django Admin怎样上传并显示图片
- 解决Django模板和Vue指令花括号冲突的问题
- 使用Django和Vue开发微信公众号
- 使用Django和Vue调用微信JSSDK开发微信支付