Python高级系列教程:Python闭包和装饰器

news2024/11/16 11:28:58

今天我们将继续讲解 Python 中的闭包和装饰器。虽然我们还没有详细学习这两个概念,但在面向对象编程中,我们已经经常使用装饰器了。装饰器可以给函数增加额外的功能,就像语法糖一样甜。在 Python 中,装饰器的格式通常是在函数上方加上 @ 符号。

下面看一下本章的学习目标:

  • 能够知道闭包的构成条件

  • 能够知道定义闭包的基本语法

  • 能够知道修改闭包内使用的外部变量所需要的关键字

  • 能够知道定义装饰器的基本语法及其作用

  • 能够写出通用的装饰器

  • 能够使用多个装饰器装饰一个函数

  • 能够写出带有参数的装饰器

  • 能够知道类装饰器的使用

闭包

下面我们将向大家介绍闭包的概念。在此之前,我们需要先解释一下变量在内存中的存储方式。如我们之前所述,Python代码中的变量分为两种类型:全局变量和局部变量。闭包与局部变量有关。接下来,我们将详细讲解闭包。

变量在内存中的存储形式

前言:全局变量与局部变量的访问

全局变量的访问范围:

①在全局作用域中可以正常访问全局变量

②在局部作用域中也可以正常访问全局变量

局部变量的访问范围:

①在局部作用域中可以正常访问局部变量

②在全局作用域中无法正常访问局部变量,因为函数执行完毕后,其内部的变量会随之消失。 在代码中展示如下:

 
 

'''
作用域就是变量的作用范围,在哪里可以使用,在哪里不可以使用。
Python中变量随着函数的出现,作用域可以划分两种情况:① 全局作用域 ② 局部作用域
随着作用域的出现,变量也被强制划分两种类型:① 全局变量 ② 局部变量
① 全局变量:在全局作用域中定义的变量就是全局变量
② 局部变量:在局部作用域中定义的变量就是局部变量
'''
# 全局作用域
num1 = 10 # 全局变量
def func():
# 局部作用域
num2 = 100 # 局部变量

扩展:为什么函数执行完毕,其内部的变量会随之消失呢?答:内存的垃圾回收机制

1、闭包的作用

我们前面已经学过了函数,我们知道当函数调用完,函数内定义的变量都销毁了,但是我们有时候需要保存函数内的这个变量,每次在这个变量的基础上完成一些列的操作,比如: 每次在这个变量的基础上和其它数字进行求和计算,那怎么办呢? 

我们就可以通过咱们今天学习的闭包来解决这个需求。 

闭包的定义: 

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数的地址,我们把这个使用外部函数变量的内部函数称为闭包。

2、闭包的构成条件

通过闭包的定义,我们可以得知闭包的形成条件: 

①在函数嵌套(函数里面再定义函数)的前提下 

②内部函数使用了外部函数的变量(还包括外部函数的参数) 

③外部函数返回了内部函数

3、闭包的示例代码

 
 

# 定义一个外部函数
def func_out(num1):
# 定义一个内部函数
def func_inner(num2):
# 内部函数使用了外部函数的变量(num1)
result = num1 + num2
print("结果是:", result)
# 外部函数返回了内部函数,这里返回的内部函数就是闭包
return func_inner
# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)
f(3)

4、闭包的作用

闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。 注意点: 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

5、小结

①当返回的内部函数使用了外部函数的变量就形成了闭包 

②闭包可以对外部函数的变量进行保存 

③实现闭包的标准格式:

 
 

# 外部函数
def test1(a):
b = 10
# 内部函数
def test2():
# 内部函数使用了外部函数的变量或者参数
print(a, b)
# 返回内部函数, 这里返回的内部函数就是闭包实例
return test2

修改闭包内使用的外部变量

1、修改闭包内使用的外部变量

错误版本演示:

 
 

def outer():
# outer函数中的局部变量
num = 10
def inner():
num = 20
print(f'原始变量:{num}') # 10
inner()
print(f'修改后的变量:{num}') # 10
return inner

f = outer()

这段代码定义了一个函数outer,它包含了另一个函数inner。在outer函数中,定义了一个局部变量num并赋值为10。在inner函数中,又定义了一个同名的局部变量num并赋值为20。但是由于Python的作用域规则,inner函数中的num只是outer函数中的num的一个新的局部变量,两者并不相互影响。因此,调用inner函数后,outer函数中的num仍然是10。最后,outer函数返回inner函数的引用,并将其赋值给变量f。这样,变量f就可以调用inner函数了。

