函数定义的弊端

函数注解Function Annotations

业务应用

inspect模块

#示例
import inspect
def add(x,y:int,*args,**kwargs) -> int:
return x+y sig = inspect.signature(add)
print('sig:',sig)
print('params :',sig.parameters)#Ordereddict
print('return :',sig.return_annotation)
print(sig.parameters['x'])
print(sig.parameters['y'])
print(sig.parameters['y'].annotation)
print(sig.parameters['args'])
print(sig.parameters['args'].annotation)
print(sig.parameters['kwargs'])
print(sig.parameters['kwargs'].annotation)
print(sig.parameters['kwargs'].empty)
print(sig.parameters['kwargs'].kind)
'''
sig: (x, y:int, *args, **kwargs) -> int
params : OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
return : <class 'int'>
x
y:int
<class 'int'>
*args
<class 'inspect._empty'>
**kwargs
<class 'inspect._empty'>
<class 'inspect._empty'>
VAR_KEYWORD
'''

业务应用

functools模块进阶

#源码 _make_key _HashSeq
def _make_key(args, kwds, typed,
kwd_mark = (object(),),
fasttypes = {int, str, frozenset, type(None)},
tuple=tuple, type=type, len=len): key = args
if kwds:
key += kwd_mark
for item in kwds.items():
key += item
if typed:
key += tuple(type(v) for v in args)
if kwds:
key += tuple(type(v) for v in kwds.values())
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedSeq(key) class _HashedSeq(list):
__slots__ = 'hashvalue' def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup) def __hash__(self):
return self.hashvalue

#利用缓存可以大大提高效率,类似于用空间换取时间!注意缓存与缓冲的区别!
import functools
@functools.lru_cache()
def fib(n):
if n<2:
return n
else:return fib(n-1)+fib(n-2) print(fib(100))

装饰器练习*****

#第一题
#装饰器的应用
#实现一个cache装饰器,实现可过期的被清除的功能
'''
简化设计,函数的形参定义不包括可变位置参数、可变关键词参数和keyword-only参数
可以不考虑缓存满了之后的换出问题。 数据类型的选择:
缓存的应用场景,是有数据需要频繁查询,且每次查询都需要大量计算或等待时间之后才能返回结果的情况,
使用缓存来提高查询速度,用空间换取时间。 cache应该选用什么数据结构?
便于查询的,且能快速找到的数据结构。每次查询的时候,只要输入一致,就应该得到同样的结果(顺序也一致,例如减法函数,参数顺序不一致,结果不一样)
基于上面的分析,此数据结构应该是字典。通过一个key,对应一个value。
key是参数列表组成的结构,value是函数返回值。难点在于key如何处理! key的存储
key必须是hash类,key能接受位置参数和关键字参数传参,位置参数是被收集在一个tuple中的,本身就有序
关键字参数被收集在一个字典中,本身无序,这会带来一个问题,传参的顺序未必是字典中保存的顺序。如何解决?
OrderedDict行吗?可以,它可以记录顺序。
不用OrderedDict行吗?可以,用一个tuple保存排过序的字典的item的kv对。 key的异同
什么才算是相同的key呢?定义一个加法函数,那么传参方式就应该有以下4种:
1.add(4,5)
2.add(4,y=5)
3.add(x=4,y=5)
4.add(y=5,x=4)
上面4种,可以有下面两种理解:
第一种: 3和4相同,1,2和3都不同。
第二种: 1、2、3、4全部相同。
lru_cache实现了第一种,可以看出单独的处理了位置参数和关键字参数。
但是函数定义为def add(4,y=5),使用了默认值,如何理解add(4,5)和add(4)是否一样呢?
如果认为一样,那么lru_cache无能为力,就需要使用inspect来自己实现算法。 key的要求
key必须是hashable,由于key是所有实参组合而成,而且最好要作为key的,key一定要可以hash,但是如果key有不可hash类型数据,就无法完成。
lru_cache就不可以,缓存必须使用key,但是key必须可hash,所以只能适用实参是不可变类型的函数调用。 key算法设计
inspect模块获取函数签名后,取parameters,这是一个有序字典,会保存所有参数的信息。
构建一个字典params_dict,按照位置顺序从args中依次对应参数名和传入的实参,组成kv对,存入params_dict中。
kwargs所有值update到params_dict中。
如果使用了缺省值的参数,不会出现在实参params_dict中,会出现在签名parameters中,缺省值也定义在其中。 调用的方式
普通的函数调用可以,但是过于明显,最好类似lru_cache的方式,让调查者无察觉的使用缓存。
构建装饰器函数 过期功能
一般缓存系统都有过期功能。
过期什么呢?
它是某一个key过期。可以对每一个key单独设置过期时间,也可以对这些key统一设定过期时间。
本次的实现就简单点,统一设置key的过期时间,当key生存超过了这个时间,就自动被清除。
注意:这里并没有考虑多线程等问题。而且这种过期机制,每一次都有遍历所有数据,大量数据的时候,
遍历可能有效率问题。
在下面的装饰器中增加一个参数,需要用到了带参装饰器了。
@mag_cache(5)代表key生存5秒钟后过期。
带参装饰器相当于在原来的装饰器外面再嵌套一层。 清除的时机,何时清除过期key?
1. 用到某个key之前,先判断是否过期,如果过期重新调用函数生成新的key对应value值。
2.一个线程负责清除过期的key,这个后面实现。本次在创建key之前,清除所有过期的key。 value的设计
1.key =>(v,createtimestamp)
适合key过期的时间都是统一的设定
2.key =>(v,createtimestamp,duration)
duration是过期时间,这样每一个key就可以单独控制过期时间。在这种设计中,-1可以表示永不过期,
0可以表示立即过期,正整数表示持续一段时间过期。
这次采用第一种实现!
'''
 #题目:实现一个cache装饰器,实现可过期、可自动清除的功能。

 from functools import wraps
