Python(五)—— 闭包装饰器

news2025/2/4 2:24:46

13. 闭包

13.1 闭包的案例

给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求

例如:整个历史中的某个商品的平均收盘价。就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值,平均值要考虑直至目前为止所有的价格:
第一天价格为:100元,平均收盘价:100 元
第二天价格为:110元,平均收盘价:(100+110) /2 元
第三天价格为:120元,平均收盘价:(100+110+120) /3 元
........

series = []
def make_averager(new_value):
    series.append(new_value)
    total = sum(series)
    return total / len(series)

print(make_averager(100))    # 100.0
print(make_averager(110))    # 105.0
print(make_averager(120))    # 110.0

从上面的例子可以看出,基本上完成了我们的要求,但是这个代码相对来说是不安全的,因为你的这个series列表是一个全局变量,只要是全局作用域的任何地方,都可能对这个列表进行改变

series = []
def make_averager(new_value):
    series.append(new_value)
    total = sum(series)
    return total / len(series)

print(make_averager(100))   # 100.0
print(make_averager(110))   # 105.0
series.append(50)           # 如果对数据进行改变,那么平均收盘价就会出现问题
print(make_averager(120))   # 95.0

那有人说了,你把他放在函数中不就行了,这样不就是局部变量了么?数据不就相对安全了么

def make_averager(new_value):
    series = []
    series.append(new_value)
    total = sum(series)
    return total / len(series)

print(make_averager(100))    # 100.0
print(make_averager(110))    # 110.0
print(make_averager(120))    # 120.0

这样计算的结果是不正确的,那是因为执行函数,会开启一个临时的名称空间,随着函数的结束而消失,所以你每次执行函数的时候,都是重新创建这个列表,那么这种情况下,就需要用到闭包了

def make_averager():
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)
    return averager

avg = make_averager()

print(avg(100))     # 100.0
print(avg(110))     # 105.0
print(avg(120))     # 110.0


"""
注释说明:
在函数中嵌套了一个函数。那么avg这个变量接收的实际是averager函数名,
也就是其对应的内存地址,我执行了三次avg也就是执行了三次averager这个函数;
series变量叫自由变量,averager函数的作用域会延伸到包含自由变量series的绑定。
也就是说,每次我调用avg对应的averager函数时,都可以引用到这个自用变量series,
这个就是闭包
"""

13.2 闭包的过程演示

① 创建了一个make_averager()函数、一个series = []空列表、一个averager(new_value)函数

② 因为avg=make_averager(),因此执行avg(100),相当于执行了make_averager()函数,从而执行averager(new_value)函数,并将值100传给了new_value,添加进series = []空列表,相当于此时series = [100],此时的total=100,列表的len为1,并返回total/len(series),即100/1=100,得出结果为100.0

③ 同理,此时再执行avg(110),将值110传给了new_value,添加进series = [100]这个列表,相当于此时series = [100, 110],此时的total=210,列表的len为2,并返回total/len(series),即210/2=105,得出结果为105.0

④ 同理,此时再执行avg(120),将值120传给了new_value,添加进series = [100, 110]这个列表,相当于此时series = [100, 110, 120],此时的total=330,列表的len为3,并返回total/len(series),即330/3=110,得出结果为110.0

13.3 闭包的定义

  • 1、闭包是嵌套在函数中的函数

  • 2、闭包必须是内层函数对外层函数的变量(非全局变量)的引用

13.4 闭包的作用

  • 保存局部信息不被销毁,保证数据的安全性

13.5 闭包的应用

  • 1、可以保存一些非全局变量但是不易被销毁、改变的数据

  • 2、装饰器

13.6 闭包的判断

"""----- 例1 ------"""

def wrapper():          # 定义了外层函数
    a = 1               # a为外层函数的变量,且非全局变量
    def inner():        # 定义了内层函数
        print(a)        # 调用了a这个变量
    return inner
ret = wrapper()         # 外层函数进行引用

# 因此:该例是闭包的应用
"""----- 例2 ------"""

a = 2                # a在外层函数外,为全局变量
def wrapper():
    def inner():
        print(a)
    return inner
ret = wrapper()

# 因此:该例非闭包的应用
"""----- 例3 ------"""

def wrapper(a,b):
    def inner():
        print(a)
        print(b)
    return inner
a = 2
b = 3
ret = wrapper(a,b)

# 因此:该例是闭包的应用

