【python--迭代生成器闭包面向对象继承多态】

🚀 作者 :“码上有前”
🚀 文章简介 :深度学习
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬
在这里插入图片描述

python--迭代生成器闭包面向对象继承多态

  • 往期内容
  • 1.迭代
    • for...in
    • 字典的迭代
    • 列表迭代
  • 生成器
    • 推导式的弊端
    • 创建生成器
    • 生成器的迭代
    • 生成器的调用
    • 生成器对象值的获取
  • 迭代器
  • 高阶函数
    • map
    • reduce
    • filter
    • sorted
    • 匿名函数lambda
    • 装饰器
  • 模块与包
    • 模块
  • 面向对象
    • 访问控制
  • 继承与多态
    • 静态语言和动态语言
    • 小结

往期内容

【Python–vscode常用快捷键,必收藏!】
【Python–代码规范 】
【Python --优雅代码写法】
【Python–Python2.x与Python3.x的区别】
【Python–Web应用框架大比较】
【Python—内置函数】
【Python—六大数据结构】
【python–迭代生成器闭包面向对象继承多态】
【Python–定时任务的四种方法】
【Python–迭代器和生成器的区别】
【Python–读写模式全解】
【Python–高级教程】
【Python–网络编程之DHCP服务器】
【Python–网络编程之Ping命令的实现】
【Python–网络编程之TCP三次握手】

1.迭代

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
可迭代对象: 包括字符串,列表,元组,,集合,字典

from collections.abc import Iterable
is_str_Iterable = isinstance('abc', Iterable)  # str可迭代
print("字符串是否是可迭代对象:", is_str_Iterable)

is_int_Iterable = isinstance(1234, Iterable)  # int不可迭代
print("整数是否是可迭代对象:", is_int_Iterable)

is_float_Iterable = isinstance(1234.5, Iterable)  # float不可迭代
print("浮点数是否是可迭代对象:", is_float_Iterable)

is_complex_Iterable = isinstance(1234+5j, Iterable)  # complex不可迭代
print("复数是否是可迭代对象:", is_complex_Iterable)

is_list_Iterable = isinstance([], Iterable)  # list不可迭代
print("列表是否是可迭代对象:", is_list_Iterable)

is_tuple_Iterable = isinstance((), Iterable)  # 元组可迭代
print("元组是否是可迭代对象:", is_tuple_Iterable)

is_dict_Iterable = isinstance({}, Iterable)  # 字典可迭代
print("字典是否是可迭代对象:", is_dict_Iterable)

# 判断生成器是否是可迭代对象
g = (x * x for x in range(10))
is_g_Iterable = isinstance(g, Iterable)  # 集合可迭代
print("生成器是否是可迭代对象:", is_g_Iterable)

set_type = {2,32,'12','34'}
print("set_type类型", type(set_type))  # type类型 <class 'set'>
is_dict_Iterable = isinstance(set_type, Iterable)  # 集合可迭代
print("集合是否是可迭代对象:", is_dict_Iterable)

# 验证一下
for s in set_type:
    print(s)

for…in

# d表示可迭代对象
d = {'a': 1, 'b': 2, 'c': 3}
for key in d:
	print(key)

字典的迭代

# 字典的item()
# 这个方法列表和元组都没有这个方法
dict = {"name":"li","school":"nux","city":"yinchuan"}
l = ['a', 'b', 'c']
tup = ('a', 'b', 'c')
for key,value in dict.items():
    print(key,value)

for value in dict.values():
    print(f"value:{value}")

for key in dict.keys():
    print(f"key:{key}")

列表迭代

# 提供了一种可列举的 键值对的方法enumerate
# 使用enumberate(['a', 'b', 'c'])将列表变成对应的索引对
for k, v in enumerate(['a', 'b', 'c']):
    print(k,v)

生成器

推导式的弊端

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
缺点:容量有限,访问元素开销大,浪费空间

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

创建生成器

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

L = [x * x for x in range(10)]
print(list(range(10)), type(list(range(10))))
print(L)

g = (x * x for x in range(10))
print(g, type(g))  # <class 'generator'>
# <generator object <genexpr> at 0x00000229A6939A10>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

g = (x * x for x in range(10))
print(g, type(g))  # <class 'generator'>
# <generator object <genexpr> at 0x00000229A6939A10>
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 4
# 没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象