2、修改闭包内使用的外部变量

正确版本演示:

 
 

def outer():
# outer函数中的局部变量
num = 10 def inner():
nonlocal num
# inner函数中的局部变量
num = 20
print(f'原始变量:{num}') # 10 inner()
print(f'修改后的变量:{num}') # 20
return inner
f = outer()

这段代码定义了一个函数outer,其中包含一个局部变量num,初始值为10。另外,函数outer还定义了一个内部函数inner。在inner函数中,使用nonlocal关键字声明了num变量是outer函数中的局部变量,而不是inner函数的局部变量。然后,将num的值修改为20,并输出修改前后的值。最后,outer函数返回inner函数的引用,并将其赋值给变量f。这样,变量f就成为了一个指向inner函数的函数对象。

3、闭包综合案例

 
 

def outer():
result = 0
def inner(num):
nonlocal result
result += num
print(result)
return inner
f = outer()
f(1)
f(2)
f(3) # 问这次的执行结果是多少?

这段代码定义了一个函数outer(),它返回了内部函数inner(),并且inner()可以访问outer()函数内的变量result。 

在函数outer()中,我们定义了一个变量result并将其初始化为0。接着,我们定义了内部函数inner(),它带有一个参数num。在inner()函数内部,我们使用nonlocal关键字来声明result变量是来自outer()函数的变量,并将其与num相加,并在每次加完之后打印result的值。最后, 我们返回内部函数inner()。 

接下来,我们在函数调用时使用f = outer()将inner()函数赋值给了变量f。这意味着我们可以在后续代码中使用f()函数,来调用我们定义的内部函数。 

我们接着多次调用了f()函数,每一次调用都会调用内部函数并将不同的参数传递给它。由于在内部函数中,我们使用了非局部变量result来记录之前的调用结果,因此在第三次调用时,函数会打印出结果为6的结果。所以,这段代码的输出为: 

6

装饰器

1、装饰器的定义

就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。 装饰器的功能特点: 

①不修改已有函数的源代码 

②不修改已有函数的调用方式 

③给已有函数增加额外的功能

2、装饰器代码

示例代码:

 
 

# 添加一个登录验证的功能
def check(fn):
def inner():
print("请先登录....")
fn()
return inner


def comment():
print("发表评论")

# 使用装饰器来装饰函数
comment = check(comment)
comment()

这段代码定义了一个装饰器函数check,它接受一个函数作为参数,并返回一个新的函数inner。新的函数inner在执行原函数fn之前,会先打印一条“请先登录”的提示信息。 

接下来,定义了一个函数comment,它用于发表评论。最后,使用装饰器来装饰函数comment,即将函数comment传入check函数中,将返回的新函数inner赋值给comment。 

最后一行代码调用了被装饰后的函数comment,会先执行inner函数中的提示信息,再执行原函数comment中的发表评论的功能。这样就实现了一个简单的登录验证功能。

 
 

# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
# def inner():
# '''执行函数之前'''
# fn() # 执行被装饰的函数
# '''执行函数之后'''
# return inner

这段代码是一个装饰器的基本雏形,它定义了一个名为 decorator 的函数,该函数接受一个参数 fn,即目标函数。decorator 函数内部定义了一个名为 inner 的函数,该函数在执行目标函数之前和之后分别执行一些操作。最后,decorator 函数返回 inner 函数,从而实现对目标函数的装饰。具体来说,当我们在定义一个函数时,可以使用 @decorator 的语法来将 decorator 函数应用到目标函数上,从而实现对目标函数的装饰。

3、装饰器的语法糖写法

  • 如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

  • Python给提供了一个装饰函数更加简单的写法,那就是语法糖,语法糖的书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

 
 

# 添加一个登录验证的功能
def check(fn):
print("装饰器函数执行了")
def inner():
print("请先登录....")
fn()
return inner

转换

 
 

# 使用语法糖方式来装饰函数
@check
def comment():
print("发表评论")

comment()

装饰器的作用