13.6.1 通过函数属性判断

"""
注释说明:
函数名.__code__.co_freevars 查看函数的自由变量
如果输出结果为:('series',) 说明series是自由变量
"""

def make_averager():
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager
avg = make_averager()

print(avg.__code__.co_freevars)

# 输出结果:
('series',)

其他参数,仅供了解: ​

def make_averager():
    series = []
    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager
avg = make_averager()


# 1、函数名.__code__.co_varnames 查看函数的局部变量
print(avg.__code__.co_varnames)
# 输出结果:('new_value', 'total')

# 2、函数名.__closure__ 获取具体的自由变量对象,也就是cell对象
print(avg.__closure__)
# 输出结果:(<cell at 0x104b59930: list object at 0x104b7ddc0>,)

# 3、cell_contents 自由变量具体的值
print(avg.__closure__[0].cell_contents)
# 输出结果:[]

14. 装饰器

14.1 开放封闭原则

开放封闭原则具体具体定义是这样:

  • 1、对扩展是开放的

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能

  • 2、对修改是封闭的

    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对函数内部进行修改,或者修改了函数的调用方式,很有可能影响其他已经在使用该函数的用户

14.2 初识装饰器

在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能

举例:你现在公司的开发部分任职,领导给你一个业务需求让你完成:让你写代码测试你写的函数的执行效率

def index():
    print('欢迎访问雨落主页')

版本1:

需求分析:你要想测试此函数的执行效率,应该在此函数执行前记录一个时间, 执行完毕之后记录一个时间,这个时间差就是具体此函数的执行效率。那么执行时间如何获取,可以利用time模块,有一个time.time() 功能

"""
此方法返回的是格林尼治时间,即此刻距离1970年1月1日0点0分0秒的时间秒数,也叫时间戳,
所以要是计算函数的执行效率就是在执行前后计算这个时间戳的时间,然后求差值即可
"""

import time
print(time.time())

# 输出结果
1703569318.8058171

由于index函数只有一行代码,执行效率太快了,所以我们利用time模块的一个sleep模拟一下,默认让他程序睡眠2秒之后再执行

import time
def index():
    time.sleep(2)      # 睡眠2秒,模拟一下网络延迟以及代码的效率
    print('欢迎访问雨落主页')

start_time = time.time()
index()
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')

# 输出结果:
欢迎访问雨落主页
此函数的执行效率为2.0052030086517334

版本1分析:你现在已经完成了这个需求,但是问题是虽然你只写了四行代码,但是你完成的是一个测试其他函数的执行效率的功能,如果让你测试一下庆言的函数效率呢? 你是不是全得复制一遍,例如:

import time
def index1():
    time.sleep(2)   # 模拟一下网络延迟以及代码的效率
    print('欢迎访问雨落首页')

def index2(name):
    time.sleep(3)   # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')

start_time = time.time()
index1()
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')

start_time = time.time()
index2('庆言')
end_time = time.time()
print(f'此函数的执行效率为{end_time-start_time}')

# 输出结果:
欢迎访问雨落首页
此函数的执行效率为2.0052318572998047
欢迎访问庆言主页
此函数的执行效率为3.002941131591797

重复代码太多了,所以要想解决重复代码的问题,可以利用一下函数,函数就是以功能为导向,减少重复代码,我们继续完善代码

版本2:

import time

def index():
    time.sleep(2)   # 模拟一下网络延迟以及代码的效率
    print('欢迎访问雨落主页')

def inner():
    start_time = time.time()
    index()
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')

inner()

# 输出结果:
欢迎访问雨落主页
此函数的执行效率为2.005192279815674

但是你这样写也是有问题的,你虽然将测试功能的代码封装成了一个函数,但是这样,你只能测试你自己的的函数index,你要是测试庆言等其他人的函数呢

import time
def index1():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('欢迎访问博客园主页')