import time
import inspect
import datetime def cache(duration):
def _cache(fn,):
local_cache = {}
@wraps(fn)
def wrapper(*args,**kwargs):
#local_cache中有没有过期的key
# for k,(_,ts) in local_cache:
# if datetime.datetime.now().timestamp()- ts>5:
# local_cache.pop(k) #不能一边迭代一边删除!
def clear_expire(cache):
expire_key = []
for k, (_, ts) in cache.items():
if datetime.datetime.now().timestamp() - ts > duration:
expire_key.append(k)
for k in expire_key:
cache.pop(k)
clear_expire(local_cache) def make_key():
key_dict = {}
sig = inspect.signature(fn)
params = sig.parameters # 返回一个有序字典
params_list = list(params.keys()) # 返回参数列表
# 位置参数add(5,6,y=8)
for i, v in enumerate(args):
k = params_list[i]
key_dict[k] = v
# 关键字参数
# print('*kwargs', *kwargs)
# for k,v in kwargs.items():
# key_dict[k] = v
key_dict.update(kwargs)
# 缺省值参数
for k in params.keys():
if k not in key_dict.keys():
key_dict[k] = params[k].default
key = tuple(sorted(key_dict.items()))
# print('处理后得到的key:',key)
# print('处理后得到的local_cache:',local_cache)
return key key = make_key() if key not in local_cache.keys():
ret = fn(*args, **kwargs)
local_cache[key] = (ret,datetime.datetime.now().timestamp()) # return local_cache[key]
return wrapper
return _cache def logger(fn):
@wraps(fn)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print(delta)
return ret
return wrapper @logger
@cache(5)
def add(x,y=5):
time.sleep(3)
return x+y add(4)
add(4,5)
add(x=4,y=5)
add(4,y=5)
add(y=5,x=4) time.sleep(5)
print('*'*20)
add(4)
add(4,5)
add(x=4,y=5)
add(4,y=5)
add(y=5,x=4)
 #2 题目:写一个命令分发器
"""
1.程序员可以方便的注册函数到某一个命令,用户输入命令时,路由到注册的函数
2.如果此命令没有对应的注册函数,执行默认函数
3.用户输入用input(">>")
"""
'''分析
输入命令映射到一个函数,并执行这个函数。应该是cmd_tbl[cmd]=fn的形式,字典正好合适。
如果输入了某一个cmd命令后,没有找到函数,就要调用缺省的函数执行,这正好是字典缺省参数。
cmd是字符串 实现主要功能后会发现代码布局很丑陋,最好是不要将所有的函数和字典都在全局中定义!
如何改进呢?将reg函数封装成装饰器,并用它来注册函数。 重复注册的问题
如果函数使用同一个函数名注册,就等于覆盖了原来的cmd到fn的关系,这样的逻辑也是合理的。
也可以加一个判断,如果key已经存在,重复注册,抛出异常。看业务需求。 注销
有注册就应该有注销
一般来说注销是要有权限的,但是什么样的人拥有注销的权限。
'''
command_dict = {} #注册(带参装饰器)
def dispatch(): def req(name):
def wrapper(fn):
command_dict[name]=fn
return wrapper def defaultfunc():
print('Unkown command!') def dispatchfunc():
while True:
userinput = input('>>')
if userinput.strip() == 'quit':
return
if userinput in command_dict.keys():
command_dict.get(userinput,defaultfunc)()
else:
defaultfunc()
return req,dispatchfunc req,dispatchfunc = dispatch()
#自定义函数
@req('cy') # f1=req('cy')(f1)
def f1():
print('chengyu')
@req('py')
def f2():
print('python') dispatchfunc()

装饰器的应用

装饰器应用场景

作业

