猿创征文|Python迭代器、生成器、装饰器、函数闭包

news2024/11/25 10:45:23

在这里插入图片描述

欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起探讨和分享Linux C/C++/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。


Python迭代器、生成器、装饰器、函数闭包

    • 1. 迭代器 iterator
        • ☞迭代器协议
        • ☞Python中的for循环
    • 2. 生成器 generator
        • ☞什么是生成器
        • ☞三元表达式
        • ☞生成器表达式
        • ☞生成器函数
        • ☞生成器函数应用实例
    • 3. 装饰器函数与函数闭包
        • ☞装饰器
        • ☞函数闭包


专栏:Python从入门到实战


1. 迭代器 iterator

☞迭代器协议

迭代合递归

  • 递归:一层一层的调用,然后一层一层的返回,A调用B,B调用C,…,然后C返回给B,B返回给A;
  • 迭代:每次循环得到一个结果,并且都依赖于上一次的结果,迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值;

迭代器协议与可迭代对象

  • 迭代器协议是指,对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常来终止迭代,即只能往后走,不能往回倒退。
  • 可迭代对象是指实现了迭代器协议的对象,即对象内部提供了一个__iter__()方法。
  • 协议是一种约定,可迭代对象实现了迭代器协议,python内部工具比如for循环、sum/min/max等函数通过使用迭代器协议来访问对象。

☞Python中的for循环

for循环的原理
其实for循环的本质就是通过迭代器协议循环所有对象,需要说明的是,for循环的本质就是遵循迭代器协议去访问对象,也就是说for循环的对象都应该是可迭代对象。我们使用for循环可以遍历字符串、列表、元组、字典、集合、文件等等,但是这些数据类型内部并没有__next__()方法,这是为什么呢?实际上它们本身并不是可迭代对象,只不过是在for循环的时候,调用了它们内部的__iter__()方法,把它们变成了可迭代对象,然后for循环再去调用这些可迭代对象的__next__()方法去访问,并捕捉StopIteration异常来终止迭代。
我们定义的字符串、列表、元组、字典、集合、文件等对象,内部都含有一个__iter__()方法,通过调用这个方法可以把对象变成可迭代对象,变成可迭代对象之后就可以使用__next__()方法了。for循环就是通过这个过程去迭代上面这些对象的,当迭代到对象最后一个元素的时候,会自动捕捉StopIteration异常停止迭代。

#for i in list:  → it = list.__iter__() → it.__next__()
#捕捉到异常StopIteration则停止迭代

在这里插入图片描述

for循环的实现
对列表取值的方式:一是索引:l[0],二是迭代器:it = l.iter() 、it.next()。
python中的for循环不是靠索引实现的,而是通过迭代器实现的。
对列表、元组、字符串这种有序的序列进行遍历的方式有:一是通过for循环迭代,即迭代器;二是通过while循环即索引 while index<len list[index]。
非序列类型(无序的)字典、集合、文件对象就不能通过索引去遍历了,只能通过for循环迭代,for循环基于迭代器协议提供了一个统一的可以遍历所有对象的方法,for循环总共做了三件事,首先,在遍历前调用对象的__iter__()方法把对象转换成一个迭代器,然后通过迭代器协议去实现循环访问也就是__next__()方法,最后捕捉StopIteration异常来结束循环。通过for循环实现了一个统一的迭代方法,不需要区分是否有索引/下标/有序,只要是含有__iter__()方法的对象,都是可以for循环的。注意,字典可迭代对象的__next__()方法返回的是字典的key值,所以for循环字典的时候默认是根据key值来的;文件的可迭代对象的__next__()方法是按照行去遍历,在for循环文件对象时,首先for循环根据文件对象转换一个iter_f,此时得到的是一个迭代器,每次执行iter_f.next()的时候只取了一行,也就是说每次循环只取了文件的一行内容放入内存,如果没有变量去保存这一行数据,那么执行完这次循环,这一块内存就立马被python释放了,然后进行下次循环,这样大大节省了内存提高了效率。

#用while模拟for循环
it_l = l.__iter__()
while True:
	try:
    	print(it_l.__next__())
	except StopIteration:
    	break

python提供的内置方法next()方法,实际上也是在调用可迭代对象内置的it.next()方法。总结来说,可迭代对象就是迭代器、即遵循迭代器协议、即包含内置方法__iter__()

2. 生成器 generator

☞什么是生成器

