流畅的Python(七)-函数装饰器和闭包

一、核心要义

主要解释函数装饰器的工作原理,包括最简单的注册装饰器和较复杂的参数化装饰器。同时,因为装饰器的实现依赖于闭包,因此会首先介绍闭包存在的原因和工作原理。

二、代码示例

1、变量作用域规则

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 11:26
# @Author  : Maple
# @File    : 01-变量作用域规则.py
# @Software: PyCharmb = 10
def f1(a):print(a)print(b)# b = 20 # 在函数体中给b赋值,因此会被判断为局部变量def f1_revise(a):global b # 声明函数体中的b为全局变量print(a)print(b)b = 20"""列表的作用域"""
students = []def f2():# students指向全局变量students.append('a')return id(students)def f3():# 内部再声明一个 students,其为局部变量,与外部的students不是同一个对象students = []students.append('a')return id(students)def f4():# 声明全局变量global studentsstudents +=[1]# print(id(students))return id(students)if __name__ == '__main__':# 1. f1测试# 说明: 1.Python在编译函数f1的定义体时,判断b为局部变量,因为在函数体中给b赋值了#       2.所以在调用函数f1(10)的时候,执行到print(b),发现局部变量b还没有赋值,此时就会报错#f1(10) # UnboundLocalError: local variable 'b' referenced before assignment# 2. f1_revise测试f1_revise(20) # 20,10# 全局变量b的值被修改print(b) # 20# 3.f2测试print(f2() == (id(students))) #True# 4.f3测试print(f3() == (id(students))) #False# 5.f4测试print(f4() == (id(students))) #True