1、装饰器的使用场景

  1. 统计程序的执行时间:可以通过编写一个装饰器来记录函数的开始时间和结束时间,然后计算函数执行的时间。这个装饰器可以被用于优化程序性能或者进行调试。

  2. 辅助系统功能输出日志信息:可以编写一个装饰器来记录函数的执行过程以及输出日志信息,这个装饰器可以被用于调试、监控和错误处理等方面。

2、装饰器实现已有函数执行时间的统计

 
 

import time
def get_time(func):
def inner():
# 开始计时
begin = time.time()
func()
# 结束计时
end = time.time()
print("函数执行花费%f" % (end - begin))

return inner

这段代码定义了一个装饰器函数get_time,接受一个函数作为参数。装饰器函数内部定义了一个嵌套函数inner,它用于包装传入的函数func。inner函数在调用func之前和之后记录了时间,并计算出函数执行的时间,最后输出函数执行花费的时间。

装饰器函数get_time返回了inner函数,即返回了一个新的函数,这个新函数的功能是在原函数执行前后记录时间并输出执行时间。使用装饰器@get_time可以将这个装饰器应用于需要计时的函数上,从而方便地获得函数执行的时间。 转换为

 
 

@get_time
def demo():
for i in range(100000):
print(i)

demo()

这段代码定义了一个装饰器函数 @get_time,并将其应用于函数 demo()  上。装饰器函数 @get_time 的作用是在函数 demo() 执行前和执行后分别记录当前时间,并计算两次时间差,最后将时间差输出。 

函数 demo() 中包含一个循环,循环次数为 100000,每次循环输出一个数值。当调用 demo() 函数时,它会执行循环并输出数值,同时也会触发装饰器函数 @get_time 的执行,从而输出函数执行的时间。

通用装饰器的使用

 

1、装饰器的使用场景

  1. 统计程序的执行时间:可以通过编写一个装饰器来记录函数的开始时间和结束时间,然后计算函数执行的时间。这个装饰器可以被用于优化程序性能或者进行调试。

  2. 辅助系统功能输出日志信息:可以编写一个装饰器来记录函数的执行过程以及输出日志信息,这个装饰器可以被用于调试、监控和错误处理等方面。

2、装饰器实现已有函数执行时间的统计

 
 

import time
def get_time(func):
def inner():
# 开始计时
begin = time.time()
func()
# 结束计时
end = time.time()
print("函数执行花费%f" % (end - begin))

return inner

这段代码定义了一个装饰器函数get_time,接受一个函数作为参数。装饰器函数内部定义了一个嵌套函数inner,它用于包装传入的函数func。inner函数在调用func之前和之后记录了时间,并计算出函数执行的时间,最后输出函数执行花费的时间。

装饰器函数get_time返回了inner函数,即返回了一个新的函数,这个新函数的功能是在原函数执行前后记录时间并输出执行时间。使用装饰器@get_time可以将这个装饰器应用于需要计时的函数上,从而方便地获得函数执行的时间。 转换为

 
 

@get_time
def demo():
for i in range(100000):
print(i)

demo()

这段代码定义了一个装饰器函数 @get_time,并将其应用于函数 demo()  上。装饰器函数 @get_time 的作用是在函数 demo() 执行前和执行后分别记录当前时间,并计算两次时间差,最后将时间差输出。 

函数 demo() 中包含一个循环,循环次数为 100000,每次循环输出一个数值。当调用 demo() 函数时,它会执行循环并输出数值,同时也会触发装饰器函数 @get_time 的执行,从而输出函数执行的时间。

通用装饰器的使用

1、装饰带有参数的函数

 
 

def logging(fn):
def inner(num1, num2):
print('--正在努力计算--')
fn(num1, num2)
return inner

@logging
def sum_num(num1, num2):
result = num1 + num2
print(result)

sum_num(10, 20)

这段代码定义了一个装饰器函数logging,它接受一个函数fn作为参数。logging返回一个内部函数inner,inner接受两个参数num1和num2,并在调用fn之前打印一条信息。最后,logging将inner函数返回。

@logging是一个装饰器语法,它可以将装饰器函数logging应用到下面的函数sum_num上。这意味着sum_num将被重新定义为inner函数,即调用sum_num实际上是调用inner函数。 

在sum_num函数内部,它计算num1和num2的和,并将结果打印出来。由于sum_num被logging装饰器修饰,所以在调用sum_num之前会先打印一条信息。最后,调用sum_num(10, 20)将会输出以下内容: 

--正在努力计算-- 

30