生成器的迭代

g = (x * x for x in range(10))
for n in g:
	print("生成器对象开始迭代",n)

# 所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
	
# 举例 :斐波那契
def fib(max):
	n,a,b = 0,0,1
	while n<max:
		print("---",b)
		a,b = b,a+b
		n+=1
	return 'done'	

fib(6)  

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

# 也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator函数,只需要把print(b)改为yield b就可以了
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b # print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

f= f(6)
print(f,type(f)) # <generator object fib at 0x0000018D563F9A10> <class 'generator'>

# 这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator: 

生成器的调用

# 可以看到,odd不是普通函数,而是generator函数,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错。
def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
    
next(odd())# step 1 1
next(odd()) # step 1 1

next(odd())# step 1 1

生成器对象值的获取

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b # print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

g= f(6)
# 将生成器函数赋值给一个变量,然后通过该变量进行遍历生成数
while True:
     try:
         x = next(g)
         print('g:', x)
     except StopIteration as e:
         print('Generator return value:', e.value)
         break

迭代器

我们知道不是生成器对象的str,list,tuple,set,dict都是可以迭代的。而生成器对象也是,有什么方法可以使不是生成器对象str,list,tuple,set,dict变成生成器对象

你可能会问,为什么list、dict、str等数据类型不是Iterator?

#这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

# Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

高阶函数

# 既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
# 接受另一个函数作为参数的函数称为高阶函数

map

# 接受两个参数,一个是函数,另一个是iterable

# map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

# 如求x的平方
def f(x):
    return x * x

r = map(f, [1, 2, 3, 4])
print(r)  # <map object at 0x000001866456AB60>
l = list(r)
print(l, type(l))  # [1, 4, 9, 16] <class 'list'>

还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串

l_s_l =  list(map(str,[1,2,3,4,5]))
print(l_s_l) # ['1', '2', '3', '4', '5']

reduce

# 再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
# reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

# 在使用前需要导入from functools import reduce
from functools import reduce
# 序列求和
 reduce(add, [1, 3, 5, 7, 9])

filter

Python内建的filter()函数用于过滤序列。

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

# 只保留奇数,删除偶数
def is_odd(n):
    return n % 2 == 1


# <filter object at 0x0000025C70D5B8E0>
print(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))

# 第一个参数是函数,第二个参数是序列
# 要使用list将filter变成序列
l_filter = list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
print(l_filter)  # [1, 5, 9, 15]

又一实例:计算素数 利用埃氏算法

# 计算素数
# 不断递增
def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n

# 定义筛选器
def _not_divisible(n):
    return lambda x: x % n > 0


# 定义一个生成器 不断生成下一个
def primes():
    yield 2
    it = _odd_iter()  # 初始序列
    while True:
        n = next(it)  # 返回序列的第一个数
        yield n
        it = filter(_not_divisible(n), it)  # 构造新序列
# 这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。


# 由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:
# 打印1000以内的素数:
for n in primes():
    if n < 1000:
        print(n)
    else:
        break
# 注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。

sorted

匿名函数lambda

装饰器

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。


现在,假设我们要增强()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下

待定

模块与包

模块

在使用Python时,我们经常需要用到很多第三方库,例如,上面提到的Pillow,以及MySQL驱动程序,Web框架Flask,科学计算Numpy等。用pip一个一个安装费时费力,还需要考虑兼容性。我们推荐直接使用Anaconda,这是一个基于Python的数据处理和科学计算平台,它已经内置了许多非常有用的第三方库,我们装上Anaconda,就相当于把数十个第三方模块自动安装好了,非常简单易用。
廖雪峰

面向对象

面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

def print_score(std):
    print('%s: %s' % (std['name'], std['score']))

假设处理学生成绩可以通过函数实现,比如打印学生的成绩,面向过程考虑的是事件的处理流程。而面向对象首选思考的不是程序的执行流程,而是Student这种数据类型应该被视为一个对象,这个对象拥有name和score这两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个print_score消息,让对象自己把自己的数据打印出来。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。
面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。

访问控制

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

class Student(object):

    def __init__(self, name, score):
        # 通过_init_方法把self的属性变成私有的属性
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

