这里是一段防爬虫文本,请读者忽略。
本文原创首发于CSDN,作者IDYS
博客首页:https://blog.csdn.net/weixin_41633902/
本文链接:https://blog.csdn.net/weixin_41633902/article/details/108038071
未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!
partial
方法
partial
生成的新函数,是对原函数的封装代码演示1
import functools
def add(x, y):
print("x={}\ty={}".format(x,y))
return x + y
add1 = functools.partial(add, 1, 2)
print(add1())
add2 = functools.partial(add, y=1)
print(add2(10))
add3 = functools.partial(add, x=1)
print(add3(y=10))
add4 = functools.partial(add, 10)
print(add4(20))
x=1 y=2
3
x=10 y=1
11
x=1 y=10
11
x=10 y=20
30
import functools
import inspect
def add(x, y) -> int:
return x + y
newadd = functools.partial(add, y=5) # 相当于提前在函数中放了一个参数 y = 5
print(newadd(7))
print(newadd(7, y=6))
print(newadd(y=10, x=6))
print(inspect.signature(newadd))
12
13
16
(x, *, y=5) -> int
import functools
import inspect
def add(x, y, *args):
print(args)
return x + y
add1 = functools.partial(add, 1, 2, 3, 4, 5, 6, 7)
print(add1(20, 50, 90))
add2 = functools.partial(add, 1)
add2(12, 45, 67, 78)
sig = inspect.signature(add2)
print(sig)
print(sig.parameters)
print(sig.return_annotation)
for key, value in sig.parameters.items():
print(key, value.annotation)
print(value.annotation is value.empty)
(3, 4, 5, 6, 7, 20, 50, 90)
3
(45, 67, 78)
(y, *args)
OrderedDict([('y', <Parameter "y">), ('args', <Parameter "*args">)])
<class 'inspect._empty'>
y <class 'inspect._empty'>
True
args <class 'inspect._empty'>
True
partial
函数本质
def partial(func, *args, **keywords): # partial 源码
def newfunc(*fargs, **fkeywords): # 包装函数
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func # 保留原函数
newfunc.args = args # 保留原函数的位置参数
newfunc.keywords = keywords # 保留原函数的关键字参数参数
return newfunc
def add(x, y):
return x+y
foo = partial(add, 4)
print(foo(5))
9
@functools.lru_cache(maxsize=128, typed=False)
Least-recently-used
装饰器。lru
,最近最少使用。cache
缓存maxsize
设置为None
,则禁用LRU
功能,并且缓存可以无限制增长。当maxsize
是二的幂 时,LRU
功能执行得最好typed
设置为True
,则不同类型的函数参数将单独缓存。例如,f(3)
和f(3.0)
将被视为具有不同结果的不同调用import functools
import time
@functools.lru_cache()
def add(x, y):
time.sleep(3)
return x + y
if __name__ == "__main__":
print(add(1, 2))
print(add(1, 2.0)) # 没有等待
print(add(1, y=2))
print(add(y=2, x=1))
print(add(x=1, y=2))
print(add(1, 2)) # 没有等待
3
3
3
3
3
3
dict_test = dict(x=123, y=234, z=890)
dict_test1 = dict(y=234, x=123, z=890)
print(dict_test)
sorted_item = sorted(dict_test.items())
sorted_item1 = sorted(dict_test1.items())
print(sorted_item)
print(sorted_item1)
test_zero = tuple()
print(test_zero)
for item in dict_test.items():
test_zero += item
print(test_zero)
{
'x': 123, 'y': 234, 'z': 890}
[('x', 123), ('y', 234), ('z', 890)]
[('x', 123), ('y', 234), ('z', 890)]
()
('x', 123, 'y', 234, 'z', 890)
lru_cache
装饰器通过一个字典缓存被装饰函数的调用和返回值
斐波那契数列递归方法的改造
import functools
@functools.lru_cache()
def fbin(n):
if n < 2:
return n
else:
return fbin(n -1) + fbin(n -2)
if __name__ == "__main__":
print(fbin(100))
lru_cache
装饰器应用
key
无法过期、失效import inspect
import functools
import time
import datetime
def logger(duration): # 装饰器传入的时间
def _logger(fn):
local_cache = {
} # 设置缓存
@functools.wraps(fn) # 属性复制,保持函数原有的属性
def wrapper(*args, **kwargs):
def expired_clear(my_local_cache): # 缓存过期清除
dict_remove = list()
for cache_key, cache_value in my_local_cache.items():
if datetime.datetime.now().timestamp() - cache_value[1] > duration:
dict_remove.append(cache_key)
for dict_k in dict_remove:
my_local_cache.pop(dict_k)
print(my_local_cache) # 主要是为了测试而添加,实际应该删除这行代码。但是为了直观的看到缓存过期清理功能。所以添加这一行
expired_clear(local_cache)
def make_key(key_fn): # 设置函数表示符,达到命中
sig = inspect.signature(key_fn)
sig_par = sig.parameters
sig_par_key_tuple = tuple(sig_par.keys())
compare_dict = dict()
for index, a_value in enumerate(args):
compare_dict[sig_par_key_tuple[index]] = a_value
compare_dict.update(kwargs)
for k in sig_par.keys():
if k not in compare_dict.keys():
compare_dict[k] = sig_par[k].default
dict_key = tuple(sorted(compare_dict.items()))
return dict_key
key = make_key(fn)
if key not in local_cache: # 看传入的函数,是否已经存在于缓存当中,如果存在。那么就直接得出值。不存在则计算存入缓存当中
ret = fn(*args, **kwargs)
local_cache[key] = (ret, datetime.datetime.now().timestamp())
else:
local_cache[key] = (local_cache[key][0], datetime.datetime.now().timestamp())
return local_cache[key][0] # 返回函数的值
return wrapper
return _logger
@logger(6) # 装饰器修饰
def add(x=11, y=10):
time.sleep(3)
return x + y
print(add())
print(add(11))
print(add(11, 10))
print(add(11, y=10))
print(add(y=10, x=11))
print(add(x=11, y=10))
print(add(34))
print(add(45))
print(add(12))
{
}
21
{
(('x', 11), ('y', 10)): (21, 1597545428.264142)}
21
{
(('x', 11), ('y', 10)): (21, 1597545428.264142)}
21
{
(('x', 11), ('y', 10)): (21, 1597545428.264142)}
21
{
(('x', 11), ('y', 10)): (21, 1597545428.264142)}
21
{
(('x', 11), ('y', 10)): (21, 1597545428.264142)}
21
{
(('x', 11), ('y', 10)): (21, 1597545428.264142)}
44
{
(('x', 11), ('y', 10)): (21, 1597545428.264142), (('x', 34), ('y', 10)): (44, 1597545431.264453)}
55
{
(('x', 34), ('y', 10)): (44, 1597545431.264453), (('x', 45), ('y', 10)): (55, 1597545434.264718)}
22
def reg_dispatcher():
command = {
}
def reg(name):
def _reg(fn):
if name in command.keys():
error = "{} is repeated ".format(name)
raise Exception(error)
else:
command.setdefault(name, fn)
return fn
return _reg
def default():
print("your command is not founded")
def dispatcher():
while True:
cmd = input(">>")
if cmd.strip() == "quit":
break
command.get(cmd.strip(), default)()
def cancel(name):
if name not in command.keys():
error = "the command name is not founded "
raise Exception(error)
else:
command.pop(name)
return reg, dispatcher, cancel
reg, dispatcher, cancel = reg_dispatcher()
@reg("welcome") # pywel = reg("welcome")(pywel)
def py_wel():
print("welcome to python")
@reg("go")
def go_prin():
print("go out")
cancel("go")
print(py_wel.__doc__)
dispatcher()
>>welcome
welcome to python
>>go
your command is not founded
>>
AOP
面向切面编程Aspect Oriented Programming
的思想体现logger
。这样会造成代码的重复,增加了耦合。logger
的改变影响所有使用它的类或方法AOP
在需要的类或者方法上切下,前后的切入点可以加入增强的功能,让调用者和被调用者解耦logger
函数功能就是对业务函数增加日志的,而业务函数中应该把与业务无关的日志功能剥离干净