2、闭包

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 11:35
# @Author  : Maple
# @File    : 02-闭包.py
# @Software: PyCharm"""需求:计算移动平局值每调用一次函数,传入一个新的数值,然后和前面的所有值进行累加,再计算最后的平均值
"""# 1. 借用数组方式实现
def make_avg():num_list = []def avg(new_value):num_list.append(new_value)total = sum(num_list)return total / len(num_list)return avg# 2.直接使用变量方式实现:但存在一个坑
def make_avg_revise1():count =  0total = 0def avg(new_value):count += 1total += new_valuereturn  total / countreturn avg# 3.直接使用变量方式实现:填坑
def make_avg_revise2():count =  0total = 0def avg(new_value):nonlocal count,total # 通过nonlocal 将变量标记为`自由变量`count += 1total += new_valuereturn total / countreturn avgif __name__ == '__main__':print("***1. make_avg 测试**********************")# 1. make_avg 测试avg = make_avg()# 分析:1.按理说调用完 make_avg_revise1()返回avg1后,make_avg_revise1函数中的局部变量num_list的作用域应该消失了#       2.但实际上,在avg1中仍然能够调用num_list,这就是所谓闭包现象(变量的作用域外延了)#       3.num_list被称作`自由变量`print(avg(5))  # 5.0print(avg(10))  # 7.5# 查看avg1 创建和绑定的变量## 1-1 创建的局部变量print(avg.__code__.co_varnames)  # ('new_value', 'total')## 1-2 绑定的自由变量print(avg.__code__.co_freevars)  # ('num_list',)## 自由变量num_list绑定在avg1的closure属性中:是一个cell对象print(avg.__closure__)  # (<cell at 0x000001C6095CA760: list object at 0x000001C6095C0900>,)## num_list的值则在cell对象中的cell_contents属性中print(avg.__closure__[0].cell_contents)  # [5, 10]print("*** 2.调用make_avg_revise1会报错**********************")# 2.调用make_avg_revise1会报错# 分析:1. 内层函数avg的变量count 和 total在函数内部赋值,因为在函数体编译的时候,会被当作局部变量,但是又没有初始化声明,所以#           当函数函数调用的时候,会报错try:avg = make_avg_revise1()print(avg(5))except Exception as e:print(e)  #local variable 'count' referenced before assignmentprint("***  3.make_avg_revise2测试**********************")# 3.make_avg_revise2测试avg2 = make_avg_revise2()print(avg2(5)) # 5.0print(avg2(10)) # 7.5

3、装饰器

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 16:04
# @Author  : Maple
# @File    : 03-装饰器.py
# @Software: PyCharmdef decoration(fun):def inner(*args):print("do something before decorated function being excuted")result = fun(*args)print("do something after decorated function being excuted")return  resultreturn inner@decoration
def add(a,b):return  a + bif __name__ == '__main__':f = add# f已经变成 inner,之后调用inner本质上是在调用inner函数print(f) # <function decoration.<locals>.inner at 0x0000015424D75310># f调用过程与闭包有什么关系?# f = add 等价于 f = decoration(add),此后再调用f, 外层函数参数add的作用域应该已经"消失"# 但由于闭包原理,add作为`自由变量`,仍然会被绑定在f中result = f(1,2)"""do something before decorated function being excuteddo something after decorated function being excuted"""print(result) # 3

4、装饰器应用

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 16:18
# @Author  : Maple
# @File    : 04-装饰器应用.py
# @Software: PyCharm"""利用装饰器对上一章中最优策略 部分进行改写"""from collections import namedtuple# 顾客具名元组
Customer = namedtuple('Customer','name fidelity')# 定义商品类
class Item:def __init__(self,product,quantity,price):""":param product: 商品名称:param quantity: 商品数量:param price: 商品单价"""self.product = productself.quantity = quantityself.price = pricedef totol(self):""":return:订单总费用"""return self.quantity * self.price# 定义上下文(订单类)
class Order:def __init__(self,customer,cart,promotion=None):""":param customer: 顾客:param cart: 购物车:param promotion: 优惠策略"""self.customer = customerself.cart = cartself.promotion = promotiondef total(self):""":return:顾客订单打折之前的总费用"""if not hasattr(self,'__total'):self.__total = sum(item.totol() for item in self.cart)return self.__totaldef due(self):""":return:顾客最终支付的费用"""if self.promotion is None:return self.total()return self.total() - self.promotion(self)def __repr__(self):fmt = '<Order total: {:.2f} due: {:.2f}>'return fmt.format(self.total(), self.due())# 策略数组
promos = []def promotion(func):# 这里的promos指向全局变量,为何不是局部变量?list是可变类型,append操作并不会生成新的对象,promos.append(func)return func# 具体策略1:积分优惠策略
# 被promotion装饰的函数,会被append到策略数组promos中
@promotion
def FidelityPromo(order):"""如果积分大于1000,享受15%优惠"""if order.customer.fidelity > 1000:return order.total() * 0.15else:return 0# 具体策略2
@promotion
def BulkItemPromo(order):"""单个商品为20个及以上时,提供10%折扣"""discount = 0for item in order.cart:if item.quantity >= 10:discount += item.totol()* 0.1return discount# 具体策略3
@promotion
def LargetOrderPromo(order):"""购物车中不同商品种类数量达到3个或以上提供7%折扣"""discount = 0# 获取购物车中所有不同的商品products = {item.product for item in order.cart}if len(products) >=3:discount += order.total() * 0.07return round(discount,2)# 最优策略
def optimization_strategy(order):""":param order: 订单类:return:最优策略和最大折扣"""# 手动定义所有优惠策略p_final =  Nonediscount_final = 0for p in promos:discount = p(order)if discount > discount_final:discount_final = discountp_final = preturn (p_final,discount_final)if __name__ == '__main__':# 1. 最优策略示例1cus1 = Customer('Maple', 2000)  # 用户积分大于1000,享受15%(注意:为了测试,数值从5%调整到15%)优惠cart1 = [Item('banana', 20, 2.0), Item('apple', 10, 1.0)]o1 = Order(cus1, cart1, FidelityPromo)print(optimization_strategy(o1))  # (<function FidelityPromo at 0x0000021CAD565310>, 7.5)print('=====================================================')# 2. 最优策略示例2cus2 = Customer('Jack', 880)cart2 = [Item('Book', 30, 1.0), Item('Radio', 5, 1.0)]  # Book订单超过20个,提供10%折扣o2 = Order(cus2, cart2, BulkItemPromo)print(optimization_strategy(o2))  # (<function BulkItemPromo at 0x0000021CAD5653A0>, 3.0)print('=====================================================')# 3. 最优策略示例3cus3 = Customer('Mick', 300)cart3 = [Item('Phone', 5, 2.0), Item('Water', 5, 1.0), Item('ring', 8, 2)]  # 购物车不同商品达到3个.提供7%折扣o3 = Order(cus3, cart3, LargetOrderPromo)print(optimization_strategy(o3))  # (<function LargetOrderPromo at 0x0000021CAD565430>, 2.17)

5、clock_deco

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/2 20:20
# @Author  : Maple
# @File    : 05-clock_deco.py
# @Software: PyCharmimport time
from functools import reduce
from operator import muldef clock(func):"""clock装饰器"""def clocked(*args):start = time.perf_counter()result = func(*args)end = time.perf_counter()time_takes =  end = startarg_str = ','.join([repr(arg) for arg in args])print('[%0.8fs] %s(%s) -> %s' % (time_takes,func.__name__,arg_str,result))return resultreturn clocked@clock
def f1(n):return reduce(mul,range(1,n+1))if __name__ == '__main__':# clock装饰器测试print(f1(5))"""[0.02694280s] f1(5) -> 120120"""

6、使用functools.lur_cache做缓存

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/2 20:56
# @Author  : Maple
# @File    : 06-使用functools.lur_cache做备忘.py
# @Software: PyCharmfrom clock_deco import clock
import functools# 如果不使用lur_cache
# @clock
# def fibonacci(n):
#     if n < 2:
#         return n
#     return fibonacci(n-2) + fibonacci(n-1)# 使用lru_cache
@functools.lru_cache()
@clock
def fibonacci(n):if n < 2:return nreturn fibonacci(n-2) + fibonacci(n-1)if __name__ == '__main__':#1.原生fibonacci测试"""打印结果[0.02825890s] fibonacci(0) -> 0[0.02828490s] fibonacci(1) -> 1[0.02825830s] fibonacci(2) -> 1[0.02829540s] fibonacci(1) -> 1[0.02830080s] fibonacci(0) -> 0[0.02830610s] fibonacci(1) -> 1[0.02830040s] fibonacci(2) -> 1[0.02829520s] fibonacci(3) -> 2[0.02825730s] fibonacci(4) -> 33"""# print(fibonacci(4))#2.使用lru_cache测试"""打印结果[0.02470590s] fibonacci(0) -> 0[0.02472680s] fibonacci(1) -> 1[0.02470550s] fibonacci(2) -> 1[0.02473770s] fibonacci(3) -> 2[0.02470470s] fibonacci(4) -> 3结果说明:(1)n的每个值都只调用一次(2)这是因为fibonacci(n)的值会被缓存起来,下次用到的时候可以直接从缓存获取结果,而不用再重新计算"""print(fibonacci(4))

补充说明原生fibonacci测试结果

7、单分派泛函数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 9:56
# @Author  : Maple
# @File    : 07-单分派泛函数-1.py
# @Software: PyCharm
from collections import abc
import html
import numbers
from functools import singledispatchdef html_parse(obj):"""生成Html,返回不同类型的Python对象:param obj: Python对象:return: html"""content = html.escape(repr(obj))return '<pre>{}</pre>'.format(content)@singledispatch
def html_parse_enhance(obj):"""针对不同的Python对象,以自定义的方式显示1. str: 把内部的换行符替换为'<br>\n';不适用<pre>,而是使用<p>2. int: 以十进制和十六进制显示数字,示例: <pre>42 (0x2a)</pre>3. list:输出一个html列表,根据各个元素的类型进行格式化。示例:html_parse_enhance(['maple',42,{1,23}]),输出-><ul><li><p>maple</p><li><li><pre>42 (0x2a)</pre><li><li><pre>{123}</pre><li></ur>:param obj:Python对象:return:html"""content = html.escape(repr(obj))return '<pre>{}</pre>'.format(content)@html_parse_enhance.register(str)
def _(text):"""对于str类型:把内部的换行符替换为'<br>\n';不适用<pre>,而是使用<p>"""content = html.escape(text).replace('\n','<br>\n')return '<p>{}</p>'.format(content)@html_parse_enhance.register(numbers.Integral)
def _(n):"""对于整数类型: 以十进制和十六进制显示数字,示例: <pre>42 (0x2a)</pre>"""return '<pre>{0} (0x{0:x})</pre>'.format(n)@html_parse_enhance.register(tuple)
@html_parse_enhance.register(abc.MutableSequence)
def _(seq):"""对于list类型: 输出一个html列表,根据各个元素的类型进行格式化"""content = '</li>\n<li>'.join([html_parse_enhance(obj) for obj in seq])return '<ul>\n<li>{}</li>\n</ul>'.format(content)if __name__ == '__main__':# 1.集合对象测试r1 = html_parse({1,2,3})print(r1) # <pre>{1, 2, 3}</pre>r1_eh = html_parse_enhance({1,2,3})print(r1_eh) #<pre>{1, 2, 3}</pre># 2.函数对象测试print('******2.函数对象测试*******************')r2 = html_parse(abs)print(r2) # <pre>&lt;built-in function abs&gt;</pre>r2_eh = html_parse_enhance(abs)print(r2_eh) #<pre>&lt;built-in function abs&gt;</pre># 3.包含换行符\n的字符串测试print('******3.包含换行符\n的字符串测试*******************')r3 = html_parse('maple \n abc')print(r3) # <pre>&#x27;maple \n abc&#x27;</pre>r3_eh = html_parse_enhance('maple \n abc')"""<p>maple <br>abc</p>"""print(r3_eh)# 4.整数测试print('******4.整数测试*******************')r4 = html_parse(42)print(r4) # <pre>42</pre>r4_eh = html_parse_enhance(42) # <pre>42 (0x2a)</pre>print(r4_eh)# 5.列表对象测试print('******5.列表对象测试*******************')r5 = html_parse(['maple',33,{1,2,3}])print(r5) # <pre>[1, 2, 3]</pre>r5_eh = html_parse_enhance(['maple',33,{1,2,3}])"""<ul><li><p>maple</p></li><li><pre>33 (0x21)</pre></li><li><pre>{1, 2, 3}</pre></li></ul>"""print(r5_eh)

8、装饰器工厂函数

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 16:47
# @Author  : Maple
# @File    : 08-装饰器工厂函数.py
# @Software: PyCharm
import time
from functools import reduce
from operator import mulregistry = set()# 定义一个装饰器工厂函数:注册或取消被装饰函数
def register(active=True):def decorate(func):print('running register(active = %s) ——> decorate(%s)'  %(active,func))if active:registry.add(func)else:registry.discard(func)return funcreturn decorate# 注意:装饰器工厂函数 并不是装饰器,必须作为函数调用,即后面要加(),即使不传参数
# 在f1上加上@register()后,模块加载的时候就会自动执行register里面的代码了
@register()
def f1(n):return reduce(mul,range(1,n+1))@register(active=False)
def f2():passdef f3():passif __name__ == '__main__':# 1. 模块加载的时候,就会执行register里面的代码# 因此会输出:"""running register(active = True) ——> decorate(<function f1 at 0x000001D5F61C4F70>)running register(active = False) ——> decorate(<function f2 at 0x000001D5F61CC430>)"""# 2.查看registry的值: 当前只有函数f1注册了print(registry) # {<function f1 at 0x00000141DCFAC0D0>}# 3.f3上并没有加@register,如何手动注册呢?register(active=True)(f3) # running register(active = True) ——> decorate(<function f3 at 0x00000287ECF45790>)# 再次查看registry:f3也被注册print(registry) # {<function f3 at 0x00000287ECF45790>, <function f1 at 0x00000287ED00C040>}

9、参数化clock装饰器

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/3 17:16
# @Author  : Maple
# @File    : 09-参数化clock装饰器.py
# @Software: PyCharm
import time
from functools import reduce
from operator import mulDEFAULT_FOTMAT = '[{time_takes:0.8f}s] {name}({arg_str}) -> {result}'def clock(fmt = DEFAULT_FOTMAT):def decorate(func):"""clock装饰器"""def clocked(*args):start = time.perf_counter()result = func(*args)end = time.perf_counter()time_takes =  end - startname = func.__name__arg_str = ','.join([repr(arg) for arg in args])# *locals是获取clocked中的局部变量:name,arg_str等print(fmt.format(**locals()))return resultreturn clockedreturn decorate@clock()
def f1(n):return reduce(mul,range(1,n+1))@clock('{name}({arg_str}): {result}')
def f2(n):return reduce(mul,range(1,n+1))@clock('fun_name:{name};time_takes:{time_takes}')
def f3(n):return reduce(mul,range(1,n+1))if __name__ == '__main__':# f1指向clocked函数f = f1print(f) #<function clock.<locals>.decorate.<locals>.clocked at 0x000001DD7193C4C0># 1. 默认格式测试f1(4) # [0.00000460s] f1(4) -> 24#2.自定义格式1测试f2(4) # f2(4): 24#3.自定义格式3测试f3(4) # fun_name:f3;time_takes:8.000000000021878e-07

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端学习笔记 | HTML5+CSS3静态网页制作的技巧(持续更新)

注&#xff1a;本文的css样式均使用less写法 1、字体居中 &#xff08;1&#xff09;先text-align:center;行内元素水平居中 &#xff08;2&#xff09;再line-heigh:(盒子高度);行内元素垂直居中 text-align: center;line-height: ( 30 / vw ); 2、盒子居中 情景1&#…

【课程作业_01】国科大2023模式识别与机器学习实践作业

国科大2023模式识别与机器学习实践作业 作业内容 从四类方法中选三类方法&#xff0c;从选定的每类方法中 &#xff0c;各选一种具体的方法&#xff0c;从给定的数据集中选一 个数据集&#xff08;MNIST&#xff0c;CIFAR-10&#xff0c;电信用户流失数据集 &#xff09;对这…

【大数据技术攻关专题】「Apache-Flink零基础入门」手把手+零基础带你玩转大数据流式处理引擎Flink(基础加强+运行原理)

手把手零基础带你玩转大数据流式处理引擎Flink&#xff08;运行机制原理加深&#xff09; 前提介绍运行Flink应用运行机制Flink的两大核心组件JobManagerTaskManagerTaskSlot Flink分层架构Stateful Stream ProcessingDataStream和DataSetDataStream&#xff08;数据流&#xf…

GPIO中断

1.EXTI简介 EXTI是External Interrupt的缩写&#xff0c;指外部中断。在嵌入式系统中&#xff0c;外部中断是一种用于处理外部事件的机制。当外部事件发生时&#xff08;比如按下按钮、传感器信号变化等&#xff09;&#xff0c;外部中断可以立即打断正在执行的程序&#xff0…

大红喜庆版UI猜灯谜小程序源码/猜字谜微信小程序源码

今天给大家带来一款UI比较喜庆的猜灯谜小程序&#xff0c;大家看演示图的时候当然也是可以看得到那界面是多么的喜庆&#xff0c;而且新的一年也很快就来了,所以种种的界面可能都比较往喜庆方面去变吧。 这款小程序搭建是免服务器和域名的&#xff0c;只需要使用微信开发者工具…

Linux一键部署telegraf 实现Grafana Linux 图形展示

influxd2前言 influxd2 是 InfluxDB 2.x 版本的后台进程,是一个开源的时序数据库平台,用于存储、查询和可视化时间序列数据。它提供了一个强大的查询语言和 API,可以快速而轻松地处理大量的高性能时序数据。 telegraf 是一个开源的代理程序,它可以收集、处理和传输各种不…

Linux开发工具

前言&#xff1a;哈喽小伙伴们&#xff0c;经过前边的学习我们已经掌握了Linux的基本指令和权限&#xff0c;相信大家学完这些之后都会对Linux有一个更加深入的认识&#xff0c;但是Linux的学习可以说是从现在才刚刚开始。 这篇文章&#xff0c;我们将讲解若干个Linux的开发工…

Java基础数据结构之Map和Set

Map和Set接口 1.Set集合&#xff1a;独特性与无序性 Set是Java集合框架中的一种&#xff0c;它代表着一组无序且独特的元素。这意味着Set中的元素不会重复&#xff0c;且没有特定的顺序。Set接口有多个实现类&#xff0c;如HashSet、LinkedHashSet和TreeSet。 2.Map集合&…

Redis核心技术与实战【学习笔记】 - 19.Pika:基于SSD实现大容量“Redis”

前言 随着业务数据的增加&#xff08;比如电商业务中&#xff0c;随着用户规模和商品数量的增加&#xff09;&#xff0c;就需要 Redis 能保存更多的数据。你可能会想到使用 Redis 切片集群&#xff0c;把数据分散保存到不同的实例上。但是这样做的话&#xff0c;如果要保存的…

利用牛顿方法求解非线性方程(MatLab)

一、算法原理 1. 牛顿方法的算法原理 牛顿方法&#xff08;Newton’s Method&#xff09;&#xff0c;也称为牛顿-拉弗森方法&#xff0c;是一种用于数值求解非线性方程的迭代方法。其基本思想是通过不断迭代来逼近方程的根&#xff0c;具体原理如下&#xff1a; 输入&#…

PCB笔记(二十三):allegro 标注长宽(一般用于测量板宽)时如何显示双单位

步骤&#xff1a;首先选择标注工具&#xff0c;然后右键→Parameters&#xff0c;在弹出来的窗口中√上如下图二所示选项 最终要达到显示单位的效果的话&#xff0c;需要在Text项键入%v%u。 今天就记录到这里啦O

Leetcode206:反转链表

一、题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 示例&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]输入&#xff1a;head [] 输出&#xff1…

[ESP32 IDF]web server

目录 通过web server控制LED 核心原理解析 分区表 web server的使用 错误Header fields are too long的解决 通过web server控制LED 通过网页控制LED灯的亮灭&#xff0c;一般的ESP32开发板都可以实现&#xff0c;下面这篇文章是国外开发者提供的一个通过web server控制…

13.2K Star,12306 抢票助手帮你回家

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 马上过年了&#xff0c;今年你在哪里过年&#xff1f;回老家吗&#x…

前端入门第一天

目录 HTML超文本标记语言——Hyper Text Markup Language 标签语法&#xff1a; 双标签&#xff1a; 单标签——只有开始标签&#xff0c;没有结束标签 基本骨架&#xff1a; 标签的关系: 注释&#xff1a; 标题标签&#xff1a;&#xff08;新闻标题、文章标题、网页区域…

探索设计模式的魅力:为什么你应该了解装饰器模式-代码优化与重构的秘诀

设计模式专栏&#xff1a;http://t.csdnimg.cn/nolNS 开篇 在一个常常需要在不破坏封装的前提下扩展对象功能的编程世界&#xff0c;有一个模式悄无声息地成为了高级编程技术的隐形冠军。我们日复一日地享受着它带来的便利&#xff0c;却往往对其背后的复杂性视而不见。它是怎样…

项目安全-----加密算法实现

目录 对称加密算法 AES &#xff08;ECB模式&#xff09; AES(CBC 模式)。 非对称加密 对称加密算法 对称加密算法&#xff0c;是使用相同的密钥进行加密和解密。使用对称加密算法来加密双方的通信的话&#xff0c;双方需要先约定一个密钥&#xff0c;加密方才能加密&#…

C++ 动态规划 线性DP 最长共同子序列

给定两个长度分别为 N 和 M 的字符串 A 和 B &#xff0c;求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。 输入格式 第一行包含两个整数 N 和 M 。 第二行包含一个长度为 N 的字符串&#xff0c;表示字符串 A 。 第三行包含一个长度为 M 的字符串&#xff0c;表…

Servlet+Ajax实现对数据的列表展示(极简入门)

目录 1.准备工作 1.数据库源&#xff08;这里以Mysql为例&#xff09; 2.映射实体类 3.模拟三层架构&#xff08;Dao、Service、Controller&#xff09; Dao接口 Dao实现 Service实现&#xff08;这里省略Service接口&#xff09; Controller层&#xff08;或叫Servlet层…