# 外部通过调用内部方法获取内部属性
# 但是如果外部代码要获取name和score怎么办?
# 可以给Student类增加get_name和get_score这样的方法:

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

# 外部通过修改内部方法获取内部属性
# 你也许会问,原先那种直接通过bart.score = 99也可以修改啊,
# 为什么要定义一个方法大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数:
    def set_score(self, score):
        self.__score = score


# 外界就不能调用了
bart = Student('Bart Simpson', 59)
bart.__name  # AttributeError: 'Student' object has no attribute '__name'




# 需要注意的是,在Python中,变量名类似__xxx__的,
# 也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。

# 有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

# 双下划线开头的实例变量是不是一定不能从外部访问呢?
# 其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:
# 但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。

# Python本身没有任何机制阻止你干坏事,一切全靠自觉。

继承与多态

继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:

class Animal(object):
    def run(self):
        print('Animal is running...')

# 对父类的方法进行改进
class Dog(Animal):

    def run(self):
        print('Dog is running...')

    def eat(self):
        print('Eating meat...')


class Cat(Animal):
    pass


dog = Dog()
dog.run()

# 当子类和父类都存在相同的run()方法时,
# 我们说,子类的run()覆盖了父类的run(),
# 在代码运行的时候,总是会调用子类的run()。
# 这样,我们就获得了继承的另一个好处:多态。

静态语言和动态语言

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。

对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:

class Timer(object):
    def run(self):
        print('Start...')

这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

小结

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写(覆写)

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

说说对BOM的理解(常见的BOM对象了解哪些)

文章目录 一、是什么二、window三、location四、navigator五、screen六、history 一、是什么 BOM (Browser Object Model)&#xff0c;浏览器对象模型&#xff0c;提供了独立于内容与浏览器窗口进行交互的对象 其作用就是跟浏览器做一些交互效果,比如如何进行页面的后退&…

【自然语言处理】seq2seq模型—机器翻译

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主 有任何疑问或者问题&#xff0c;也欢…

SG3225EAN规格书

SG3225EAN 晶体振荡器利用先进的锁相环技术和AT切割晶体单元&#xff0c;提供了宽频率范围和高性能LV-PECL输出&#xff0c;73.5 MHz至700 MHz的宽频率范围&#xff0c;能够保证高稳定性和宽频率调整的能力&#xff0c;适应于多样化的应用需求。2.5V和3.3V两种供电电压&#xf…

AutoSAR(基础入门篇)10.3-BswM配置