Python进阶3---python类型注解、functools的更多相关文章

  1. Python进阶09 动态类型

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 谢谢TeaEra, 猫咪cat 动态类型(dynamic typing)是Pyth ...

  2. python进阶:Python进程、线程、队列、生产者/消费者模式、协程

    一.进程和线程的基本理解 1.进程 程序是由指令和数据组成的,编译为二进制格式后在硬盘存储,程序启动的过程是将二进制数据加载进内存,这个启动了的程序就称作进程(可简单理解为进行中的程序).例如打开一个 ...

  3. python 进阶篇 python 的值传递

    值传递和引用传递 值传递,通常就是拷贝参数的值,然后传递给函数里的新变量,这样,原变量和新变量之间互相独立,互不影响. 引用传递,通常是指把参数的引用传给新的变量,这样,原变量和新变量就会指向同一块内 ...

  4. 第二篇 python进阶

    目录 第二篇 python进阶 一 数字类型内置方法 二 字符串类型内置方法 三 列表类型内置方法(list) 四 元组类型内置方法(tuple) 五 字典内置方法 六 集合类型内置方法(self) ...

  5. 【Python大系】Python快速教程

    感谢原作者:Vamei 出处:http://www.cnblogs.com/vamei 怎么能快速地掌握Python?这是和朋友闲聊时谈起的问题. Python包含的内容很多,加上各种标准库.拓展库, ...

  6. Python3.6引入的f-string 与 Python 3的新的特性:类型注解;

    f-string 1.介绍 f-string(formatted string literals):格式化字符串常量,是Python3.6新引入的一种字符串格式化方法,使格式化字符串的操作更加简便. ...

  7. Python入门篇-类型注解

    Python入门篇-类型注解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.函数定义的弊端 1>.动态语言很灵活,但是这种特性也是弊端 Python是动态语言,变量随时可 ...

  8. ​Python 3 新特性:类型注解——类似注释吧,反正解释器又不做校验

    ​Python 3 新特性:类型注解 Crossin ​ 上海交通大学 计算机应用技术硕士 95 人赞同了该文章 前几天有同学问到,这个写法是什么意思: def add(x:int, y:int) - ...

  9. python 类型注解

    函数定义的弊端 python 是动态语言,变量随时可以被赋值,且能赋值为不同类型 python 不是静态编译型语言,变量类型是在运行器决定的 动态语言很灵活,但是这种特性也是弊端 def add(x, ...

  10. Python Type Hint类型注解

    原文地址:https://realpython.com/python-type-checking/ 在本指南中,你将了解Python类型检查.传统上,Python解释器以灵活但隐式的方式处理类型.Py ...

随机推荐

  1. Spring事务事件监控

    前面我们讲到了Spring在进行事务逻辑织入的时候,无论是事务开始,提交或者回滚,都会触发相应的事务事件.本文首先会使用实例进行讲解Spring事务事件是如何使用的,然后会讲解这种使用方式的实现原理. ...

  2. 第六课 Html5常用标签 html5学习1

    HTML标签的认识一.标签的分类1.双标签 如<html> </html>2.单标签 如<br \> 换行标签 二.标签的关系1.嵌套关系 如<head> ...

  3. csharp: LocalDataCache.sync

    app.config: <?xml version="1.0" encoding="utf-8" ?> <configuration> ...

  4. AEAI CRM V1.6.0 升级说明,开源客户关系管理系统

    1 升级说明 AEAI CRM v1.6.0版是AEAI CRM v1.5.2版客户关系管理系统的升级版本,本次版本是基于AEAI DP v3.8.0_20170228进行打包部署的,升级内容主要是针 ...

  5. Error occurred during initialization of VM Could not reserve enough space for 2097152KB object heap

    ionic build Android后的报错问题 ionic 升级了splashscreen和statusbar的插件后,执行ionic build android会一直报打包错误.原因是过低的An ...

  6. Visual Studio 无法记忆标签页、断点等的解决办法

    1.到工程目录删除*.suo文件 2.*.suo默认是隐藏的,需要打开查看隐藏文件的开关

  7. idea上使用maven入门(二)——本地仓库以及使用maven

    本地仓库: 首先创建完一个项目之后,点击左上角file->setting(建一个文件夹repository,建议创建到maven的外面,主要如果maven需要换版本,方便管理)如下图: ​ ma ...

  8. postgresql 日志报错could not write to log file: No space left on device,could not write lock file "postmaster.pid": No space left on device

    今天遇到了一个特别奇怪的问题,我在用docker容器的时候,发现我的postgresql怎么也启动不起来 尝试了N多种办法,最后看了看postgresql的日志发现 postgresql 日志中报错 ...

  9. SQLServer之修改存储过程

    修改存储过程注意事项 只能修改先前在 SQL Server 中通过执行 CREATE PROCEDURE 语句创建的过程. Transact-SQL 存储过程修改为 CLR 存储过程,反之亦然. AL ...

  10. spark-2.4.0-hadoop2.7-安装部署

    1. 主机规划 主机名称 IP地址 操作系统 部署软件 运行进程 备注 mini01 172.16.1.11[内网] 10.0.0.11  [外网] CentOS 7.5 Jdk-8.zookeepe ...