'''2.2 高级语法''' # 1.迭代器 __next__返回容器的下一个元素 __iter__返回迭代器本身 # 用内置函数iter和一个序列创建迭代器 i = iter('abc') item = next(i) print(item) # a second_item = next(i) print(second_item) # b # 自定义迭代器 class CountDown: def __init__(self, step): self.step = step def __next__(self): if self.step <= 0: raise StopIteration self.step -= 1 return self.step def __iter__(self): return self count = CountDown(5) first_count = count.__next__() print(first_count) second_count = count.__next__() self_second_count = count.__iter__() print(second_count, '\n', self_second_count) # 2.生成器 让编写返回元素序列的函数所需代码变得简单,高效 yield语句 生成器暂停函数并返回一个中间值 并保存执行上下文 # 返回generator对象 可用next和for循环从生成器中获取新的元素 像迭代器一样 def fibonacci(): a, b = 0, 1 while True: yield b a, b = b, b + a fib = fibonacci() return_element1 = next(fib) print((return_element1)) return_element2 = next(fib) print(return_element2) return_element3 = next(fib) print(return_element3) for i in range(10): a = next(fib) print(a, end=' ') # 每个函数都定义一个对序列的转换然后将这些函数链接起来 def power(values): for value in values: print('powering %s' % value) yield value def adder(values): # yield values for value in values: # value = next(values) print('adding to %s' % value) if value % 2 == 0: yield (value + 3) else: yield (value + 2) elements = [1, 4, 7, 9, 12, 19] results = adder(power(elements)) first = next(results) print(first) second = next(results) print(second) # 生成器的另一个特性是可利用next函数与调用的代码进行交互,yield变成一个表达式 # 值可通过名为send的方法来传递 def psychogist(): print('please tell me your issues') while True: answer = (yield) if answer is not None: if answer.endswith('?'): print("don't ask") elif 'good' in answer: print("good") elif 'bad' in answer: print('bad') free = psychogist() next(free) free.send('i feel bad') free.send('why i should ?') free.send("i should find what is good for me") # send会将函数定义内部传入的值变成yield的返回值 # 3.装饰器 作用是使函数包装与方法包装(一个函数,接受函数并返回其增强函数) # 变得更容易阅读和理解 # 最初的使用场景是在方法定义的开头能够将其定义为类方法或静态方法 class WithDecorators: @staticmethod def some_static_method(): print("this is static method") @classmethod def some_class_method(cls): print("this is class method") # 一、一般语法和可能的实现 # 接受单一参数 # 1) 作为一个函数 : 自定义装饰器,编写一个函数,返回包装原始函数调用的一个子函数 def mydecorator(function): def wrapped(*args, **kwargs): # 在调用函数之前,做点什么 result = function(*args, **kwargs) # 在调用函数之后 做点什么 并返回结果 return result return wrapped # 2) 作为一个类 : 如果装饰器需要复杂的参数化或者依赖于特定状态,用户自定义类更好 class DecoratorAsClass: def __init__(self, function): self.function = function def __call__(self, *args, **kwargs): # 在调用原始函数之前做点什么 result = self.function(*args, **kwargs) # 在调用函数之后做点什么 # 返回结果 return result # 3) 参数化装饰器 可接受参数 def repeat(number=3): """多次重复执行装饰函数。 返回最后一次原始函数调用的值作为结果 :param number:重复次数 默认为3 """ def actual_decorator(function): def wrapper(*args, **kwargs): result = None for _ in range(number): result = function(*args, **kwargs) return result return wrapper return actual_decorator # 装饰器接收参数 @repeat(2) def foo(): print("foo") foo() # 4)保存内省的装饰器 # 使用装饰器常见错误是在使用装饰器时不保存函数元数据(文档字符串,原函数名), # 例如: def dummy_devorator(function): def wrapped(*args, **kwargs): """包装函数内部文档""" return function(*args, **kwargs) return wrapped @dummy_devorator def function_with_important_docstring(): """保存的重要文档""" print(function_with_important_docstring.__name__) # wrapped print(function_with_important_docstring.__doc__) # 包装函数内部文档 # 失去了原始名字和文档字符串 # 解决该问题使用functools模块内置的wraps()装饰器 from functools import wraps def preserving_decorator(function): @wraps(function) def wrapped(*args, **kwargs): """包装函数内部文档""" return function(*args, **kwargs) return wrapped @preserving_decorator def function_with_important_docstring(): """保存的重要文档""" print(function_with_important_docstring.__name__) # function_with_important_docstring print(function_with_important_docstring.__doc__) # 保存的重要文档 # 二、用法和有用的例子 # 常见装饰器模式 # 1)参数检查:检查函数接受或返回的参数 rpc_info = {} def xmlrpc(in_=(), out=(type(None),)): def _xmlrpc(function): # 注册签名 func_name = function.__name__ rpc_info[func_name] = (in_, out) # print(rpc_info) def _check_types(elements, types): # print(elements) # print(len(elements)) # print(types) # print(len(types)) """用来检查类型的子函数""" if len(elements) != len(types): raise TypeError('argument count is wrong') typed = enumerate(zip(elements, types)) for index, couple in typed: arg, of_the_right_type = couple if isinstance(arg, of_the_right_type): continue raise TypeError('arg #%d should be %s' % (index, of_the_right_type)) # 包装过的函数 def __xmlrpc(*args): # 检查输入的内容 checkable_args = args[1:] _check_types(checkable_args, in_) # 运行函数 res = function(*args) # 检查输出内容 if not type(res) in (tuple, list): checkable_res = (res,) else: checkable_res = res _check_types(checkable_res, out) # 函数极其类型检查成功 return res return __xmlrpc return _xmlrpc class RPCView: @xmlrpc((int, int)) def meth1(self, int1, int2): print('received %d and %d' % (int1, int2)) @xmlrpc((str,), (int,)) def meth2(self, phrase): print('received %s' % phrase) return 12 my = RPCView() print(my.meth1(1, 2)) print((my.meth2('a'))) # 输出 我看不懂!!!!!!!!!!! # received 1 and 2 # None # received a # 12
# 2)缓存:重点关注那些内部状态不会影响输出的函数 每组参数都可以链接到唯一的结果 # 函数式编程 # 缓存装饰器可以将输出和计算它所需要的参数放在一起,在后续的调用中直接返回它 import time,hashlib,pickle cache = {} def is_obsolete(entry, duration): return time.time() - entry['time'] > duration def compute_key(function, args, kw): key = pickle.dumps((function.__name__, args, kw)) return hashlib.sha1(key).hexdigest() def memoize(duration=10): def _memoize(function): def __memoize(*args, **kw): key = compute_key(function, args, kw) # 是否已经有它了 if (key in cache and not is_obsolete(cache[key], duration)): print('we got a winner') return cache[key]['value'] # 计算 result = function(*args, **kw) # 保存结果 cache[key] = { 'value': result, 'time': time.time() } print(cache) return result return __memoize return _memoize # duration参数作用是 如果上一次函数调用已经过去很久 他会使缓存值无效 @memoize(10) def very_complex_stuff(a, b): return a + b # 有问题!!!!!!!!!!!! test = very_complex_stuff(2, 2) print(test) # time.sleep(15) test2 = very_complex_stuff(2, 5) print(test2) # 代理 代理装饰器用全局机制来标记和注册函数 # 一个根据当前用户来保护代码访问的安全层可以使用集中式检查器和相关的可调用对象要求的权限来实现 # 这一模型常用于python web框架中,用于定义可发布类的安全性 class User(object): def __init__(self, roles): self.roles = roles class Unauthorized(Exception): pass def protect(role): def _protect(function): def __protect(*args, **kw): user = globals().get('user') print(user.roles) print(role) if user is None or role not in user.roles: raise Unauthorized("I WON'T TELL YOU") return function(*args, **kw) return __protect return _protect tarek = User(('admin', 'user')) bill = User(('user',)) class MySecrets(object): @protect('admin') def waffle_recipe(self): print('use tons of butter') these_are = MySecrets() user = tarek print(these_are.waffle_recipe()) user = bill print(these_are.waffle_recipe()) # 4)上下文提供者 上下文装饰器确保函数可以运行在正确的上下文中,或者在函数前后运行一些代码 # 它设定并复位一个特定的执行环境 # 当一个数据项需要在多个线程之间共享时 需要用一个锁来保护它避免多次访问 # 这个锁可以在装饰器中编写 from threading import RLock lock = RLock() def synchronized(function): def _synchronized(*args, **kw): lock.acquire() try: return function(*args, **kw) finally: lock.release() return _synchronized @synchronized def thread_safe(): pass #
############################################# # 4.上下文管理器-with语句 # 为了确保即使在出现错误的情况下也能运行某些清理代码 # try finally语句是很有用的 多种使用场景 # 关闭一个文件 释放一个锁 创建一个临时的代码补丁 在特殊环境中运行受保护的代码 # with语句为这些使用场景下的代码块包装提供了一种简单的方法 # 即使代码块引发异常 也可以在其执行前后调用一些代码 # 处理文件 # linux系统 ''' with open('/etc/hosts') as hosts: for line in hosts: if line.startswith('#'): continue print(line.strip()) ''' # 一、一般语法和可能的实现 # 1)作为一个类 # 执行with语句的过程如下 # [1]调用__enter__ 任何返回值都会绑定到指定的as子句 # [2]执行内部代码块 # [3]调用__exit__方法 接受代码块中出现错误时填入的3个参数 若无错误则三个值都为None # 任何实现了上下文管理器协议的对象都可以用作上下文管理器 class ContextIllustration: def __enter__(self): print('entering context') def __exit__(self, exc_type, exc_value, traceback): print('leaving context') if exc_type is None: print('with no error') else: print('with an error (%s)' % exc_value) with ContextIllustration(): print("inside") # 输出 # entering context # inside # leaving context # with no error # 2)作为一个函数-contextlib模块 # 标准库的contextlib模块提供了与上下文管理器一起使用的辅助函数 # 它最有用的部分contextmanager装饰器,可以在一个函数里面同时提供__enter__/__exit__ # 中间用yield语句分开 from contextlib import contextmanager @contextmanager def context_illustration(): print('entering context') try: yield except Exception as e: print('leaving context') print('with an error (%s)' % e) # 需要再次抛出异常 raise else: print('leaving context') print('with no error') with context_illustration(): print("inside_second") # 输出 # entering context # inside_second # leaving context # with no error # 该模块还提供其他三个辅助函数 # [1]closing(element): 返回一个上下文管理器,退出时会调用该元素的close方法 # [2]supress(*exceptions):他会压制发生在with语句正文中的特定异常 # [3]redirect_stout(new_target) and redirect_stderr(new_target):他会将代码块中任何代码的 # sys.stout or sys.stderr 输出重定向到类文件(file-like)对象的另一个文件 '''2.3其他语法''' # 1.for...else..语句 可以在循环‘自然结束‘而不是被break语句终止时 执行一个代码块 for number in range(1): print(number) else: print('no break') # 输出 0 no break # 2.函数注解 关于用户自定义函数使用的类型的完全可选的元信息 # 没有任何语法意义,可以为函数定义注解,并在运行时获取这些注解 # [1] 一般语法-- def f(ham: str, eggs: str= 'eggs') -> str: pass print(f.__annotations__) # {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>} # 返回值注解定义为表示def语句结尾的冒号与参数列表之后的->之间的表达式 # 参数注解的定义为冒号后计算注解值的表达式