生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议,而其它的数据类型需要调用自己内置的__iter__()方法来实现,所以说,生成器就是可迭代对象。
生成器分类及在python中的表现形式
python有两种方式提供生成器:

  • 生成器函数:常规函数的定义,但是使用yield语句返回结果(常规函数使用return返回结果),yield语句一次返回一个结果,在每个结果中间挂起函数的状态以便下次从离开的地方重新执行。也就是说,只要是函数内部带有yield语句,那么这个函数得到的就是一个生成器。
  • 生成器表达式:类似与列表推导,但是生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。it = (‘string %s’ %i for i in range(10)),通过it.next()便可以在列表中取值了,该表达式生成的就是一个迭代器。

生成器的优点
python使用生成器对延迟操作提供了支持,所谓延迟操作就是指在需要的时候才产生结果,而不是立即产生结果。
总结
生成器是可迭代对象,它实现了延迟计算,节省内存,生成器的本质和其它数据类型一样,都实现了迭代器协议,只不过生成器附加了一个延迟计算来节省内存。

☞三元表达式

‘A’ if name == ‘name’ else ‘B’ # → 如果表达式结果为真,则返回’A’否则返回’B’

#列表解析 → 生成一个列表
[i for i in range(5)] #→ [0, 1, 2, 3, 4]

str_list=[]
for i in range(10)
	str_list.append(‘string %s’ %i)
print(str_list)
# -----→ 相当于
[‘string %s’ %i for i in range(10)]

[‘string %s’ %i for i in range(10) if i > 5]  # → 三元表达式

☞生成器表达式

把列表解析的[]换成()就是生成器表达式,生成器表达式得到的是一个迭代器。相对于列表解析,生成器表达式更节省内存(占用很少内存),因为列表解析是一下子就把整个列表生成了,列表中的全部元素都放在内存中;而生成器表达式是通过__next__()方法取值,每次调用只取一个值,也就是每次只取一个值放到内存中,所以更节省内存。
map、reduce、filter、sum、for、max、min等都是python中使用迭代器协议的例子。
为了节省内存,我们可以在这些函数的参数是[]列表的时候,用()生成器表达式来代替,也就是说如果一个方法的参数如果是可迭代对象,那么就可以使用生成器表达式来作为它的参数。

#比如说我们需要一个列表
[i for i in range(100000000)] # 直接就得到这个列表

#这个数据量非常大,非常耗时间耗内存,我们可以用生成器表达式来代替
(i for i in range(100000000)) # 得到一个迭代器,内部有__next__()方法

#生成器自动实现了迭代器协议
it = (i for i in range(100000000))
it. __next__()
#注意,__next__()只能不停的去取下一个值,当某个值已经被__next__()取过了,那么我们就无法再次取到这个值了。生成器只能遍历一次。
def test():
	for i in range(4):
    	yield i
t = test()
t1 = (i for i in t)
t2 = (i for i in t1)
print(list(t1)) # 0 1 2 3
print(list(t2)) # 空

#生成器在产生的时候,不会进行任何操作,也无法知道列表里面都有什么
#值,只有运行__next__()才能获取一个值,不运行__next__()就无法知道里
#面有什么
#t1和t2都是由生成器表达式生成的生成器,但是他们只是生成器
#里面有什么是未知的(它们不是列表,只有执行next才会得到值)
#当print函数中对t1和t2进行list操作的时候,也就是for循环遍历
#此时,才会通过一次一次的__next__()不停的取值
#list(t1)就相当于取t的值,list之后t1就已经被遍历完一次了
#list(t2)就相当于取t1的值,t1已经被遍历过一次了,
#所以只能取到list最后的元素后面的位置,也就是空
#生成器只能被遍历一次,__next__()只能往下走,不能往回走
#遍历不只是__next__(),list、for、sum等都是遍历

☞生成器函数

在函数内用yield来代替return,return在函数内只能执行一次,因为执行一次就返回出去了,即便写了多个return,那么后面的return语句也没有机会执行了,而yield语句可以执行多次。

def func():
	yield 1
	yield 2
	yield 3
	yield 4
gen = func()  #  得到一个generator
print(gen.__next__()) # 打印1  此时函数func停留在yield 1处
print(gen.__next__()) # 打印2  在yield 1的基础上执行
print(gen.__next__()) # 打印3  在yield 2的基础上执行
#迭代器每次都是在上一次执行结果的基础上执行并得到本次结果
#实际上,可以把yield来理解为一个断点,每次执行__next__()就相当于
#执行到下一个断点,并且执行完会停留在当前断点处
def func():
	print('begin')
	print('call __next__ : 1')
	yield 1
	print('call __next__ : 2')
	yield 2
	print('call __next__ : 3')
	yield 3
	print('end')