目录 一、ECU State Handing(ESH) 二、Module Initialization 三、Communication Control 说起BswM的配置,其实博主问过很多朋友了,大家基本都只用自动配置;很少有用到手动配置的时候,对于刚刚入门的大家来说,掌握自动配置基 本也就足够了。 一、ECU State Handing(ESH…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱13(附带项目源码)

效果演示 文章目录 效果演示前言每次丢弃一个物品源码完结前言 欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中,我们将探索如何用unity制作一个3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机…

【Python中unittest(单元测试)的简单应用】

1、unittest核心组成&#xff1a; TestFixture&#xff1a;对一个测试用例环境的搭建和销毁&#xff0c;一个fixture&#xff0c;通过覆盖TestCase的setUp()和tearDown()方法来实现。如可以在setUp()中通过建立数据库连接来初始化&#xff0c;在tearDown()中清除数据库产生的数…

GIS分析交通事故

确定交通事故的集中区域对于制定新的战略以最大限度地减少事故具有重要作用。在本研究中&#xff0c;我们将使用核密度分析来检测交通事故集中的区域。 下载显示 2018 年至 2020 年期间北卡罗来纳州凯里市发生的交通事故的点数据。 我们可以看到&#xff0c;数据使用的是地理坐…

每日五道java面试题之java基础篇(十二)

目录: 第一题 谈谈你对AQS的理解&#xff0c;AQS如何实现可重⼊锁&#xff1f;第二题. Sychronized的偏向锁、轻量级锁、重量级锁第三题 CountDownLatch和Semaphore的区别和底层原理第四题 线程池中阻塞队列的作⽤&#xff1f;为什么是先添加列队⽽不是先创建最⼤线程&#xff…

【设计模式】使用装饰器模式对价格计算功能做灵活的拓展

文章目录 1. 概述2.实现方式2.1.原始商品类及计算接口2.2.加入装饰器2.3.装饰器的组合使用 3.总结 1. 概述 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型的设计模式&#xff0c;使用组合的方式来替代了继承&#xff0c;它的核心作用是在不修改对象本身的…

值得收藏的的适用于 Windows 11 的免费数据恢复软件榜单

终于要说到Windows 11了&#xff0c;有太多令人惊叹的功能&#xff0c;让人跃跃欲试。但是&#xff0c;在升级到 Windows 11 或使用 Windows 11 时&#xff0c;人们可能会因计算机问题而导致文件被删除或丢失。这就是为什么需要 Windows 11 的免费文件恢复的原因。这是适用于 W…

一周学会Django5 Python Web开发-项目配置settings.py文件-其他配置

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计17条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

C++ //练习 7.27 给你自己的Screen类添加move、set和display函数,通过执行下面的代码检验你的类是否正确。

C Primer&#xff08;第5版&#xff09; 练习 7.27 练习 7.27 给你自己的Screen类添加move、set和display函数&#xff0c;通过执行下面的代码检验你的类是否正确。 Screen myScreen(5, 5, X); myScreen.move(4, 0).set(#).display(cout); cout<<"\n"; myScr…

开源C语言库Melon:Cron格式解析

本文介绍开源C语言库Melon的cron格式解析。 关于 Melon 库&#xff0c;这是一个开源的 C 语言库&#xff0c;它具有&#xff1a;开箱即用、无第三方依赖、安装部署简单、中英文文档齐全等优势。 Github repo 简介 cron也就是我们常说的Crontab中的时间格式&#xff0c;格式如…

Debezium发布历史136

原文地址&#xff1a; https://debezium.io/blog/2023/01/06/change-data-capture-with-questdb-and-debezium/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. Change Data Capture with QuestDB and Debezium …

【NI-DAQmx入门】处理数据采集和测试系统中噪声的几种主要方法

在实际的测试系统中测量模拟信号并不总是像将信号源连接到测量设备那么简单。数据完整性取决于被控制和监视的电气设备发送和接收的干净的电信号。 电噪声可能会掩盖电信号并使其无法识别&#xff0c;从而损害原本具备功能的 DAQ 系统。数据采集​​是关键任务应用测试系统的一…

深入浅出了解谷歌「Gemini大模型」发展历程

Google在2023年12月官宣了Gemini模型&#xff0c;随后2024年2月9日才宣布Gemini 1.0 Ultra正式对公众服务&#xff0c;并且开始收费。现在2024年2月14日就宣布了Gemini 1.5 Pro&#xff0c;史诗级多模态最强MoE首破100万极限上下文纪录&#xff01;&#xff01;&#xff01;Gem…

语音唤醒——

文章目录 配置主代码 参考文档&#xff1a;https://picovoice.ai/docs/quick-start/porcupine-python/ 配置 pip install pvporcupine主代码 ACCESS_KEY&#xff1a;需要将该参数填入即可 # # Copyright 2018-2023 Picovoice Inc. # # You may not use this file except in …

如何修复Microsoft Edge不能以全屏模式打开​?这里提供几个故障排除方法

随着越来越多的Windows 10用户将Edge设置为默认浏览器&#xff0c;各种错误和小故障层出不穷。例如&#xff0c;许多用户抱怨他们无法在全屏模式下启动Edge。如果你正在寻找解决方案来解决这个恼人的问题&#xff0c;请按照下面的故障排除步骤进行操作。 修复Microsoft Edge不…

内容检索(2024.02.17)

随着创作数量的增加&#xff0c;博客文章所涉及的内容越来越庞杂&#xff0c;为了更为方便地阅读&#xff0c;后续更新发布的文章将陆续在此汇总并附上原文链接&#xff0c;感兴趣的小伙伴们可持续关注文章发布动态&#xff01; 本期更新内容&#xff1a; 1. 信号完整性理论与…

JavaWeb:关于登录认证的简单拓展

前提介绍 本文基于文章-------JavaWeb&#xff1a;SpringBootWeb登录认证 --黑马笔记 -------再做简单拓展 如果没有关于登录认证知识的基础&#xff0c;可以先看上面所说的的文章&#xff0c;文章在专栏javaweb中&#xff0c;下面我为了大家观看&#xff0c;直接放了链接。…