2、装饰带有返回值的函数

 
 

def logging(fn):
def inner(num1, num2):
print('--正在努力计算--')
result = fn(num1, num2)
return result
return inner

@logging
def sum_num(num1, num2):
result = num1 + num2
return result

print(sum_num(10, 20))

这段代码定义了一个装饰器函数logging,它接受一个函数作为参数,并返回一个新的函数inner。inner函数在执行原函数之前会打印一条信息“--正在努力计算--”,然后执行原函数,最后返回原函数的返回值。 

接下来使用@logging装饰器来修饰函数sum_num,相当于将sum_num函数传递给logging函数,并将返回的新函数重新赋值给sum_num。这样,当调用sum_num函数时,实际上执行的是inner函数。 

最后,调用sum_num函数并传入参数10和20,输出结果为30。同时,也会打印出"--正在努力计算--"这条信息。

3、装饰带有不定长参数的函数

 
 

def logging(fn):
def inner(*args, **kwargs):
print('--正在努力计算--')
fn(*args, **kwargs)
return inner

这段代码定义了一个装饰器函数 logging,它接受一个函数 fn 作为参数,并返回一个函数 inner。 

函数 inner 接受任意数量的位置参数和关键字参数,并在执行 fn 函数之前打印一条信息 "--正在努力计算--"。然后,函数 inner 调用 fn 函数并传入相同的参数。 

这个装饰器函数可以用来给其他函数添加日志信息,以便在函数执行时打印一些有用的信息,比如函数开始执行、执行结束等。

 
 

@logging
def sum_num(*args, **kwargs):
result = 0
for i in args:
result += i

for i in kwargs.values():
result += i
print(result)

sum_num(10, 20, a=30)

这段代码定义了一个函数sum_num(),它将任意数量的位置参数和关键字参数作为输入,并将它们加起来。函数内部首先将所有位置参数相加,接着将所有关键字参数的值相加,最后将两者之和打印出来。 

代码中的装饰器@logging表示在函数执行时会调用一个名为logging的函数(这个函数并没有给出),它可能会记录函数的执行情况(比如开始时间、结束时间、参数值等等)。这样,我们就可以在调用函数时进行记录和日志的输出,从而方便我们进行错误调试和问题排查。 

最后,我们对函数进行了一次调用sum_num(10, 20, a=30),输出结果为60。这是由于10与20是位置参数,相加为30,而a=30是一个关键字参数,值为30,所以总和为60。 

运行结果:

4、通用装饰器

 
 

# 添加输出日志的功能
def logging(fn):
def inner(*args, **kwargs):
print("--正在努力计算--")
result = fn(*args, **kwargs)
return result

return inner

这段代码定义了一个装饰器函数 logging,它接受一个函数 fn 作为参数,并返回一个新函数 inner。 

新函数 inner 接受任意数量的位置参数和关键字参数,并在函数执行前输出一条日志,表示正在计算中。然后调用原始函数 fn 并返回其结果。 

这个装饰器函数可以应用于其他函数,以添加输出日志的功能,使每次函数执行时都会输出一条日志。例如:

 
 

@logging
def add(a, b):
return a + b

当调用 add(2, 3) 时,会输出 "--正在努力计算--",然后返回 5。

 
 

# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
result = 0
for value in args:
result += value

for value in kwargs.values():
result += value

return result


@logging
def subtraction(a, b):
result = a - b
print(result)

这段代码定义了两个函数,使用装饰器@logging对这两个函数进行装饰。

装饰器@logging是一个函数,用于装饰其他函数。它的作用是在被装饰的函数执行前和执行后打印一些日志信息,以便于调试和追踪代码执行情况。 

函数sum_num(args, *kwargs)是一个可变参数函数,用于计算传入的所有参数的和。它的参数列表中包括一个可变位置参数args和一个可变关键字参数kwargs。函数体中使用循环遍历args和kwargs.values(),将它们的值相加,并返回结果。 

函数subtraction(a, b)是一个简单的函数,用于计算a和b的差,并打印结果。它的函数体中只有一行代码。 

因为这两个函数都使用了装饰器@logging进行装饰,所以在它们执行前和执行后都会打印日志信息。这些日志信息包括函数名、参数列表和返回值等。这样可以方便地追踪函数的执行情况和调试代码。

 
 

result = sum_num(1, 2, a=10)
print(result)