gen = func()  #仅仅是拿到了一个生成器,函数内的语句一句都不会执行
print(gen.__next__()) 
print(gen.__next__()) 
print(gen.__next__())
#yield的作用:返回值、保留程序运行状态

使用生成器函数的好处:通过生成器函数取值,每次取到一个值就可以立即对这个值操作,不用等待函数执行后续操作,比如说你获取了num1就可以立即操作num1,并且yield会保留运行状态,当你操作完num1并且需要num2的时候,通过__next__获取num2。如果是普通函数,你需要等到100个数num1-num100全部生成才能对num1进行操作。

def get_num():
	for i in range(100)
		yield 'num%s' %i
		
it = get_num()
ret1 = it.__next__()
ret2 = it.__next__()
#可以直接对it进行for循环
for temp in it:
	print(temp)
#相当于对一个函数进行for循环

生成器函数的特点

  • 语法上和函数相似:生成器函数和常规函数都是使用def语句进行定义,区别在于生成器函数使用yield函数返回一个值,而常规函数使用return语句返回一个值。
  • 自动实现迭代器协议:对于生成器来说,python会自动实现迭代器协议,所以我们可以直接调用它的__next__()方法,并且没有值返回的时候,生成器会自动产生StopIteration异常。
  • 状态挂起:生成器使用yield语句返回一个值,yield语句挂起该生成器函数的状态,保留足够的信息,以便于之后从它离开的地方继续执行。
  • 生成器函数只能遍历一次。

☞生成器函数应用实例

使用生成器函数来处理大量数据

'''
txt: 
{'str':'str1', 'len':1} 
{'str':'str2', 'len':3} 
{'str':'str3', 'len':2} 
...
'''
def get_file_line():
	with open('txt', 'r', encoding = 'utf-8') as f:
	    for i in f:
			yield i #每次返回文件的一行记录