def index2(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')

def inner():
    start_time = time.time()
    index1()
    index2('庆言')
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')

inner()

# 输出结果:
欢迎访问博客园主页
欢迎访问庆言主页
此函数的执行效率为5.008612871170044

你要是像上面那么做,每次测试其他同事的代码还需要手动改,所以如何变成动态测试其他函数,我们学过函数的传参,能否将被装饰函数的函数名作为函数的参数传递进去呢

版本3:

import time
def index1():
    time.sleep(2)
    print('欢迎访问雨落主页')

def index2(name):
    time.sleep(3)
    print(f'欢迎访问{name}主页')

def timmer(func):   # 第2步、此时func相当于index函数
    start_time = time.time()
    func()          # 第3步、执行func(),相当于执行index()
    end_time = time.time()
    print(f'此函数的执行效率为{end_time-start_time}')

timmer(index1)       # 第1步、执行timmer()函数,里面的index被传给func

# 输出结果:
欢迎访问雨落主页
此函数的执行效率为2.0050861835479736

这样我将index1函数的函数名作为参数传递给timmer函数,然后在timmer函数里面执行index1函数,这样就变成动态传参了。对比着开放封闭原则说: 首先,index1函数除了完成了自己之前的功能,还增加了一个测试执行效率的功能,符合开放原则。 其次,index1函数源码没有改变,但是执行方式改变了,所以不符合封闭原则。 原来如何执行,index1() 现在如何执行,timmer(index1)这样会造成什么问题?假如index1在你的项目中被100处调用,那么这相应的100处调用我都得改成timmer(index),非常麻烦,且不符合开放封闭原则

版本4:实现真正的开放封闭原则:装饰器

import time
def index1():                     # 第8步,执行该程序
    time.sleep(2)
    print('欢迎访问雨落主页')        # 第9步,打印

def index2(name):
    time.sleep(3)
    print(f'欢迎访问{name}主页')

def timer(func):                  # 第2步,执行该程序,此时 func = index1
    def inner():                  # 第5步,执行该程序
        start_time = time.time()  # 第6步,记录开始时间
        func()                    # 第7步,执行index1()
        end_time = time.time()    # 第10步,记录结束时间
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner                  # 第3步,将inner()返回给函数调用者f

f = timer(index1)                 # 第1步,程序到此,执行timer函数
f()                               # 第4步,执行f(),也就是inner()

# 输出结果:
欢迎访问雨落主页
此函数的执行效率为2.005129098892212

将上面的inner函数在套一层最外面的函数timer,然后将里面的inner函数名作为最外面的函数的返回值,这样简单的装饰器就写好了

代码执行到这一行:f = timer(index) 先执行 timer(index) 执行timer函数,将index函数名传给了func形参。内层函数inner不执行,inner函数返回给f变量。所以我们执行f() 就相当于执行inner闭包函数

14.3 带返回值的装饰器

现在这个代码,完成了最初版的装饰器,但是还是不够完善,因为你被装饰的函数index可能会有返回值,如果有返回值,你的装饰器也应该不影响,但是现在:

import time
def index():
    time.sleep(2)
    print('欢迎访问雨落主页')
    return '访问成功'

def timer(func):
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

index = timer(index)
print(index())

# 输出结果:
欢迎访问雨落主页
此函数的执行效率为2.005070924758911
None

加上装饰器之后,他的返回值为None,这是因为现在的index不是函数名index,这index实际是inner函数名。所以index() 等同于inner() 你的 '访问成功'返回值应该返回给谁,应该返回给index,这样才做到开放封闭,实际返回给了func,所以你要更改一下你的装饰器代码,让其返回给外面的index函数名,所以:你应该这么做:

import time
def index():
    time.sleep(2)
    print('欢迎访问雨落主页')
    return '访问成功'

def timer(func):
    def inner():
        start_time = time.time()
        ret = func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
        return ret
    return inner

index = timer(index)
print(index())          # 等同于print(inner())

# 输出结果:
欢迎访问雨落主页
此函数的执行效率为2.0050370693206787
访问成功

借助于内层函数inner,你将func的返回值,返回给了inner函数的调用者也就是函数外面的index,这样就实现了开放封闭原则,index返回值,确实返回给了'index'

14.4 被装饰函数带参数的装饰器

到目前为止,你的被装饰函数还是没有传参呢?按照我们的开放封闭原则,加不加装饰器都不能影响你被装饰函数的使用

import time
def index1():
    time.sleep(2)
    print('欢迎访问雨落主页')
    return '访问成功'

def index2(name):
    time.sleep(3)
    print(f'欢迎访问{name}主页')

def timer(func):
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

# 要想timer装饰index2函数怎么做?
index2 = timer(index2)
index2('庆言')

# 输出结果:
报错

上面那么做,显然报错了,为什么? 你的index2这个变量是谁?是inner,index2('庆言')实际是inner('庆言'),但是你的'庆言'这个实参应该传给index2函数,实际却传给了inner,所以我们要通过更改装饰器的代码,让其将实参'庆言'传给index2

import time
def index1():
    time.sleep(2)
    print('欢迎访问雨落主页')
    return '访问成功'

def index2(name):
    time.sleep(3)
    print(f'欢迎访问{name}主页')

def timer(func):             # func = index2
    def inner(name):
        start_time = time.time()
        func(name)           # index2(name) == index2('庆言')
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

# 要想timer装饰index2函数怎么做?
index2 = timer(index2)
index2('庆言')

# 输出结果:
欢迎访问庆言主页
此函数的执行效率为3.005120038986206

这样就实现了,不过现在被装饰函数的形参只是有一个形参,如果要是多个,且可以接受不定数参数的形参接受他们,这样就要想到*args,**kwargs

import time
def index1():
    time.sleep(2)
    print('欢迎访问博客园主页')
    return '访问成功'

def index2(name,age):
    time.sleep(3)
    print(name,age)
    print(f'欢迎访问{name}主页')

def timer(func):                   # func = index2
    def inner(*args,**kwargs):     # 函数定义时,*代表聚合
        start_time = time.time()
        func(*args,**kwargs)       # 函数的执行时,*代表打散
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

index2 = timer(index2)
index2('庆言',18)

# 输出结果:
庆言 18
欢迎访问庆言主页
此函数的执行效率为3.005236864089966

14.5 标准版装饰器

刚刚我们已经知道一个装饰器是可以这样写的:

def timer(func):                   # func = index2
    def inner(*args,**kwargs):     # 函数定义时,*代表聚合
        start_time = time.time()
        func(*args,**kwargs)       # 函数的执行时,*代表打散
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner

index2 = timer(index2)
index2('庆言',18)

每次执行index2之前你要写上一句:index2 = timer(index2),这样是比较麻烦的,Python给我们提供了一个简化机制,用一个很简单的符号去代替这一句话:

--------- 装饰器在上 ---------

def timer(func):      # func = home
    def inner(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner


--------- 函数在下 ---------

@timer                # 作用等同于index2 = timer(index2)
def index2(name,age):
    time.sleep(3)
    print(name,age)
    print(f'欢迎访问{name}主页')

index2('庆言',18)

至此标准版的装饰器就是这个样子:

def wrapper(func):
    def inner(*args,**kwargs):
        '''执行被装饰函数之前的操作'''
        ret = func
        '''执行被装饰函数之后的操作'''
        return ret
    return inner

14.6 带参数的装饰器

装饰器其实就是一个闭包函数,再说简单点就是两层的函数。那么是函数,就应该具有函数传参功能

login_status = {
    'username': None,
    'status': False,}

def auth(func):
    def inner(*args,**kwargs):
        if login_status['status']:
            ret = func()
            return ret
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()
        if username == '雨落' and password == '123':
            login_status['status'] = True
            ret = func()
            return ret
    return inner

上面的装饰器,不要打开,他可以不可以再套一层

login_status = {
    'username': None,
    'status': False,}

def auth(x):
    def auth2(func):
        def inner(*args,**kwargs):
            if login_status['status']:
                ret = func()
                return ret
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            if username == '雨落' and password == '123':
                login_status['status'] = True
                ret = func()
                return ret
        return inner
    return auth

举例说明:雨落:绑定的是微信账号密码,庆言:绑定的是qq的账号密码;现在要完成的就是该装饰器要分情况去判断账号和密码,不同的函数用的账号和密码来源不同。但是你之前写的装饰器只能接受一个参数就是函数名,所以你写一个可以接受参数的装饰器

login_status = {
    'username': None,
    'status': False, }
def auth2(func):
    def inner(*args, **kwargs):
        if login_status['status']:
            ret = func()
            return ret
        if 'WeChat':
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            if username == 'yuluo' and password == '123':
                login_status['status'] = True
                ret = func()
                return ret
        elif 'QQ':
            username = input('请输入用户名:').strip()
            password = input('请输入密码:').strip()
            if username == 'qingyan' and password == '123':
                login_status['status'] = True
                ret = func()
                return ret
    return inner

@auth2
def YL():
    print('欢迎来到雨落的网站')

@auth2
def QY():
    print('欢迎来到庆言的网站')

解决方式如下:

login_status = {
    'username': None,
    'status': False, }

def auth(x):
    def auth2(func):
        def inner(*args, **kwargs):
            if login_status['status']:
                ret = func()
                return ret
            
            if x == 'WeChat':
                username = input('请输入用户名:').strip()
                password = input('请输入密码:').strip()
                if username == 'yuluo' and password == '123':
                    login_status['status'] = True
                    ret = func()
                    return ret
            elif x == 'QQ':
                username = input('请输入用户名:').strip()
                password = input('请输入密码:').strip()
                if username == 'qingyan' and password == '123':
                    login_status['status'] = True
                    ret = func()
                    return ret
        return inner
    return auth2

@auth('WeChat')
def YL():
    print('欢迎来到雨落的网站')

@auth('QQ')
def QY():
    print('欢迎来到庆言的网站')

注释:@auth('WeChat')分两步:

  • 第一步先执行@auth('WeChat')函数,得到返回值auth2
  • 第二步@与auth2结合,形成装饰器@auth2然后在依次执行

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

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

相关文章

SpringBoot源码搭建

文章目录 源码下载搭建项目构建学习博客 源码下载 需要环境 &#xff1a; JDK 1.8Maven 3.5Spring Boot 1.x.x: Gradle 版本建议为2.9或更高版本。Spring Boot 2.x.x: Gradle 版本建议为4.x.x或更高版本。 GitHub 从v2.3.x开始&#xff0c;SpringBoot开始强制用Gradle构建项…

深度解析:CRM、ERP之间的关联和区别以及双系统的联系与集成(附系统架构图)

目录 一、系统简介 1.1 CRM是什么 1.2 ERP是什么 二、发展阶段 2.1 CRM发展阶段 2.2 ERP发展阶段 三、系统架构 3.1 CRM系统架构 3.2 ERP系统架构 四、CRM与ERP的区别 4.1 目标不同 4.2 不同阶段的发展中不同的适用性 4.2.1 刚起步的小公司 4.2.2 对于更大、更成…

数字电子技术 一天速成

文章目录 一、数制与编码1. 数制转换2. BCD编码 二、逻辑代数1. 常见逻辑运算及逻辑门 三、化简逻辑表达式1. 卡诺图 求 表达式2. 表达式 画 卡诺图3. 卡诺图 化简 表达式4. 公式法 化简 表达式 ⭐⭐5. 表达式 求 反函数6. 卡诺图 求 反函数 四、组合逻辑电路的分析和设计1. 逻…

Gin框架之使用 go-ini 加载.ini 配置文件

首先,联想一个问题,我们在部署服务时,通常为了方便,对于需要迭代更新的代码进行修改,但是比对shell,可以搞一个变量将需要修改的,以及修改起来变动处多的,写在变量内,到时候如果需要变更,可以直接变更变量即可; 那么,golang有没有什么方式可以将需要变的东西保存起…

Hexo 部署 Github Pages, Github Actions自动部署

想整个静态的博客部署在github pages 历经两天的折磨终于是摸索成功了&#xff0c;官网的文档太简陋了&#xff0c;很多东西没说清楚。 欢迎大家访问我的博客&#xff01; Canyue 最终实现的效果&#xff0c;一个项目仓库&#xff0c;main 分支存放源代码&#xff0c;gh-page…

H266/VVC帧间预测编码技术概述

帧间预测编码简述 帧间预测利用视频时间域的相关性&#xff0c;使用邻近已编码图像像素值预测当前图像的像素值&#xff0c;能有效去除视频时域冗余。 目前主要的视频编码标准中&#xff0c;帧间预测都采用基于块的运动补偿技术&#xff0c;不同的编码标准有不同的分块方式。 …

Spring企业开发核心框架

文章目录 Spring企业开发核心框架一、框架前言1. 总体技术体系2. 框架概念和理解 二、Spring Framework简介1. Spring 和 SpringFramework2. SpringFramework主要功能模块3. SpringFramework 主要优势 三、Spring IoC 容器概念1. 组件和组件管理概念2. Spring IoC容器和容器实现…

C# LINQ

一、前言 学习心得&#xff1a;C# 入门经典第8版书中的第22章《LINQ》 二、LINQ to XML 我们可以通过LINQ to XML来创造xml文件 如下示例&#xff0c;我们用LINQ to XML来创造。 <Books><CSharp Time"2019"><book>C# 入门经典</book><…

上海亚商投顾:沪指冲高回落 游戏股午后集体重挫

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数12月22日冲高回落&#xff0c;黄白二线分化严重。游戏股午后大跌&#xff0c;盛天网络、游族网络、巨…

【JAVA】黑马MybatisPlus 学习笔记【终】【插件功能】

4.插件功能 MybatisPlus提供了很多的插件功能&#xff0c;进一步拓展其功能。目前已有的插件有&#xff1a; PaginationInnerInterceptor&#xff1a;自动分页TenantLineInnerInterceptor&#xff1a;多租户DynamicTableNameInnerInterceptor&#xff1a;动态表名OptimisticL…

bootstrap:bootstrapValidator校验数据是否可用(验证账户名是否重复)

目录 1、html内容 2、bootstrap的校验 3、控制层代码&#xff1a; 4、业务层核心代码 5、效果 1、html内容 <form id"jangleEditForm" name"jangleEditForm" class"formJ" ><div class"form-group" ><label for&q…

10个练习Web渗透测试的最佳网站

黑客的最高境界——社会工程学&#xff01;社会工程在网络安全领域充当了关键角色&#xff01;黑客技术如何操纵信息安全&#xff1f;社会工程攻击的多种形式&#xff0c;包括网络钓鱼、电子邮件欺诈、诱饵场景&#xff1b;如何应对黑客利用未提出的问题的策略。防范黑客社会工…

二叉树OJ题——3.翻转二叉树

226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 翻转每一棵树的左右子树根节点 */void swap (struct TreeNode**x,struct TreeNode**y) {struct TreeNode*num0;num*x;*x*y;*ynum; }struct TreeNode* invertTree(struct TreeNode* root) { i…

【广州华锐互动】VR科技科普展厅平台:快速、便捷地创建出属于自己的虚拟展馆

随着科技的不断进步&#xff0c;虚拟现实(VR)技术已经在许多领域取得了显著的成果。尤其是在展馆设计领域&#xff0c;VR科技科普展厅平台已经实现了许多令人瞩目的新突破。 VR科技科普展厅平台是广州华锐互动专门为企业和机构提供虚拟展馆设计和制作的在线平台。通过这个平台&…

二分查找——OJ题(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、点名1、题目讲解2、算法原理3、代码实现 二、搜索旋转排序数组中的最⼩值1、题目讲解2、算…

LabVIEW在齿轮箱故障诊断中的应用

LabVIEW在齿轮箱故障诊断中的应用 在现代机械工业中&#xff0c;齿轮箱作为重要的传动设备&#xff0c;其性能稳定性对整体机械系统的运行至关重要。故障的及时诊断和处理不仅保障了设备的稳定运行&#xff0c;还减少了维护成本。利用LabVIEW强大数据处理和仿真能力&#xff0…

在k8s中将gitlab-runner的运行pod调度到指定节点

本篇和前面的 基于helm的方式在k8s集群中部署gitlab 具有很强的关联性&#xff0c;因此如果有不明白的地方可以查看往期分享&#xff1a; 基于helm的方式在k8s集群中部署gitlab - 部署基于helm的方式在k8s集群中部署gitlab - 备份恢复基于helm的方式在k8s集群中部署gitlab - 升…

(11)Linux 进程以及进程控制块PCB

前言&#xff1a;章我们将带着大家深入理解 "进程" 的概念&#xff0c;"进程" 这个概念其实使我们一直在接触&#xff0c;只不过这个概念我们没有进行详细讲解罢了&#xff0c;本章我们就把 "进程" 好好地深入理解一番&#xff01;引出进程的概念…

C语言结构体内存对齐

文章目录 一、结构体内存对齐问题二、查看结构体成员起始位置三、设置内存对齐方式 一、结构体内存对齐问题 如下的info_s结构体类型&#xff0c;包含一个int型成员age, 一个char型成员gender, 一个int型成员id。 单从数据成员的大小进行分析&#xff0c;整个结构体的大小应为…

Spring Boot国际化i18n配置指南

Spring Boot国际化i18n配置指南 一、配置 1、yml文件配置 spring:messages:basename: i18n/Messages,i18n/Messages_en_US,i18n/Messages_zh_CNencoding: UTF-8i18n&#xff1a;表示国际化文件的父目录 Messages&#xff1a;默认国际化文件 Messages_en_US&#xff1a;英文文…