subtraction(4, 2)

这段代码定义了两个函数sum_num()和subtraction()并调用它们。下面是对这些代码的详细解释: 

第一部分:定义函数 

1. sum_num(1, 2, a=10):定义了名为sum_num()的函数,该函数接受两个位置参数和一个名为a的关键字参数。函数的作用是返回两个位置参数的和再加上a的值。 

2. subtraction(4, 2):定义了名为subtraction()的函数,该函数接收两个位置参数,返回两个参数的差。 

第二部分:调用函数 

1. result = sum_num(1, 2, a=10):调用sum_num()函数,并将参数1和2传递给它作为位置参数,将10传递给它作为关键字参数a的值。函数执行后,返回1 + 2 + 10 = 13,然后将这个结果赋值给result变量。 

2. print(result):打印result变量的值,即13。 

3. subtraction(4, 2):调用subtraction()函数,并将参数4和2传递给它作为位置参数。函数执行后,返回4 - 2 = 2,但是这个结果没有被存储在任何变量中,也没有被打印出来。

带有参数的装饰器介绍

1、带有参数的装饰器介绍

带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,...)

错误演示

 
 

def decorator(fn, flag):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
return inner

这是一个装饰器函数,接受两个参数:一个函数和一个标志。返回一个内部函数 inner。 

inner 函数根据标志 flag 的不同,输出不同的提示信息。然后调用传入的函数 fn,传入 num1 和 num2 两个参数,并返回结果。最终将结果返回给调用者。 

装饰器函数的作用是在不改变原函数代码的前提下,给函数添加额外的功能。在这个例子中,装饰器函数可以为加法和减法函数添加提示信息,让程序更加友好。

 
 

@decorator('+')
def add(a, b):
result = a + b
return result

result = add(1, 3)
print(result)

这是一个装饰器函数。装饰器函数可以添加功能或修改函数的行为,而无需更改源代码。在这里,装饰器函数用符号 '+' 将两个数相加,并返回计算结果。 

该装饰器函数被应用在 add(a, b) 函数上,它将两个参数相加并返回结果。因此,在调用 add(a, b) 函数时,实际上执行了装饰器函数中的逻辑。该函数将两个参数相加,并返回计算结果。 

最后,实例化 add(1, 3) 函数,并将结果(4)打印出来。 执行结果:

 
 

Traceback (most recent call last):
File "/home/python/Desktop/test/hho.py", line 12, in <module>
@decorator('+')
TypeError: decorator() missing 1 required positional argument: 'flag'

2、正确语法

在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。

 
 

# 添加输出日志的功能
def logging(flag):

def decorator(fn):
def inner(num1, num2):
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
return inner

# 返回装饰器
return decorator

这段代码定义了一个装饰器函数 logging,它接受一个字符串参数 flag,并返回一个装饰器函数 decorator。 

decorator  函数接受一个函数 fn 作为参数,它定义了一个内部函数 inner,该函数接受两个参数 num1 和 num2。在 inner 函数中,根据传入的 flag  值输出相应的日志信息,然后调用原始函数 fn 并返回其结果。 

最后,decorator 函数返回 inner 函数作为装饰器,并将其应用于被装饰的函数。这样,当被装饰的函数被调用时,会先输出相应的日志信息,然后再执行原始函数。

 
 

# 使用装饰器装饰函数
@logging("+")
def add(a, b):
result = a + b
return result

@logging("-")
def sub(a, b):
result = a - b
return result

result = add(1, 2)
print(result)
result = sub(1, 2)
print(result)

这段代码定义了两个函数add和sub,分别实现加法和减法运算。同时,使用装饰器@logging对这两个函数进行装饰,装饰器函数logging会在函数执行前和执行后打印日志信息,其中使用"+"和"-"作为日志信息的前缀。最后,分别调用add和sub函数,输出它们的返回结果。

类装饰器使用

1、类装饰器的介绍

装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。

 
 

class Check(object):
def __init__(self, fn):
# 初始化操作在此完成
self.__fn = fn

# 实现__call__方法,表示把类像调用函数一样进行调用。
def __call__(self, *args, **kwargs):
# 添加装饰功能
print("请先登陆...")
self.__fn()

这段代码定义了一个装饰器类 Check,用于在被装饰的函数执行前进行一些操作,例如检查用户是否已经登录。 