it = get_file_line()
#print(it.__next__())-->it.__next__()得到的是一行字符串,而不是字典
#得到的是文件一行记录 "{'str':'str1', 'len':1}"
#要想把字符串中的字典提出来,使用函数eval
# "{'str':'str1', 'len':1}" -->eval-->{'str':'str1', 'len':1}		
print(it.__next__())
dic = eval(it.__next__())
print(type(dic)
print(dic['len']) #取出'len'对应的value

'''
#如果想全部取出,可以使用for循环
for i in it:
	temp = eval(i)
print(temp['len'])

#按'len'求和,所有len的长度之和
ret = sum(eval(i)['len'] for i in it)
print(ret)
ret2 = sum(eval(i)['len'] for i in it)
print(ret2)
#生成器对象不能迭代第二次,上面ret的时候,it已经被迭代完了
#ret2的时候只能得到文件最后一行的记录,因为yeild就停留在这里
#生成器只能for循环一次
'''

生成器的.send()方法

def producer():
	print(‘first produce’)
	first = yield 1 #send发送的值被yield接收并传递给first
	print(‘second product’, first)
	yield 2
	print(‘third product’)
	yield 3
#def consumer():
    
gen = producer()
ret = gen.__next__()
print(ret)
gen.send(None)
#gen.send(‘第一次yield完成’)
#send的作用:1.传递一个值给当前yield 2.触发执行到下一个yield

生产者消费者模型

#在一个进程中实现并行 → 协程
def producer():
	c = consumer()
	c.__next__()
	for i in range(10):
    	time.sleep(1)
    	c.send(i)
def consumer():
	print(‘consumer’)
	while True:
    	temp = yield
		time.sleep(1)
    	print(‘consumer %s’ %temp)
producer()

3. 装饰器函数与函数闭包

☞装饰器

什么是装饰器

  • 装饰器
    器的本质是一个函数,装饰是指修饰其他函数,为其他函数添加附加功能。
    装饰器两个原则:一是不修改被修饰函数的源代码;二是不修改被修饰函数的调用方式。 (必须遵循)
    装饰器 = 高阶函数 + 函数嵌套 + 闭包
  • 高阶函数
    高阶函数的定义主要有两个原则:一是函数接收的参数是一个函数名;二是函数的返回值是一个函数名。满足二者之一就是高阶函数。

示例
实现一个功能:统计函数foo的执行时间,并且不能修改foo的源代码,不能修改foo的调用方式。

import time
def foo():
	time.sleep(3)
	print(‘function foo’)
def timer(func)
	start_time = time.time()
	func()
	stop_time = time.time()
	print(‘func run time %s’ %(stop_time – start_time))
	return func
#timer(foo) 修改了foo的调用方式,原调用方式为foo()
foo = timer(foo)
#再次强调:函数名是函数地址,函数名加括号才是函数调用
foo() #这样会执行两次foo函数

函数嵌套
在函数内部定义函数(在函数内部定义函数,而不是调用函数)

def func1(arg):
	print(‘func1’)
	def func2(): #作用域在func1内,只能在func1内调用,是局部变量
    	print(‘func2 %s’ %arg)
    	arg = 100
    	def func3():
        	print(‘func3 %s’ %arg) #本层未找到,则去上一层寻找
	print(locals()) #函数即变量 func2 arg

☞函数闭包

闭即封装(变量),包即一层,闭包即作用域。
在这里插入图片描述
实现装饰器
实现一个功能:给test增加一个功能,统计函数test的执行时间,不修改test的源码,不改变test的调用方式

import time
def test():
	time.sleep(3)
	print(‘function test’)
def timmer(func)
    def wrapper():
		start_time = time.time()
		func()
		stop_time = time.time()
		print(‘func run time %s’ %(stop_time – start_time))
	return wrapper
test = timmer(test)
test()
#缺点:调用test之前需要一个赋值操作test = timmer(test)

改进,使用@语法糖

@timmer #相当于test = timmer(test)
def test():
	time.sleep(3)
	print(‘function test’)

test()
"""
只要把@timmer放在被装饰的函数前就行,它就相当于把被装饰函数的函数名test传递给timmer()并把返回值重新赋给test。
"""

改进1: 加上返回值,即被修饰函数有返回值

import time
def timmer(func)
    def wrapper():
		start_time = time.time()
		ret = func()
		stop_time = time.time()
		print(‘func run time %s’ %(stop_time – start_time))
		return ret #返回test的返回值
	return wrapper
@temmer
def test():
	time.sleep(3)
	print(‘function test’)
	return ‘test return’

ret = test() #表面上运行的是test实际上运行的是wrapper
#要想得到test()函数的返回值,就应该把test的返回值加在wrapper
#函数的return语句中
print(ret)

改进2: 给函数闭包加上参数,即被修饰函数有参数
可变参数

#   *args – 接收全部位置参数
#   **kwargs – 接收关键字参数key=value
def func(*args, **kwargs): #就相当于,把所有位置参数传给args组成一个元组,把所有关键字参数传给kwargs组成一个字典。
import time
def timmer(func)
    def wrapper(*args, **kwargs): #可变参数
		start_time = time.time()
		ret = func(*args, **kwargs)
		stop_time = time.time()
		print('func run time %s' %(stop_time – start_time))
		return ret #返回test的返回值
	return wrapper
@temmer
def test(name, age):
	time.sleep(3)
	print('function test [%s - %s]' %(name, age))
	return 'test return'

ret = test('su', age = 18)
"""
wrapper(*args, **kwargs): 接收到的参数
args – ('su')
kwargs – {'age':18}
所以,当wrapper传参给test的时候,必须是*('su') **{'age':18}才行,*就是把列表里面的东西拿出来
"""
print(ret)

为其他函数做装饰

@temmer
def test2(name, age, gender):
	time.sleep(3)
	print('function test2 [%s - %s - %s]' %(name, age, gender))
	return 'test return'
ret = test2('su', 18, 'girl')
# ret = test2(*('su', 18), **{'gender':'girl'}) #传参方式2

"""
解压序列:
test2(name, age, gender)  (*(‘su’, 18), **{‘gender’:‘girl’})
name, age = (‘su’, 18), gender = ‘girl’
一一对应去赋值
比如 a, b, c = (1, 2, 3) #a=1, b=2, c=3
a, b, c = (1, 2, 3, 4) #err
"""

序列解压
a, b, c = ‘hel’
a, b, c = (1, 2, 3)
a, b, c = [1, 2, 3]
只要后面是一个序列就行,并且必须要一一对应。
如果有一个很长很长的序列li,我们只想取出该序列的第一个和最后一个元素,应该怎么办呢?
first, *_, last = li ,这样就可以可,*表示中间所有的元素组成的子序列
first, *mid, last = li , mid = [ ] , 对li去头去尾后的子序列
交换a, b的值可以直接a, b = b, a

有参装饰器
在原来的装饰器外面再套一层函数,根据参数可以进行一些逻辑判断。

def timmer_type(type = '1'):
def timmer(func):
    #原来的逻辑不变,可以加一些判断
    if type == '1'
        print('type1')
        #逻辑1
    else:
        print('other')
        #其他逻辑
return timmer
@timmer_type(type = '1') # timmer = timmer_type(type = '1')
def test():
    print('test')
"""
这里的@timmer_type(type = '1') 就相当于 
 timmer = timmer_type(type = '1') 也就相当于 
@timmer() 只不过在timmer的基础上增加了一个外层函数 
通过这个外层函数可以传入参数,并在内部根据参数增加一些逻辑
"""

举例

def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')


在这里插入图片描述
在这里插入图片描述


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

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

相关文章

C语言qsort()函数针对:整型、单个字符、字符串、结构体,超详细讲解(多维度分析举例,小白一看就懂!!!!!)

目录 一、前言 二、qsort()函数 &#x1f351;qsort()函数简介 &#x1f349;qsort()函数中整型、double型、字符型的应用 &#x1f4a6;整型 &#x1f4a6; double型 &#x1f4a6;字符排序 &#x1f34e;qsort()函数在字符串中的应用 &#x1f4a6;在字符串中按首字母…

Android NDK开发基础

文章目录cmake语法基础cmake添加日志&#xff1a;cmake增加宏字符串比较cmake在build.gradle中传递编译参数到cmake通过javah生成native对应的c头文件jni和java之间字符串的相互操作JavaVM和JNIEnv字符串的编码native方法静态注册和动态注册静态注册动态注册extern cC中STATIC和…

SpringCloud Alibaba-Sentinel保姆级教程

文章目录1、Sentinel前奏1.1、服务雪崩效应1.2、常见容错方案1、隔离2、超时3、限流4、熔断5、降级1.3、常见容错组件1、Hystrix2、Resilience4J3、Sentinel2、Sentinel入门2.1、什么是Sentinel2.2、实战编码1、安装Sentinel服务端2、微服务引入Sentinel2.3、Sentinel流控模式1…

YOLOv5蒸馏 | 知识蒸馏理论篇 | 1/2

之前在《一文搞懂【知识蒸馏】【Knowledge Distillation】算法原理》这篇文章中介绍过一些知识蒸馏的原理,这篇博文将会着重介绍目标检测领域的知识蒸馏原理。 文章目录 1.《Object detection at 200 Frames Per Second》1.1 蒸馏难点1.2 蒸馏损失1.3 实验结果2. 《Learning E…

计算机毕业设计ssm+vue基本微信小程序的体检预约小程序

项目介绍 我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;体检预约系统小程序被用户普遍使用&#xff0c;为方便用户…

【前端】CSS(1) —— CSS的基本语法和一些简单的选择器

JavaEE传送门JavaEE 网络原理——网络层与数据链路层 【前端】HTML入门 —— HTML的常见标签 目录CSS基本语法注释引入方式内部样式内联样式外部样式代码风格样式格式样式大小写空格规范CSS 选择器标签选择器类选择器id 选择器通配符选择器复合选择器后代选择器子选择器并集选…

文件包含漏洞和hash破解

介绍 Windows是当今世界最主要的操作系统&#xff0c;因为它易于使用的GUI可访问性。大约85%的市场份额已成为攻击的关键操作系统。此外&#xff0c;大多数组织使用Active Directory来设置其Windows域网络。微软聘请NTLM&#xff08;New Technology LAN Manager&#xff09;和…

Pico Neo3 4VR游戏下载地址及十大好玩游戏推荐

大家好&#xff0c;杰克今天为大家分享的是VR一体机游戏。 说到VR一体机&#xff0c;我们首先想到的一定是国产之光pico啦&#xff0c;picovr被认为是国内最被看好的VR一体机&#xff0c;有望在国内取代红极一时的OculusQuest&#xff0c;填补这块市场的空白。OculusQuest将6D…

#Primavera Unifier:关于零代码/低代码平台特点【2/3】

在之前对Unifier的介绍中&#xff0c;我提到了Unifier应用的一个非常关键的特征&#xff0c;及零代码快速配置使用&#xff0c;而为了更好的介绍Oracle Primavera Unifier 的零代码特点&#xff0c;以下我将通过3篇内容来逐一介绍零代码/低代码平台的特点。 前面介绍到了零代码…

Opencv项目实战:14 手势控制音量

目录 0、项目介绍 1、项目展示 2、项目搭建 3、项目的代码与讲解 4、项目资源 5、项目总结 0、项目介绍 本篇与上一篇有很多联系&#xff0c;大家可以看看这篇Opencv项目实战&#xff1a;13 手部追踪&#xff0c;我们将根据上一节的内容&#xff0c;进行一个拓展。本篇你…

AtCoder Beginner Contest 275 【E】【F】

E - Sugoroku 4 【概率dp】 题意&#xff1a; 对于每个样例&#xff0c;读入n&#xff0c;m&#xff0c;k。 一维数轴&#xff0c;你现在在0这个点上&#xff0c;目标是到达n这个点&#xff0c;你有k次掷骰子的机会&#xff0c;每次可能等概率的掷出1~m的任意一个数字&#xff…

面了一个4年经验的测试工程师,自动化都不会也要15k,我真是醉了...

在深圳这家金融公司也待了几年&#xff0c;被别人面试过也面试过别人&#xff0c;大大小小的事情也见识不少&#xff0c;今天又是团面的一天&#xff0c; 一百多个人都聚集在一起&#xff0c;因为公司最近在谈项目出来面试就2个人&#xff0c;无奈又被叫到面试房间。 整个过程…

.NET Core HttpReports 监控

HttpReports 基于.NET Core 开发的APM监控系统&#xff0c;使用MIT开源协议&#xff0c;主要功能包括&#xff0c;统计, 分析, 可视化&#xff0c; 监控&#xff0c;追踪等&#xff0c;适合在中小项目中使用。 语雀&#xff1a;https://www.yuque.com/httpreports/docs/uyaiil …

SQL注入之绕过is_numeric过滤

目录预备知识PHP常用的过滤类函数is_numeric()函数介绍实验目的实验环境实验步骤一通过源代码审计&#xff0c;发现临时文件实验步骤二通过分析源代码的执行逻辑&#xff0c;了解程序的功能&#xff0c;发现程序中的安全漏洞实验步骤三绕过过滤函数实现SQL注入预备知识 PHP常用…

Linux进程信号

文章目录什么是信号signal函数的功能&#xff08;捕捉信号后自己处理&#xff09;Core Dump&#xff08;核心转储&#xff09;kill&#xff0c;raise&#xff0c;alarm系统调用再度理解OS给进程发送信号信号集操作函数自定义捕捉详解什么是信号 生活中的信号&#xff1a;闹钟&…

0083 环形链表

package LinkedList_; /* * 单向环形链表应用场景——约瑟夫问题 * 设编号为1&#xff0c;2....n的n个人围成一圈&#xff0c;约定编号为k&#xff08;1<k<n&#xff09;的人从1开始报数&#xff0c;数到m的人出列&#xff0c; * 它的下一位又从…

黑胶歌曲没权限,还好我会Python,一分钟一个歌单,硬盘有点不够用了~

今日份Python白嫖人生苦短&#xff0c;我用Python一、你需要准备1、环境2、模块二、效果展示三、代码展示四、写在最后人生苦短&#xff0c;我用Python 人之初&#xff0c;喜白嫖。大家都喜欢白嫖&#xff0c;我也喜欢&#xff0c;那么今天就来试试怎么白嫖抑云~ 我不是&#…

React面向组件编程(定义组件,组件三大核心属性,组件事件处理、组件收集表单数据、高阶函数和函数的柯里化)

目录 一、React中定义组件 1、函数式组件 2、类式组件 二、组件三大核心属性 1、组件三大核心属性1: State(状态) 2、组件三大核心属性2: props 3、组件三大核心属性3: ref 三、组件事件处理 1、事件处理 四、组件收集表单数据 1、受控组件 2、非受控组件 五、高阶函…

【数据结构】算法的时间复杂度和空间复杂度

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;&#x1f6f9;初出茅庐C语言、&#x1f6f4;数据结构 ☀️ 学习格言&#xff1a;眼泪终究流不…

象棋中的马跳步问题

象棋中的马跳步问题 作者&#xff1a;Grey 原文地址&#xff1a; 博客园&#xff1a;象棋中的马跳步问题 CSDN&#xff1a;象棋中的马跳步问题 题目描述 中国象棋中&#xff0c;整个棋盘就是横坐标上 9 条线、纵坐标上 10 条线的一个区域&#xff0c;给你三个 参数 x&…