到现在,我们已经学习涵盖了你将使用到 Python 的大部分方面。在本章中,我们将介绍一些其它的方面,来让我们对 Python 有更加全面的认识。
假如希望从一个函数中返回两个不同的值?用Python,只需要使用一个元组。
def get_error_details():
return (2, 'details')
errnum, errstr = get_error_details()
errnum
2
errstr
'details'
要注意到 a, b = <some expression> 的用法会将表达式的结果解释为,拥有两个值的一个元组。
这也意味着在Python中交换两个变量的最快方法是:
a=5; b=8
a, b
(5, 8)
a, b = b, a
a, b
(8, 5)
诸如 __init__ 和 __del__ 等一些方法对于类来说有特殊意义。
特殊方法用来模拟内置类型的某些行为。举个例子,如果你希望为你的类使用 x[key] 索引操作(就像你在列表与元组中使用的那样),那么你所需要做的只不过是实现__getitem__() 方法,然后你的工作就完成了。如果你试图理解它,就想想 ,Python 就是对list 类这样做的!
下面的表格列出了一些有用的特殊方法。如果你想了解所有的特殊方法,请参阅手册。
__init__(self, ...)
__del__(self)
__str__(self)
print 函数时,或 str() 被使用时就会被调用。__lt__(self, other)
__getitem__(self, key)
x[key] 索引操作时会被调用。__len__(self)
len() 函数时会被调用。我们已经见过每一个语句块都由其自身的缩进级别与其它部分进行区分,这里有一个小小的提示。如果你的语句块只包括单独的一句语句,那么你可以在同一行指定它,例如条件语句与循环语句。下面这个例子应该能比较清楚地解释:
flag = True
if flag: print('Yes')
···
Yes
单个语句是在原地立即使用的,它不会被看作一个单独的块。尽管,你可以通过这种方式来使你的程序更加小巧,但除非是为了检查错误,我强烈建议你避免使用这种快捷方法,主要是因为如果你不小心使用了一个“恰到好处”的缩进,它就很容易添加进额外的语句。
lambda 语句可以创建一个新的函数对象。从本质上说, lambda 需要一个参数,后跟一个表达式作为函数体,这一表达式执行的值将作为这个新函数的返回值。
(保存为 more_lambda.py )
points = [{'x': 2, 'y': 3},
{'x': 4, 'y': 1}]
points.sort(key=lambda i: i['y'])
print(points)
[{'x': 4, 'y': 1}, {'x': 2, 'y': 3}]
它是如何工作的?
要注意到一个
list的sort方法可以指定一个key参数,用以决定列表的排序方式(通常我们只知道升序与降序)。在本例中,我们希望进行一次自定义排序,因此我们需要编写一个函数,但是又不是为函数编写一个独立的 def 块,只在这一个地方使用,因此我们使用 Lambda 表达式来创建一个新函数。
列表推导(List Comprehension)用于从一份现有的列表中得到一份新列表。假如,现在你已经有了一份数字列表,你想得到一个相应的新列表,其中的数字在大于 2 的情况下将乘以2,列表推导就是这类情况的理想选择。
保存为 (more_list_comprehension.py)
listone = [2,3,4]
listtwo = [2*i for i in listone if i>2]
print(listtwo)
[6, 8]
在本例中,当满足了某些条件时(
if i > 2),我们进行指定的操作(2*i),以此来获得一份新的列表。要注意到原始列表依旧保持不变。使用列表推导的优点在于,当我们使用循环来处理列表中的每个元素并将其存储到新的列表中时时,它能减少样板(Boilerplate)代码的数量。
有一种特殊方法,即分别使用 * 或 ** 作为元组或字典的前缀,来使它们作为一个参数传给函数。当函数需要一个可变数量的实参时,这将非常实用。
>>> 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 语句就是这种情况下的理想选择。当语句断言失败时,将会抛出 AssertionError 。
>>> 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 语句,在大多数情况下,它捕获异常好用,也好过定位问题或向用户显示错误信息然后退出。
装饰器(Decorators)是应用包装函数的快捷方式,这有助于将某一功能与一些代码一遍又一遍地“包装”。举个例子,我们创建了一个 retry 装饰器,这样我可以将其运用到任何函数之中,如果在一次运行中抛出了任何错误,它就会尝试重新运行,直到最大次数 5 次,并且每次运行期间都会有一定的延迟。这对于你在对一台远程计算机进行网络调用的情况十分有用:
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
# 这将在第一次调用时抛出异常
# 在第二次运行时将正常工作(也就是重试)
if counter < 2:
raise ValueError(arg)
if __name__ == '__main__':
save_to_database("Some bad value")
Write to a database or make a network call or etc.
Traceback (most recent call last):
This will be automatically retried if exception is thrown.
File "E:/PyCharm/Python36/LearnPython_OOP/more_decorator.py", line 13, in wrapped_f
return f(*args, **kwargs)
File "E:/PyCharm/Python36/LearnPython_OOP/more_decorator.py", line 36, 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 的更多功能,不过我们还未涵盖到 Python 的所有功能。不过,在这一阶段,我们已经涉猎了大多数你将在实践中遇到的内容。这足以让你开始编写任何你所期望的程序。
接下来,我们将讨论如何进一步探索 Python。