在类的初始化方法 init 中,将被装饰的函数 fn 保存在实例属性 fn 中。 在类中定义了一个特殊方法 call,该方法会在类实例像函数一样被调用时执行。在 call 方法中,先输出一条提示信息,然后调用保存在 fn 属性中的函数。 

当我们使用该装饰器来装饰一个函数时,实际上是创建了一个 Check 类的实例,并将被装饰的函数作为参数传入该实例的初始化方法 init 中,然后将该实例作为函数的新定义。因此,当我们调用被装饰的函数时,实际上是调用了 Check 类的实例,进而调用了 call 方法,从而实现了装饰器的功能。

 
 

@Check
def comment():
print("发表评论")

comment()

2、代码说明

@Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。 

要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。 

在call方法里进行对fn函数的装饰,可以添加额外的功能。

 

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

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

相关文章

三分钟学习一个python小知识1-----------我的对python的基本语法的理解

文章目录 一、变量定义二、数据类型三、条件语句四、循环语句五、函数定义总结 一、变量定义 在Python中&#xff0c;使用等号&#xff08;&#xff09;进行变量的定义&#xff0c;并不需要声明变量的类型&#xff0c;Python会自动根据赋值的数据类型来判断变量的类型&#xf…

算法篇——动态规划 完全和多重背包问题 (js版)

一些分析总结 01 背包 问题和 完全背包 问题的不同点在于&#xff0c;所有的物品只能使用一次&#xff0c;判断 哪些物品 装进背包里 物品价值和 最大&#xff1b;而 完全背包 问题中&#xff0c;所有物品都能使用n次&#xff0c;判断 哪个物品 装 n 个进去 物品价值和 最大。…

Mybatis源码分析_日志模块 (1)

不得不承认&#xff0c;学习MyBatis的时间成本要比学习Spring低很多&#xff0c;Mybatis是我所了解过的代码量最小、整体架构最简单&#xff0c;但是又是最具有学习价值的一个框架。如果&#xff0c;你想快速的阅读一个框架的源码&#xff0c;并且掌握这个框架的精髓&#xff0…

浅析GeoServer CVE-2023-25157 SQL注入

简介 GeoServer是一个开源的地图服务器&#xff0c;它是遵循OpenGIS Web服务器规范的J2EE实现&#xff0c;通过它可以方便的将地图数据发布为地图服务&#xff0c;实现地理空间数据在用户之间的共享。 影响版本 geoserver<2.18.7 2.19.0<geoserver<2.19.7 2.20.0…

国内外八大敏捷开发工具盘点

1、Leangoo领歌&#xff1b;官网&#xff1a;Leangoo领歌 - 高效企业必备的敏捷工具,Scrum工具,SAFe敏捷工具,敏捷项目管理,敏捷研发工具 2、VersionOne&#xff1b;官网&#xff1a;https://www.collab.net/products/versiononehttps://www.collab.net/products/versionone …

Python高级系列教程:Python高级语法与正则表达式

学习目标 1、能够掌握with语句的使用 2、能够知道生成器的两种创建方式 3、能够知道深拷贝和浅拷贝的区别 4、能够掌握Python中的正则表达式编写 一、Python高级语法 1、with语句和上下文管理器 ☆ with语句 Python提供了 with 语句的写法&#xff0c;既简单又安全。文件操…

Vue-scoped(局部)样式

scoped(局部)样式 scoped是在脚手架有一个编写样式的小技巧 作用&#xff1a;让样式在局部生效&#xff0c;防止冲突 1 编写案例 现在有两个组件&#xff0c;一个student,一个school&#xff0c;现在想给组件写点样式 这里只给个背景色 没问题&#xff0c;样式生效 2 样式冲…

docker canal 安装

(373条消息) 使用 Docker 部署 canal 服务_canal docker部署_qq2276031的博客-CSDN博客https://blog.csdn.net/qq2276031/article/details/120234122 docker canal github 网址 Canal Admin Docker alibaba/canal Wiki (github.com)https://github.com/alibaba/canal/wiki/Ca…

璞华产业园区租赁运营平台,助力空间资产管理数字化转型!

{ 产业园区租赁运营平台 } 直面行业痛点 专注技术创新 点击输入图片描述&#xff08;最多30字&#xff09; 产业园区作为产业转型升级的重要载体&#xff0c;产业园区租赁运营也正迎来新的发展机遇。璞华一直关注为客户智能化转型过程中提供的服务&#xff0c;能否将技术方案…

