迄今为止我们已经学习了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)
有一些诸如__intit__和__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)
输出:
$ python more_lambda.py
[{'y': 1, 'x': 4}, {'y': 3, 'x': 2}]
它是如何工作的:
注意,list
对象的sort
函数有一个名为key
的参数决定了这个列表是怎样被排序的(通常情况下为升序或者降序)。在我们的例子中,我们想要有一个自己的排序规则,我们需要写一个比较函数,而不是使用def
定义一个只在这里使用一次的函数,因此我们使用lambda表达式创建一个新的函数。
列表解析用于从一个现有的列表派生出一个新的列表。 假设你有一个数字列表,你想让其中所有大于2的元素乘以2并组成一个新的列表。类似问题正是使用列表解析的理想场合。
例子 (保存为more_list_comprehension.py
):
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
),由此产生一个新列表。注意原始列表并不会被改变。
使用列表解析的好处在于,当我们使用循环遍历元素并将其存储到新列表时可以减少样板代码量。
这里有一种特殊的方法可以将函数的形参当做元组或字典,那就是分别使用*
和**
前缀。 当需要在函数内得到可变数量的实参时这个方法很有用。
>>> 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语句断言失败,则引发一个AssertError
。
>>> 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
应当慎重使用。多数时候用于捕获异常,处理问题或是向用户显示错误后随即终止程序。
装饰器是包装函数的一种快捷方式。如果你有很多函数使用了同样的一段代码,使用装饰器对函数进行包装会非常方便。例如,我创建了一个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
# 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.
它是如何工作的:
请参考:
请参考:
本章我们探讨了Python语言更多的特性。虽然我们仍然没有覆盖到Python语言的全部特性,但基本上已经可以应付在实践中的绝大多数情况。对于你即将创建的任何应用程序来说这就已经足够了。
接下来,我们来看看阅读完本书之后怎样继续学习Python。