10 分钟
Python高级编程阅读笔记(一)
一、装饰器
1、理解装饰器
以下为定义装饰器函数
def decorated_by(func):
"""这就是一个装饰器函数"""
func.__doc__ += '\n通过decorated_by装饰进行装饰。'
return func
def add(a, b):
"""返回a+b的值"""
return a+b
add = decorated_by(add)
help(add)Help on function add in module __main__:
add(a, b)
返回a+b的值
通过decorated_by装饰进行装饰。2、python中的语法
(1)定义
就是普通的python函数或者对象的方法;函数的第一个参数、对象的方法的第二个参数要当做函数类型来处理
(2)使用
@decorated_by
def add1(a, b):
"""返回a+b的值"""
return a+b
help(add1)Help on function add1 in module __main__:
add1(a, b)
返回a+b的值
通过decorated_by装饰进行装饰。(3)多个装饰器的调用顺序
自低而上
def alse_decorated_by(func):
"""这就也是一个装饰器函数"""
func.__doc__ += '\n通过alse_decorated_by装饰进行装饰。'
return func
@alse_decorated_by
@decorated_by
def add2(a, b):
"""返回a+b的值"""
return a+b
help(add2)Help on function add2 in module __main__:
add2(a, b)
返回a+b的值
通过decorated_by装饰进行装饰。
通过alse_decorated_by装饰进行装饰。3、装饰器的用途
- python创建类方法
@classmethod或@staticmethod - 单元测试,
@mock.patch - web框架登录验证Django的
@login_required - web框架URI与函数的映射Flask的
@app.route - 表示是否为异步Celery的
@task
4、装饰器的一些例子
(1)一个函数注册表
registry = []
def register(decorated):
registry.append(decorated)
return decorated
@register
def foo():
return 3
@register
def bar():
return 5
answers = []
for func in registry:
answers.append(func())
print(answers)[3, 5]实际应用测试
class Registry(object):
def __init__(self):
self._functions = []
def register(self, decorated):
self._functions.append(decorated)
return decorated
def run_all(self, *args, **kwargs):
return_values = []
for func in self._functions:
return_values.append(func(*args, **kwargs))
return return_values
a = Registry()
b = Registry()
@a.register
def foo(x=3):
return x
@b.register
def bar(x=5):
return x
@a.register
@b.register
def baz(x=7):
return x
a.run_all() # [3, 7]
b.run_all() # [5, 7]
a.run_all(x=4) # [4, 4][4, 4](2)类型检查器
def requires_ints(decorated):
def inner(*args, **kwargs):
kwarg_values = [i for i in kwargs.values()]
for arg in list(args) + kwarg_values:
if not isinstance(arg, int):
raise TypeError('%s only accepts integers as arguments.' %
decorated.__name__)
# 执行被装饰的函数
return decorated(*args, **kwargs)
return inner
@requires_ints
def foo(x, y):
"""Return the sum of x and y."""
return x + y
foo(1,2)
help(foo)Help on function inner in module __main__:
inner(*args, **kwargs)此时出现了一个问题,help查不到该函数的文档,使用@functools.wraps(decorated)解决
import functools
def requires_ints(decorated):
@functools.wraps(decorated)
def inner(*args, **kwargs):
kwarg_values = [i for i in kwargs.values()]
for arg in list(args) + kwarg_values:
if not isinstance(arg, int):
raise TypeError('%s only accepts integers as arguments.' %
decorated.__name__)
# 执行被装饰的函数
return decorated(*args, **kwargs)
return inner
@requires_ints
def foo(x, y):
"""Return the sum of x and y."""
return x + y
help(foo)Help on function foo in module __main__:
foo(x, y)
Return the sum of x and y.(3)用户验证
检验函数的第一个对象是否是指定类的实例(用于类型检测)
class User(object):
"""A representation of a user in our application."""
def __init__(self, username, email):
self.username = username
self.email = email
class AnonymousUser(User):
"""匿名用户类"""
def __init__(self):
self.username = None
self.email = None
#def __nonzero__(self): #python2
def __bool__(self):
return False
import functools
def requires_user(func):
@functools.wraps(func)
def inner(user, *args, **kwargs):
"""Verify that the user is truthy; if so, run the decorated method,
and if not, raise ValueError.
"""
# Ensure that user is truthy, and of the correct type.
# The "truthy"check will fail on anonymous users, since the
# AnonymousUser subclass has a ‘__nonzero__‘ method that
# returns False.
if user and isinstance(user, User):
return func(user, *args, **kwargs)
else:
raise ValueError('A valid user is required to run this.')
return inner
@requires_user
def login(u):
passlogin(User('name','email'))login(AnonymousUser())---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-12-83a4ec01f1d0> in <module>()
----> 1 login(AnonymousUser())
<ipython-input-10-05cf3cf34ebe> in inner(user, *args, **kwargs)
28 return func(user, *args, **kwargs)
29 else:
---> 30 raise ValueError('A valid user is required to run this.')
31
32 return inner
ValueError: A valid user is required to run this.login(1)---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-13-f3ac46340d07> in <module>()
----> 1 login(1)
<ipython-input-10-05cf3cf34ebe> in inner(user, *args, **kwargs)
28 return func(user, *args, **kwargs)
29 else:
---> 30 raise ValueError('A valid user is required to run this.')
31
32 return inner
ValueError: A valid user is required to run this.(4)Json格式化输出
import functools
import json
def json_output(decorated):
"""运行装饰函数,序列化该函数的结果
到JSON,并返回JSON字符串。
"""
@functools.wraps(decorated)
def inner(*args, **kwargs):
result = decorated(*args, **kwargs)
return json.dumps(result)
return inner
@json_output
def do_nothing():
return {'status': 'done'}
do_nothing()'{"status": "done"}'同时捕获特定异常并序列化
import functools
import json
class JSONOutputError(Exception):
def __init__(self, message):
self._message = message
def __str__(self):
return self._message
def json_output(decorated):
"""Run the decorated function, serialize the result of that function
to JSON, and return the JSON string.
"""
@functools.wraps(decorated)
def inner(*args, **kwargs):
try:
result = decorated(*args, **kwargs)
except JSONOutputError as ex:
result = {
'status': 'error',
'message': str(ex),
}
return json.dumps(result)
return inner
@json_output
def error():
raise JSONOutputError('This function is erratic.')
error()'{"status": "error", "message": "This function is erratic."}'(5)日志管理
将函数执行时间记录到日志
import functools
import logging
import time
def logged(method):
"""Cause the decorated method to be run and its results logged, along
with some other diagnostic information.
"""
@functools.wraps(method)
def inner(*args, **kwargs):
# Record our start time.
start = time.time()
# Run the decorated method.
return_value = method(*args, **kwargs)
# Record our completion time, and calculate the delta.
end = time.time()
delta = end - start
# Log the method call and the result.
logger = logging.getLogger('decorator.logged')
logger.warn('Called method %s at %.02f; execution time %.02f '
'seconds; result %r.' %
(method.__name__, start, delta, return_value))
# Return the method's original return value.
return return_value
return inner
@logged
def sleep_and_return(return_value):
time.sleep(2)
return return_value
sleep_and_return(1)/root/anaconda3/lib/python3.6/site-packages/ipykernel_launcher.py:22: DeprecationWarning: The 'warn' method is deprecated, use 'warning' instead
Called method sleep_and_return at 1506232994.18; execution time 2.00 seconds; result 1.
15、装饰器参数
import functools
import json
class JSONOutputError(Exception):
def __init__(self, message):
self._message = message
def __str__(self):
return self._message
def json_output(indent=None, sort_keys=False):
"""Run the decorated function, serialize the result of that function
to JSON, and return the JSON string.
"""
def actual_decorator(decorated):
@functools.wraps(decorated)
def inner(*args, **kwargs):
try:
result = decorated(*args, **kwargs)
except JSONOutputError as ex:
result = {
'status': 'error',
'message': str(ex),
}
return json.dumps(result, indent=indent, sort_keys=sort_keys)
return inner
return actual_decorator@json_output(indent=4)
def do_nothing():
return {'staus':'done'}
do_nothing()'{\n "staus": "done"\n}'@json_output()
def do_nothing1():
return {'staus':'done'}
do_nothing1()'{"staus": "done"}'@json_output
def do_nothing2():
return {'staus':'done'}
do_nothing2()---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-20-6ca3e34af532> in <module>()
3 return {'staus':'done'}
4
----> 5 do_nothing2()
TypeError: actual_decorator() missing 1 required positional argument: 'decorated'(1)带参数(包括空参数)装饰器的执行原理
- 首先执行装饰器函数,并获得返回值
decorator - 执行
decorator(函数)获得返回值target - 执行
target(参数)
而不带参数的装饰器仅执行后两步,所以导致@json_output报错
执行流程模拟:
# 对以下代码模拟
# @json_output(indent=4)
# def do_nothing():
# return {'staus':'done'}
# do_nothing()
def do_nothing():
return {'staus':'done'}
decorator = json_output(indent=4)
do_nothing = decorator(do_nothing)
do_nothing()'{\n "staus": "done"\n}'# 对以下代码模拟
#@json_output
#def do_nothing2():
# return {'staus':'done'}
#do_nothing2()
def do_nothing2():
return {'staus':'done'}
do_nothing2 = json_output(do_nothing2)
do_nothing2()---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-22-c041282b9566> in <module>()
9
10 do_nothing2 = json_output(do_nothing2)
---> 11 do_nothing2()
TypeError: actual_decorator() missing 1 required positional argument: 'decorated'(2)修正,使代码可以向前兼容
import functools
import json
class JSONOutputError(Exception):
def __init__(self, message):
self._message = message
def __str__(self):
return self._message
def json_output(decorated_=None, indent=None, sort_keys=False):
"""Run the decorated function, serialize the result of that function
to JSON, and return the JSON string.
"""
# Did we get both a decorated method and keyword arguments?
# That should not happen.
if decorated_ and (indent or sort_keys):
raise RuntimeError('Unexpected arguments.')
# Define the actual decorator function.
def actual_decorator(func):
@functools.wraps(func)
def inner(*args, **kwargs):
try:
result = func(*args, **kwargs)
except JSONOutputError as ex:
result = {
'status': 'error',
'message': str(ex),
}
return json.dumps(result, indent=indent, sort_keys=sort_keys)
return inner
# Return either the actual decorator, or the result of applying
# the actual decorator, depending on what arguments we got.
if decorated_:
return actual_decorator(decorated_)
else:
return actual_decorator@json_output(indent=4)
def do_nothing():
return {'staus':'done'}
do_nothing()'{\n "staus": "done"\n}'@json_output()
def do_nothing1():
return {'staus':'done'}
do_nothing1()'{"staus": "done"}'@json_output
def do_nothing2():
return {'staus':'done'}
do_nothing2()'{"staus": "done"}'6、装饰类
添加对象的创建时间并添加排序的支持
import functools
import time
def sortable_by_creation_time(cls):
"""给定一个类,增加类以使其实例
可通过它们被实例化的时间戳排序
"""
original_init = cls.__init__
@functools.wraps(original_init)
def new_init(self, *args, **kwargs):
original_init(self, *args, **kwargs)
self._created = time.time()
cls.__init__ = new_init
cls.__lt__ = lambda self, other: self._created < other._created
cls.__gt__ = lambda self, other: self._created > other._created
return cls
@sortable_by_creation_time
class Sortable(object):
def __init__(self, identifier):
self.identifier = identifier
def __repr__(self):
return self.identifier
first = Sortable('first')
second = Sortable('second')
third = Sortable('third')
sortables = [second, first, third]
sorted(sortables)[first, second, third]6、类型转换
装饰器装饰一个函数后,返回一个类实例,添加很多样板代码 例如一个简单的任务执行器
class Task(object):
"""一个task基类,拥有一个运行任务的run方法"""
def run(self, *args, **kwargs):
raise NotImplementedError('Subclasses must implement `run`.')
def identify(self):
return 'I am a task.'
def task(decorated):
"""返回一个运行decorated方法的Task派生类"""
class TaskSubclass(Task):
def run(self, *args, **kwargs):
return decorated(*args, **kwargs)
return TaskSubclass
@task
def foo():
return 2 + 2
f = foo()
f.run()
f.identify()'I am a task.'此时直接执行foo(),不会只执行foo,而是返回一个task派生类的实例,这使编程变得很复杂,修改如下
class Task(object):
"""一个task基类,拥有一个运行任务的run方法"""
def __call__(self, *args, **kwargs):
return self.run(*args, **kwargs)
def run(self, *args, **kwargs):
raise NotImplementedError('Subclasses must implement `run`.')
def identify(self):
return 'I am a task.'
def task(decorated):
"""返回一个运行decorated方法的Task派生类"""
class TaskSubclass(Task):
def run(self, *args, **kwargs):
return decorated(*args, **kwargs)
return TaskSubclass()
@task
def foo():
return 2 + 2
foo()48、装饰器总结
优点:
- 带来显示的代码可充用性
- 提供了一种完美的方式使用样板代码
问题:
- 模糊了函数被封装于另一个函数的事实
- 不便于调试
- 执行效率的下降
二、上下文管理器
1、理解上下文管理器
上下文管理器是一个包装任意代码块的对象。上下文管理器保证进入上下文管理器时,每次代码执行的一致性;当退出时,相关资源会被回收
2、上下文管理器语法
(1)with关键字
with open('/path/to/filename', 'r') as my_file:
contents = my_file.read()as子句是可选的
当调用open方法时,返回一个对象,该对象包含两个特殊方法__enter__和__exit__,首先执行__enter__并将其的返回值赋给as后的关键字,在执行完with代码块内的内容后,将会执行__exit__方法
(2)enter和exit方法
__enter__方法除了self不接受任何参数__exit__方法除了self外,还接受三个参数:异常类型、异常实例、回溯- 实现异常传播,该方法返回一个False
- 中止异常,返回一个True
- 抛出其他异常,在方法中直接抛出
例子
class ContextManager(object):
def __init__(self):
self.entered = False
print("__init__")
def __enter__(self):
self.entered = True
print("__enter__")
return self
def __exit__(self, exc_type, exc_instance, traceback):
self.entered = Falsecm = ContextManager()
cm.entered__init__
Falsewith cm as cm:
print(cm.entered)__enter__
Truewith ContextManager() as cm:
print(cm.entered)__init__
__enter__
True3、何时编写上下文管理器
(1)资源清理
一个数据库连接上下文管理器
import psycopg2
class DBConnection(object):
def __init__(self, dbname=None, user=None,
password=None, host='localhost'):
self.host = host
self.dbname = dbname
self.user = user
self.password = password
def __enter__(self):
self.connection = psycopg2.connect(
dbname=self.dbname,
host=self.host,
user=self.user,
password=self.password,
)
return self.connection.cursor()
def __exit__(self, exc_type, exc_instance, traceback):
self.connection.close()
with DBConnection(user='luke', dbname='foo') as db:
db.execute('SELECT 1 + 1')
db.fetchall()(2)避免重复
传播异常
class BubbleExceptions(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_instance, traceback):
if exc_instance:
print('Bubbling up exception: %s.' % exc_instance)
return False
with BubbleExceptions():
5+5
with BubbleExceptions():
5/0Bubbling up exception: division by zero.
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-9-abae51d6cf71> in <module>()
11
12 with BubbleExceptions():
---> 13 5/0
ZeroDivisionError: division by zero中止异常
class BubbleExceptions(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_instance, traceback):
if exc_instance:
print('Bubbling up exception: %s.' % exc_instance)
return True
with BubbleExceptions():
5+5
with BubbleExceptions():
5/0Bubbling up exception: division by zero.处理特定异常
略
4、更简单是实现上下文管理器
使用上下文管理器生成器
import contextlib
@contextlib.contextmanager
def acceptable_error_codes(*codes):
try:
yield
except ShellException as exc_instance:
# If this error code is not in the list of acceptable error
# codes, re-raise the exception.
if exc_instance.code not in codes:
raise
# This was an acceptable error; no need to do anything.
pass三、生成器
1、理解生成器
生成器是一个函数,他并不执行并返回一个值,而是按照顺序返回一个或者多个值。
2、生成器语法
(1)yield关键字
def fibonacci():
yield 1
yield 1
yield 2
yield 3
yield 5
yield 8
for i in fibonacci():
print(i)1
1
2
3
5
8def fibonacci():
yield 1
yield 1
yield 2
yield 3
yield 5
yield 8
for i in fibonacci():
print(i)1
1
2
3
5
8(2)next函数
def fibonacci():
numbers = []
while True:
if len(numbers) < 2:
numbers.append(1)
else:
numbers.append(sum(numbers))
numbers.pop(0)
yield numbers[-1]
gen = fibonacci()
gen<generator object fibonacci at 0x7f9a440c96d0>next(gen)
next(gen)2(3)执行分析
- 调用带有
yield的函数时,解释器会自动生成一个生成器对象 - 首次
next函数时,从头开始执行生成器函数到yield,将其后面的值返回,并暂停(类似于挂起) - 其他时候,从上次的yield语句的下一条语句开始执行,再到yield后返回并暂停
(4)生成器退出
- python2 使用
raise StopIteraton异常中止 python3 使用
return同时兼容py2的写法def my_generator(): yield 1 yield 2 return yield 3 [i for i in my_generator()][1, 2]
gen = my_generator() next(gen) next(gen) next(gen)StopIteration Traceback (most recent call last)
in () 2 next(gen) 3 next(gen) —-> 4 next(gen) StopIteration:
3、生成器间交互
给生成器传递参数,使用gen.send()
例子:按顺序返回完全平方数
def squares(cursor = 1):
while True:
response = yield cursor**2 #一般情况下会返回None
if response:
cursor = int(response)
else:
cursor += 1
sq = squares()
print(next(sq))
print(next(sq))
sq.send(7)
print(next(sq))1
4
644、迭代对象与迭代器
- 生成器是一种迭代器
- 迭代器是一种包含
__next__方法的对象 - 迭代对象是一种包含了
__iter__方法的对象,该方法负责返回一个迭代器 for in可以相应迭代对象和迭代器next只能相应迭代器
range(python3)或者xrange(python2)是一种迭代对象
r = range(1,3)
next(r)---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-19-7cb3a8583905> in <module>()
1 r = range(1,3)
----> 2 next(r)
TypeError: 'range' object is not an iteratorit = r.__iter__()
next(it)15、标准库中的生成器
(1)range
该对象返回的迭代器实际上是一个生成器
(2)dict.items及其家族
dict.keysdict.valusedict.itemsd = {'foo':'bar', 'baz':'bacon'} it = iter(d.items()) next(it) next(it)(‘baz’, ‘bacon’)
使用生成器的原因是防止创建额外的字典副本,节约内存。 在迭代期间修改字典可能出现副作用
d = {'foo':'bar', 'baz':'bacon'}
it = iter(d.items())
next(it)
d['spam'] = 'eggs'
next(it)---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-25-c1caf20db5b5> in <module>()
3 next(it)
4 d['spam'] = 'eggs'
----> 5 next(it)
RuntimeError: dictionary changed size during iteration(3)zip
将两个列表依次组成元组返回,直到达到最短的停止
z = zip(['a','b','c','d'], ['x','y','z'])
next(z)
next(z)
next(z)
next(z)---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-26-358fa184f7bb> in <module>()
3 next(z)
4 next(z)
----> 5 next(z)
StopIteration:(4)map
接受一个能接受N参数的函数对象 和 N个参数列表 的对象,依次将参数列表的参数依次传入函数对象并执行并返回返回值
m = map(lambda x,y: max([x,y]),[4,1,7],[3,4,5])
next(m)
next(m)
next(m)
next(m)---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-29-db80638e5ef9> in <module>()
3 next(m)
4 next(m)
----> 5 next(m)
StopIteration:(5)文件对象
open()返回的对象
f = open('lines.txt')
next(f)
next(f)6、何时编写生成器
- 分块访问数据
- 分块计算数据
7、单例模式生成器
指的是该对象既是迭代对象又是生成器,例子:
class Fibonacci(object):
def __init__(self):
self.numbers = []
def __iter__(self):
return self
def __next__(self):
if len(self.numbers) < 2:
self.numbers.append(1)
else:
self.numbers.append(sum(self.numbers))
self.numbers.pop(0)
return self.numbers[-1]
def send(self, value):
pass
# For Python 2 compatibility
next = __next__f = Fibonacci()
it1 = iter(f)
next(it1),next(it1),next(it1),next(it1),next(it1)(1, 1, 2, 3, 5)it2 = iter(f)
next(it2)8搞清楚是否有些对象可以有多个迭代器,而有些对象则不可以
8、生成器内部的生成器
对生成器进行组合
def gen1():
yield 'foo'
yield 'bar'
def gen2():
yield 'spam'
yield 'eggs'
#组合使用python3.3之前
def full_gen():
for word in gen1():
yield word
for word in gen2():
yield word
for s in full_gen():
print(s)
# 或
import itertools
def full_gen():
for word in itertools.chain(gen1(),gen2()):
yield word
for s in full_gen():
print(s)
#组合使用python3.3及更新
def full_gen():
yield from gen1()
yield from gen2()
for s in full_gen():
print(s)foo
bar
spam
eggs
foo
bar
spam
eggs
foo
bar
spam
eggs