【自监督论文阅读 3】DINOv1

文章目录 一、摘要二、引言三、相关工作3.1 自监督学习3.2 自训练与知识蒸馏 四、方法4.1 SSL with Knowledge Distillation4.2 教师网络4.3 网络架构4.4 避免坍塌 五、实验与评估六、消融实验6.1 不同组合的重要性6.2 教师网络选择的影响6.3 避免坍塌6.4 在小batch上训练 七、…

elk搭建

1、前言 ELK 是一个开源的日志管理解决方案&#xff0c;主要是为了统一收集生产的日志&#xff0c;方便日志的查询&#xff0c;传统的日志都是保存在每个机器上&#xff0c;当要查询的时候&#xff0c;需要到每一台机器上去查询日志&#xff0c;非常麻烦&#xff0c;而elk则使…

阿里巴巴 Java 开发手册部分整理

阿里巴巴 Java 开发手册 一、编程规约(一) 命名风格(二) 常量定义(三) 代码格式(四) OOP 规约(五) 集合处理(六) 并发处理(七) 控制语句(八) 注释规约(九) 其它 二、异常日志(一) 异常处理(二) 日志规约 三、单元测试四、安全规约五、MySQL 数据库(一) 建表规约(二) 索引规约(三…

Java 操作pdf工具类

1、获取pdf页数 添加maven依赖&#xff1a; <!-- java获取Pdf文件页码 --><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>1.8.11</version></dependency>代码实现&#x…

Goby 漏洞发布|电信网关配置管理系统后台 /manager/teletext/material/upload.php 文件上传漏洞

漏洞名称&#xff1a;电信网关配置管理系统后台 /manager/teletext/material/upload.php 文件上传漏洞 English Name&#xff1a;Telecom system /manager/teletext/material/upload.php fileupload vulnerability CVSS core: 9.8 影响资产数&#xff1a;856 漏洞描述&…

不过时的经典层架构

在《设计服务要考虑的7个维度》中讲到设计一定要把不稳定的部分做封装。今天咱们就从这个角度重新审视一下经典的四层架构。 上面是一个经典层架构的示意图。这个架构大多数的公司和项目都在直接用&#xff0c;或者用其变体。 比如&#xff0c;某大厂上层Client客户端可能是电商…

安卓蓝牙GATT协议介绍

前言 现在低功耗蓝牙&#xff08;BLE&#xff09;连接都是建立在 GATT (Generic Attribute Profile) 协议之上。GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范&#xff0c;这些很短的数据段被称为属性&#xff08;Attribute&#xff09;。 GAP 详细介绍 GAT…

GBASE金融信创优秀解决方案鉴赏 · 核心业务系统数据库解决方案

为此&#xff0c;实验室特别开设金融信创优秀解决方案专栏&#xff0c;集中展示优秀成果。现在&#xff0c;让我们一起来领略下GBASE的优秀解决方案吧~可点击阅读原文 →《金融信创优秀解决方案--核心业务系统数据库解决方案》。 核心业务系统数据库解决方案 方案简介 随着技…

Android Qcom USB Driver学习(十三)

该系列文章总目录链接与各部分简介&#xff1a; Android Qcom USB Driver学习(零) Android Qcom USB Driver学习(八) Android Qcom USB Driver学习(九) UCSI USB Type-C Connector System Software Interface Specification DPM Device Policy Manager deals with the USB P…

智安网络|驾驭数字化转型时代:加速业务转型的战略

随着科技的飞速发展和数字化时代的到来&#xff0c;企业面临着前所未有的机遇和挑战。数字化正在改变着商业的方方面面&#xff0c;而那些能够及时适应和把握这些变化的企业将脱颖而出。因此&#xff0c;加速企业转型成为了当务之急。 一、为什么需要加速企业转型 1.全球市场竞…

D. Pairs of Segments(最大不相交区间数量)

Problem - D - Codeforces 给定一个由n个线段组成的数组[[l1,r1],[l2,r2],…,[ln,rn]]&#xff0c;其中每个线段用左右端点表示。如果存在至少一个x满足l1≤x≤r1且l2≤x≤r2&#xff0c;则称线段[l1,r1]和[l2,r2]相交。 如果k为偶数&#xff0c;并且可以将该数组的元素分成k/…