函数进阶

一、参数的补充

在函数基础部分,我们掌握函数和参数基础知识,掌握这些其实完全就可以进行项目的开发。

今天的补充的内容属于进阶知识,包含:内存地址相关、面试题相关等,在特定情况下也可以让代码更加简洁,提升开发效率。

1.参数内存地址相关【面试题】

如果想要查看下某个值的在内存中的地址:
若想判断两个值是否为同一个内存地址,可使用该方法:

v1 = "张三"
addr = id(v1)print(addr) # 2796807672144
v1 = [11,22,33]
v2 = [11,22,33]print( id(v1) )
print( id(v2) )# 输出:
2540634586368
2540636371904
内存地址不同
v1 = [11,22,33]
v2 = v1print( id(v1) )
print( id(v2) )
# 内存地址相通

函数执行传参时,传递的是内存地址。

def func(data):print(data, id(data))  # 张三 2474490810704v1 = "张三"
print(id(v1))  # 2474490810704func(v1)

面试题:请问Python的参数默认传递的是什么?

实际上Python的参数默认传递的是内存地址
Python参数的这一特性有两个好处:

  • 节省内存

  • 对于可变类型且函数中修改元素的内容,所有的地方都会修改。可变类型:列表、字典、集合。

  # 可变类型 & 修改内部修改def func(data):data.append(999)v1 = [11,22,33]func(v1)print(v1) # [11,22,33,666]
  # 特殊情况:可变类型 & 重新赋值def func(data):data = ["武沛齐","alex"]v1 = [11,22,33]func(v1)print(v1) # [11,22,33]
  # 特殊情况:不可变类型,无法修改内部元素,只能重新赋值。def func(data):data = "alex"v1 = "武沛齐"func(v1)

其他很多编程语言执行函数时,默认传参时会将数据重新拷贝一份,会浪费内存。

提示注意:其他语言也可以通过 ref 等关键字来实现传递内存地址。

当然,如果你不想让外部的变量和函数内部参数的变量一致,也可以选择将外部值拷贝一份,再传给函数。

import copy# 可变类型 & 修改内部修改
def func(data):data.append(999)v1 = [11, 22, 33]
new_v1 = copy.deepcopy(v1) # 拷贝一份数据
func(new_v1)print(v1)  # [11,22,33]

2. 函数的返回值是内存地址

def func():data = [11, 22, 33]print(id(data))return datav1 = func()
print(v1,id(v1)) # [11,22,33]# 输出:
1989829131136
[11, 22, 33] 1989829131136

上述代码的执行过程:

  • 执行func函数
  • data = [11, 22, 33] 创建一块内存区域,内部存储[11,22,33],data变量指向这块内存地址。
  • return data 返回data指向的内存地址
  • v1接收返回值,所以 v1 和 data 都指向 [11,22,33] 的内存地址(两个变量指向此内存,引用计数器为2)
  • 由函数执行完毕之后,函数内部的变量都会被释放。(即:删除data变量,内存地址的引用计数器-1)

所以,最终v1指向的函数内部创建的那块内存地址。

def func():data = [11, 22, 33]return datav1 = func()
print(v1) # [11,22,33]v2 = func()
print(v2) # [11,22,33]

上述代码的执行过程:

  • 执行func函数
  • data = [11, 22, 33] 创建一块内存区域,内部存储[11,22,33],假设data变量指向这块内存地址 1000001110。
  • return data 返回data指向的内存地址
  • v1接收返回值,所以 v1 和 data 都指向 [11,22,33] 的内存地址(两个变量指向此内存,引用计数器为2)
  • 由函数执行完毕之后,函数内部的变量都会被释放。(即:删除data变量,内存地址的引用计数器-1)

所以,最终v1指向的函数内部创建的那块内存地址。(v1指向的1000001110内存地址)

  • 执行func函数
  • data = [11, 22, 33] 创建一块内存区域,内部存储[11,22,33],data变量指向这块内存地址 11111001110(新创建的区域)。
  • return data 返回data指向的内存地址
  • v2接收返回值,所以 v1 和 data 都指向 [11,22,33] 的内存地址(两个变量指向此内存,引用计数器为2)
  • 由函数执行完毕之后,函数内部的变量都会被释放。(即:删除data变量,内存地址的引用计数器-1)

所以,最终v2指向的函数内部创建的那块内存地址。(v2指向的11111001110内存地址)

验证如下:

def func():data = [11, 22, 33]print(id(data))return datav1 = func()
print(v1, id(v1))  # [11,22,33]v2 = func()
print(v2, id(v1))  # [11,22,33]# 输出:
2475908004736
[11, 22, 33] 2475908004736
2475913072576
[11, 22, 33] 2475908004736# 此处有缓存机制,所以两个地址是一样的,为python优化

3. 参数的默认值【面试题】

这个知识点在面试题中出现的概率比较高,但真正实际开发中用的比较少。

def func(a1,a2=18):print(a1,a2)

原理:Python在创建函数(未执行)时,如果发现函数的参数中有默认值,则在函数内部会创建一块区域并维护这个默认值。

  • 执行函数未传值时,则让a2指向 函数维护的那个值的地址。
func("root")
  • 执行函数传值时,则让a2指向新传入的值的地址。
func("admin",20)

在特定情况【默认参数的值是可变类型 list/dict/set】 & 【函数内部会修改这个值】下,参数的默认值 有坑 。

  # 在函数内存中会维护一块区域存储 [1,2,666,666,666],假设内存地址为: 100010001def func(a1,a2=[1,2]):a2.append(666)print(a1,a2)# a1=100# a2 -> 100010001func(100) # a1 = 100    a2 = [1,2,666]# a1=200# a2 -> 100010001func(200) # a1 = 200   没有规定a2,故此处a2,为默认参数,但是上一次执行,默认参数已经被修改,且又执行了一次故 a2.append(666),a2 = [1,2,666,666]# a1=99# a2 -> 1111111101,a2传输的值,故不再指向默认存储地址,指向新传入值的地址,此处假设为1111111101func(99,[77,88]) # a1 = 66 ,a2 = [77,88,666]# a1=300# a2 -> 100010001func(300) #a1 = 300 ,a2 = [1,2,666,666,666] 
  • 大坑
  # 此处print放在最后的执行结果与上边是不同的:# 在内部会维护一块区域存储 [1, 2, 10, 20,40 ] ,内存地址假设为 1010101010def func(a1, a2=[1, 2]):a2.append(a1)return a2# a1=10# a2 -> 1010101010# v1 -> 1010101010v1 = func(10)print(v1) # [1, 2, 10]# a1=20# a2 -> 1010101010# v2 -> 1010101010v2 = func(20)print(v2) # [1, 2, 10, 20 ]# a1=30# a2 -> 11111111111      此处a2传了值,故不会用默认存储地址的值,又执行 a2.append(a1),故 [11, 22,30]# v3 -> 11111111111v3 = func(30, [11, 22])print(v3) #  [11, 22,30]# a1=40# a2 -> 1010101010# v4 -> 1010101010v4 = func(40)print(v4) # [1, 2, 10, 20,40 ] 
  • 深坑
  # 内存中创建空间存储 [1, 2, 10, 20, 40] 地址:1010101010def func(a1, a2=[1, 2]):a2.append(a1)return a2# a1=10# a2 -> 1010101010# v1 -> 1010101010   此时:[1, 2, 10]v1 = func(10)# a1=20# a2 -> 1010101010# v2 -> 1010101010  此时:[1, 2, 10, 20] v2 = func(20)# a1=30# a2 -> 11111111111   此时:[11,22,30]# v3 -> 11111111111   v3 = func(30, [11, 22])# a1=40# a2 -> 1010101010    # v4 -> 1010101010    此时:[1, 2, 10, 20, 40]v4 = func(40)print(v1) # 执行完所有函数后,此时默认存储已经发生变化,变化为最后执行完成时被改动后的默认存储内容:[1, 2, 10, 20, 40] print(v2) # [1, 2, 10, 20, 40]print(v3) # [11,22,30]print(v4) # [1, 2, 10, 20, 40] 

4. 动态参数

动态参数,定义函数时在形参位置用 *或** 可以接任意个参数。

def func(*args,**kwargs):print(args,kwargs)func("张三","李四",n1="alex",n2="eric")

在定义函数时可以用 *和**,其实在执行函数时,也可以用。

  • 形参固定,实参用*和**
def func(a1,a2):print(a1,a2)func( 11, 22 )
func( a1=1, a2=2 )func( *[11,22] )     # 11 22
func( **{"a1":11,"a2":22} )   # 11 22
  • 形参用*和**,实参也用 *和**
def func(*args,**kwargs):print(args,kwargs)func( 11, 22 )
func( 11, 22, name="张三", age=18 )# 小坑,此处实际输出为:([11,22,33], {"k1":1,"k2":2}), {}
func( [11,22,33], {"k1":1,"k2":2} )# args=(11,22,33),kwargs={"k1":1,"k2":2}
func( *[11,22,33], **{"k1":1,"k2":2} ) # 值得注意:按照这个方式将数据传递给args和kwargs时,数据是会重新拷贝一份的(可理解为内部循环每个元素并设置到args和kwargs中)。

所以,在使用format字符串格式化时,可以可以这样:

v1 = "我是{},年龄:{}。".format("张三",18)
v2 = "我是{name},年龄:{age}。".format(name = "张三",age = 18)v3 = "我是{},年龄:{}。".format(*["张三",18])
v4 = "我是{name},年龄:{age}。".format(**{"name": "张三", "age": 18})

练习题

  1. 看代码写结果
   def func(*args,**kwargs):print(args,kwargs)params = {"k1":"v2","k2":"v2"}func(params)    # ({"k1":"v2","k2":"v2"}, ) {}func(**params)  # (), {"k1":"v2","k2":"v2"}
  1. 读取文件中的 URL 和 标题,根据URL下载视频到本地(以标题作为文件名)。
   模仿,https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f570000bvbmace0gvch7lo53oog&ratio=720p&line=0卡特,https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34q1g&ratio=720p&line=0罗斯,https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg&ratio=720p&line=0
   # 下载视频示例import requestsres = requests.get(url="https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg&ratio=720p&line=0",headers={"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"})with open('rose.mp4', mode='wb') as f:f.write(res.content)
import requestsdef download(title, url):""" 下载并保存视频 """res = requests.get(url=url,headers={"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"})with open('{}.mp4'.format(title), mode='wb') as f:f.write(res.content)
# 读取文件
with open('db.csv', mode='r', encoding='utf-8') as file_object:for line in file_object:line = line.strip()row_list = line.split(',')# download(row_list[0], row_list[1])download(*row_list)

二、函数名,函数名到底是什么?

函数名其实就是一个变量,这个变量只不过代指的函数而已。

name = "张三"
def add(n1,n2):return n1 + n2

注意:函数必须先定义才能被调用执行(解释型语言)。

# 正确
def add(n1,n2):return n1 + n2ret = add(1,2)
print(ret) 
# 错误
ret = add(1,2)
print(ret) def add(n1,n2):return n1 + n2

1.函数做元素

既然函数就相当于是一个变量,那么在列表等元素中是否可以把行数当做元素呢?

def func():return 123data_list = ["张三", "func", func , func() ]
#            字符串   字符串  函数名   函数执行后的返回值,这里为123
print( data_list[0] ) # 字符串"张三"
print( data_list[1] ) # 字符串 "func"
print( data_list[2] ) # 函数 func
print( data_list[3] ) # 整数 123res = data_list[2]()
print( res ) # 执行函数 func,并获取返回值;print再输出返回值。print( data_list[2]() ) # 123

注意:函数同时也可被哈希,所以函数名通知也可以当做集合的元素、字典的键。

掌握这个知识之后,对后续的项目开发有很大的帮助,例如,在项目中遇到根据选择做不同操作时:

【1】 情景1,例如:要开发一个类似于微信的功能。

实现该功能以前的代码写法,判断过多:

  def send_message():"""发送消息"""passdef send_image():"""发送图片"""passdef send_emoji():"""发送表情"""passdef send_file():"""发送文件"""passprint("欢迎使用xx系统")print("请选择:1.发送消息;2.发送图片;3.发送表情;4.发送文件")choice = input("输入选择的序号")if choice == "1":send_message()elif choice == "2":send_image()elif choice == "3":send_emoji()elif choice == "4":send_file()else:print("输入错误")

将函数当成元素放在列表或字典中会大大简化代码,不必多次if判断:

  def send_message():"""发送消息"""passdef send_image():"""发送图片"""passdef send_emoji():"""发送表情"""passdef send_file():"""发送文件"""passdef xxx():"""收藏"""passfunction_dict = {"1": send_message,   #没有加括号send_message(),故此处函数不会被执行,代指函数名"2": send_image,"3": send_emoji,"4": send_file,"5": xxx}print("欢迎使用xx系统")print("请选择:1.发送消息;2.发送图片;3.发送表情;4.发送文件")choice = input("输入选择的序号") # "1"func = function_dict.get(choice)if not func:print("输入错误")else:# 执行函数func()

【2】情景2,例如:某个特定情况,要实现发送短信、微信、邮件。

def send_msg():"""发送短信"""passdef send_email():"""发送图片"""passdef send_wechat():"""发送微信"""# 执行函数
send_msg()
send_email()
send_wechat()

优化后:

def send_msg():"""发送短信"""passdef send_email():"""发送图片"""passdef send_wechat():"""发送微信"""passfunc_list = [ send_msg, send_email, send_wechat ]
for item in func_list:item()

上述两种情景,在参数相同时才可用,如果参数不一致,会出错。所以,在项目设计时就要让程序满足这一点,如果无法满足,也可以通过其他手段时间,例如:

【1】情景1:

def send_message(phone,content):"""发送消息"""passdef send_image(img_path, content):"""发送图片"""passdef send_emoji(emoji):"""发送表情"""passdef send_file(path):"""发送文件"""passfunction_dict = {"1": [ send_message,  ['15131255089', '你好呀']],"2": [ send_image,  ['xxx/xxx/xx.png', '消息内容']],"3": [ send_emoji, ["😁"]],"4": [ send_file, ['xx.zip'] ]
}print("欢迎使用xx系统")
print("请选择:1.发送消息;2.发送图片;3.发送表情;4.发送文件")
choice = input("输入选择的序号") # 1item = function_dict.get(choice) # [ send_message,  ['15131255089', '你好呀']],
if not item:print("输入错误")
else:# 执行函数func = item[0] # send_messageparam_list = item[1] #  ['15131255089', '你好呀']func(*param_list) # send_message(*['15131255089', '你好呀'])

【2】情景2:

def send_msg(mobile, content):"""发送短信"""passdef send_email(to_email, subject, content):"""发送图片"""passdef send_wechat(user_id, content):"""发送微信"""passfunc_list = [{"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短消息"}},{"name": send_email, "params": {'to_email': "wupeiqi@live.com", "subject": "报警消息", "content": "硬盘容量不够用了"}},{"name": send_wechat, "params": {'user_id': 1, 'content': "约吗"}},
]#  {"name": send_msg, "params": {'mobile': "15131255089", "content": "你有新短消息"}},
for item in func_list:func = item['name'] # send_msgparam_dict = item['params'] # {'mobile': "15131255089", "content": "你有新短消息"}func(**param_dict) # send_msg(**{'mobile': "15131255089", "content": "你有新短消息"})

2.函数名赋值

1.将函数名赋值给其他变量,函数名其实就个变量,代指某函数;如果将函数名赋值给另外一个变量,则此变量也会代指该函数,例如:

def func(a1,a2):print(a1,a2)xxxxx = func# 此时,xxxxx和func都代指上面的那个函数,所以都可以被执行。
func(1,1)
xxxxx(2,2)
def func(a1,a2):print(a1,a2)func_list = [func,func,func]func(11,22)
func_list[0](11,22)
func_list[1](33,44)
func_list[2](55,66)

2.对函数名重新赋值,如果将函数名修改为其他值,函数名便不再代指函数,例如:

def func(a1,a2):print(a1,a2)# 执行func函数
func(11,22)   # 11,22# func重新赋值成一个字符串
func = "张三"print(func)   # 张三,func的默认赋值出现变更
def func(a1,a2):print(a1+a2)func(1,2)   # 3def func():print(666)func()   # 666

注意:由于函数名被重新定义之后,就会变量新被定义的值,所以大家在自定义函数时,不要与python内置的函数同名,否则会覆盖内置函数的功能,例如:

id,bin,hex,oct,len...
# len内置函数用于计算值得长度
v1 = len("张三三")
print(v1) # 3# len重新定义成另外一个函数
def len(a1,a2):return a1 + a2# 以后执行len函数,只能按照重新定义的来使用
v3 = len(1,2)
print(v3)

3.函数名做参数和返回值

函数名其实就一个变量,代指某个函数,所以,他和其他的数据类型一样,也可以当做函数的参数和返回值。

【1】参数

def plus(num):return num + 100def handler(func):res = func(10) # 110msg = "执行func,并获取到的结果为:{}".format(res)print(msg) # 执行func,并获取到的结果为:110# 执行handler函数,将plus作为参数传递给handler的形式参数func
handler(plus)

【2】返回值

def plus(num):return num + 100def handler():print("执行handler函数")return plusresult = handler()
data = result(20) # 120
print(data)

三、返回值和print的区别

对于初学者的同学,很多人都对print和返回值分不清楚,例如:

def add(n1,n2):print(n1 + n2)v1 = add(1,3)
print(v1)# 输出
4
Nonedef plus(a1,a2):return a1 + a2v2 = plus(1,2)
print(v2)
# 输出
3

这两个函数是完全不同的

  • 在函数中使用print,只是用于在某个位置输出内容而已。
  • 在函数中使用return,是为了将函数的执行结果返回给调用者,以便于后续其他操作。

在调用并执行函数时,要学会分析函数的执行步骤。

def f1():print(123)def f2(arg):ret = arg()return retv1 = f2(f1)
print(v1)# 输出
123
None
def f1():print(123)def f2(arg):ret = arg()return f1v1 = f2(f1)v2 = v1()
print(v2)# 输出
123
123
None

四、函数的作用域

作用域,可以理解为一块空间,这块空间的数据是可以共享的。通俗点来说,作用域就类似于一个房子,房子中的东西归里面的所有人共享,其他房子的人无法获取。

1. 函数为作用域

Python以函数为作用域,所以在函数内创建的所有数据,可以此函数中被使用,无法在其他函数中被使用。

def func():name = "张三三"data_list = [11,22,33,44]print(name,data_list)age = 20print(age)def handler():age = 18    # 此处age与上边的age没有什么关系,两个不属于一个作用域print(age)func()
handler()

学会分析代码,了解变量到底属于哪个作用域且是否可以被调用:

def func():name = "张三三"age = 29print(age)data_list = [11,22,33,44]print(name,data_list)for num in range(10):print(num)print(num)if 1 == 1:value = "admin"print(value)print(value)if 1 > 2:max_num = 10print(max_num)print(max_num)def handler():age = 18print(age)handler()
func()

2.全局和局部

Python中以函数为作用域,函数的作用域其实是一个局部作用域。

# 以前写代码的方式,相当于写在一个py文件中,均为全局变量
goods = [{"name": "电脑", "price": 1999},{"name": "鼠标", "price": 10},{"name": "游艇", "price": 20},{"name": "美女", "price": 998}
]
for index in range(len(goods)):item = goods[index]print(index + 1, item['name'], item['price'])while True:num = input("请输入要选择的商品序号(Q/q):")  # "1"if num.upper() == "Q":breakif not num.isdecimal():print("用输入的格式错误")breaknum = int(num)send_email()if num > 4 or num < 0:print("范围选择错误")breaktarget_index = num - 1choice_item = goods[target_index]print(choice_item["name"], choice_item['price'])send_email()
# 全局变量(代码规范:全局变量名大写)
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]    def download():# 局部变量url = "http://www.xxx.com"...def upload():file_name = "rose.zip"...

COUNTRYCITY_LIST是在全局作用域中,全局作用域中创建的变量称之为【全局变量】,可以在全局作用域中被使用,也可以在其局部作用域中被使用。

downloadupload函数内部维护的就是一个局部作用域,在各自函数内部创建变量称之为【局部变量】,且局部变量只能在此作用域中被使用。局部作用域中想使用某个变量时,寻找的顺序为:优先在局部作用域中寻找,如果没有则去上级作用域中寻找。全局作用域中使用某个变量时,只能在全局作用域中寻找,不能在其下的局部作用域寻找。

注意:全局变量一般都是大写。

示例1:在局部作用域中读取全局作用域的变量。

COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]def download():url = "http://www.xxx.com"print(url)print(COUNTRY)print(CITY_LIST)def upload():file_name = "rose.zip"print(file_name)print(COUNTRY)print(CITY_LIST)print(COUNTRY)
print(CITY_LIST)
download()
upload()print(file_name) # 报错
print(url) # 报错

示例2:局部作用域和全局作用域变量同名

COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]def download():url = "http://www.xxx.com"CITY_LIST = ["河北","河南","山西"]print(url)print(COUNTRY)print(CITY_LIST)def upload():file_name = "rose.zip"print(COUNTRY)print(CITY_LIST)print(COUNTRY)
print(CITY_LIST)
download()
upload()# 输出:
中国
['北京', '上海', '深圳']
http://www.xxx.com
中国
['河北', '河南', '山西']
中国
['北京', '上海', '深圳']
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]def download():url = "http://www.xxx.com"CITY_LIST = ["河北","河南","山西"]print(url)print(COUNTRY)print(CITY_LIST)def upload():file_name = "rose.zip"print(COUNTRY)print(CITY_LIST)print(COUNTRY)
print(CITY_LIST)
download()
upload()COUNTRY = "中华人民共和共国"
CITY_LIST = [11,22,33]download()
upload()# 输出
中国
["北京","上海","深圳"]
http://www.xxx.com
中国
["河北","河南","山西"]
中国["北京","上海","深圳"]
http://www.xxx.com
中华人民共和共国
["河北","河南","山西"]
中华人民共和共国
[11,22,33]

3.global关键字

默认情况下,在局部作用域对全局变量只能进行:读取和修改内部元素(可变类型),无法对全局变量进行重新赋值。

  • 读取
COUNTRY = "中国"CITY_LIST = ["北京","上海","深圳"]def download():    url = "http://www.xxx.com"    print(COUNTRY)    print(CITY_LIST)    download()
  • 修改内部元素(可变类型)
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]def download():url = "http://www.xxx.com"print(CITY_LIST)CITY_LIST.append("广州")CITY_LIST[0] = "南京"print(CITY_LIST)download()
  • 无法对全局变量重新赋值
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]def download():url = "http://www.xxx.com"# 不是对全部变量赋值,而是在局部作用域中又创建了一个局部变量 CITY_LIST 。CITY_LIST =  ["河北","河南","山西"]print(CITY_LIST)def upload():file_name = "rose.zip"print(COUNTRY)print(CITY_LIST)download()
upload()

如果想要在局部作用域中对全局变量重新赋值,则可以基于 global关键字实现,例如:

COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]def download():url = "http://www.xxx.com"global CITY_LISTCITY_LIST =  ["河北","河南","山西"]print(CITY_LIST)global COUNTRYCOUNTRY = "中华人民共和国"print(COUNTRY)def upload():file_name = "rose.zip"print(COUNTRY)print(CITY_LIST)download()
upload()# 输出:
['河北', '河南', '山西']
中华人民共和国
中华人民共和国
['河北', '河南', '山西']

总结

  1. 函数参数传递的是内存地址。

    • 想重新创建一份数据再传递给参数,可以手动拷贝一份。

    • 特殊:参数是动态参数时,通过*或**传参时,会将数据循环添加到参数中(类似于拷贝一份)

 def fun(*args, **kwargs):print(args, kwargs)fun(*[11, 22, 33], **{"k1": 1, "k2": 2})
  1. 函数的返回值也是内存地址。(函数执行完毕后,其内部的所有变量都会被销毁,引用计数器为0时,数据也销毁)
   def func():name = [11,22,33]data = namefunc()
   def func():name = [11,22,33]return namedata = func()while True:print(data)
  1. 当函数的参数有默认值 & 默认值是可变类型 & 函数内部会修改内部元素(有坑)
   # 内部会维护一个列表 [],只要b不传值则始终使用都是这个列表。    def func(a,b=[]):b.append(a)
  1. 定义函数写形式参数时可以使用***,执行函数时也可以使用。

  2. 函数名其实也是个变量,他也可以做列表、字典、集合等元素(可哈希)

  3. 函数名可以被重新赋值,也可以做另外一个函数的参数和返回值。

  4. 掌握 print 和 return的区别,学会分析代码的执行流程。

  5. python是以函数为作用域。

  6. 在局部作用域中寻找某数据时,优先用自己的,自己没有就在上级作用域中寻找。

  7. 基于 global关键字可以在局部作用域中实现对全局作用域中的变量(全局变量)重新赋值。

作业

  1. 如何查看一个值得内存地址?
    id
  2. 函数的参数传递的是引用(内存地址)还是值(拷贝一份)?
    引用
  3. 看代码写结果
   v1 = {}v2 = v1v1["k1"] = 123print(v1,v2)# 输出:
{'k1': 123} {'k1': 123}
  1. 看代码写结果
   def func(k,v,info={}):info[k] = vreturn infov1 = func(1,2)print(v1)v2 = func(4,5,{})print(v2)v3 = func(5,6)print(v3)# 输出:
{1: 2}
{4: 5}
{1: 2, 5: 6}
  1. 看代码写结果
   def func(k,v,info={}):info[k] = vreturn infov1 = func(1,2)v2 = func(4,5,{})v3 = func(5,6)print(v1,v2,v3)# 输出:
{1: 2, 5: 6} {4: 5} {1: 2, 5: 6}
  1. 简述第5题、第6题的结果为何结果不同。

    第5题中的 v1和v3变量指向的都是函数内部维护的那个列表的内存地址。
    先print(v1)时,函数内部维护的列表的值当时是{1: 2}
    最后print(v3)时,函数内部维护的列表的值已被修改为{1: 2, 5: 6}

    第5题中的 v1和v3变量也是指向的都是函数内部维护的那个列表的内存地址。
    最后再print v1和v3 时,结果就是最终函数内部维护的列表的值,即: {1: 2, 5: 6}

  2. 看代码写结果

   def func(*args, **kwargs):print(args, kwargs)return "完毕"v1 = func(11, 22, 33)print(v1)v2 = func([11, 22, 33])print(v2)v3 = func(*[11, 22, 33])print(v3)v4 = func(k1=123, k2=456)print(v4)v5 = func({"k1": 123, "k2": 456})print(v5)v6 = func(**{"k1": 123, "k2": 456})print(v6)v7 = func([11, 22, 33], **{"k1": 123, "k2": 456})print(v7)v8 = func(*[11, 22, 33], **{"k1": 123, "k2": 456})print(v8)

带结果:

   print(args, kwargs)return "完毕"v1 = func(11, 22, 33)  # (11, 22, 33) {}
print(v1)  # 完毕v2 = func([11, 22, 33])  # ([11, 22, 33],) {}
print(v2)  # 完毕v3 = func(*[11, 22, 33])  # (11, 22, 33) {}
print(v3)  # 完毕v4 = func(k1=123, k2=456)  # () {'k1': 123, 'k2': 456}
print(v4)  # 完毕v5 = func({"k1": 123, "k2": 456})  # ({'k1': 123, 'k2': 456},) {}
print(v5)  # 完毕v6 = func(**{"k1": 123, "k2": 456})  # () {'k1': 123, 'k2': 456}
print(v6)  # 完毕v7 = func([11, 22, 33], **{"k1": 123, "k2": 456})  # ([11, 22, 33],) {'k1': 123, 'k2': 456}
print(v7)  # 完毕v8 = func(*[11, 22, 33], **{"k1": 123, "k2": 456})  # (11, 22, 33) {'k1': 123, 'k2': 456}
print(v8)  # 完毕
  1. 看代码写结果
   def func(*args,**kwargs):prev = "-".join(args)data_list = []for k,v in kwargs.items():item = "{}-{}".format(k,v)data_list.append(item)content = "*".join(data_list)return prev,contentv1 = func("北京","上海",city="深圳",count=99)print(v1)v2 = func(*["北京","上海"],**{"city":"深圳","count":99})print(v2)
# 输出:
('北京-上海', 'city-深圳*count-99')
('北京-上海', 'city-深圳*count-99')
  1. 补充代码,实现获取天气信息并按照指定格式写入到文件中。
   # 获取天气信息示例import requestsres = requests.get(url="http://www.weather.com.cn/data/ks/101010100.html")res.encoding = "utf-8"weather_dict = res.json()# 获取的天气信息是个字典类型,内容如下:print(weather_dict)"""{'weatherinfo': {'city': '北京', 'cityid': '101010100', 'temp': '18', 'WD': '东南风', 'WS': '1级', 'SD': '17%', 'WSE': '1', 'time': '17:05', 'isRadar': '1', 'Radar': 'JC_RADAR_AZ9010_JB', 'njd': '暂无实况', 'qy': '1011', 'rain': '0'}}"""
   import requestsdef write_file(**kwargs):"""将天气信息拼接起来,并写入到文件格式要求:1. 每个城市的天气占一行2. 每行的格式为:city-北京,cityid-101010100,temp-18..."""# 补充代码def get_weather(code):""" 获取天气信息 """url = "http://www.weather.com.cn/data/ks/{}.html".format(code)res = requests.get(url=url)res.encoding = "utf-8"weather_dict = res.json()return weather_dictcity_list = [{'code': "101020100", 'title': "上海"},{'code': "101010100", 'title': "北京"},]# 补充代码

完整代码:

import requestsdef write_file(**kwargs):data_list = []row_dict = kwargs["weatherinfo"]for k, v in row_dict.items():group = "{}-{}".format(k, v)data_list.append(group)row_string = ",".join(data_list)with open('xxxx.txt', mode='a', encoding="utf-8") as file_object:file_object.write("{}\n".format(row_string))def get_weather(code):url = "http://www.weather.com.cn/data/ks/{}.html".format(code)res = requests.get(url=url)res.encoding = "utf-8"weather_dict = res.json()return weather_dictcity_list = [{'code': "101020100", 'title': "上海"},{'code': "101010100", 'title': "北京"},
]for item in city_list:# 101020100result_dict = get_weather(item["code"])write_file(**result_dict)
  1. 看代码写结果

    def func():return 1,2,3val = func()
    print( type(val) == tuple )   # True
    print( type(val) == list )    # False
    

补充: print( type(val) == tuple ) # True 该条件的具体使用例子

info = {"1": [11, 22, 33],"2": {'k1': 123, "k2": 456, "k3": "999"}
}
index = input("请输入序号:")
value = info[index]
if type(value) == list:print(value[0], value[1], value[2])
elif type(value) == dict:print(value['k1'], value['k2'], value['k3'])
  1. 看代码写结果
   def func(users,name):users.append(name)print(users)result = func(['张三','李四'],'王五')print(result)# 输出:['张三', '李四', '王五']None
  1. 看代码写结果

    def func(v1):return v1 * 2def bar(arg):return "%s 好吗?" %(arg,)val = func('你')  # 你你
    data = bar(val)
    print(data)# 输出:
    你你 好吗?
    
  2. 看代码写结果

    def func(v1):return v1* 2def bar(arg):msg = "%s 好吗?" %(arg,)print(msg) val = func('你')
    data = bar(val)
    print(data)#  输出:
    你你 好吗?
    None																					
    
  3. 看代码写结果

    def func():data = 2 * 2return datadata_list = [func,func,func]
    for item in data_list:v = item()print(v)# 输出:
    4
    4
    4
  4. 分析代码,写结果:

def func(handler, **kwargs):# handler() -> killer()# kwargs = {"name": "张三三", "age": 18}extra = {"code": 123,"name": "张三三"}kwargs.update(extra)# kwargs = {"name": "张三三", "age": 18,"code": 123,}return handler(**kwargs)def something(**kwargs):return len(kwargs)def killer(**kwargs):# {"name": "张三三", "age": 18,"code": 123,}key_list = []for key in kwargs.keys():key_list.append(key)return key_list # ["name","age","code"]v1 = func(something, k1=123, k2=456)
print(v1)  # 4v2 = func(killer, **{"name": "张三三", "age": 18})
print(v2)  # ["name","age","code"]# 输出:
4
["name","age","code"]
  1. 两个结果输出的分别是什么?并简述其原因。

    def func():return 123v1 = [func,func,func,func,]
    print(v1)  #  # 列表,内部元素都是函数(将函数名放在列表的索引位置,函数名代指函数)v2 = [func(),func(),func(),func()]
    print(v2)  #  # 列表,内部元素都是123(执行函数之后,将函数的返回值放在列表的索引位置)# 输出:
    [<function func at 0x00000263B83669D0>, <function func at 0x00000263B83669D0>, <function func at 0x00000263B83669D0>, <function func at 0x00000263B83669D0>]
    [123, 123, 123, 123]
    
  2. 看代码结果

    v1 = '张三三'def func():print(v1)func()
    func()# 输出:
    张三三
    张三三
  3. 看代码结果

    v1 = '张三三'def func():print(v1)func()
    v1 = '好人'
    func()# 输出:
    张三三
    好人
    
  4. 看代码写结果

    NUM_LIST = []
    SIZE = 18
    def f1():NUM_LIST.append(8)SIZE = 19def f2():print(NUM_LIST)print(SIZE)f2()
    f1()
    f2()# 输出:
    []
    18
    [8]
    18
    
  5. 看代码写结果

    NUM_LIST = []
    SIZE = 18
    def f1():global NUM_LISTglobal SIZENUM_LIST.append(8)SIZE = 19def f2():print(NUM_LIST)print(SIZE)f2()
    f1()
    f2()# 输出:
    []
    18
    [8]
    19
    
  6. 根据要求实现资源下载器。

    • 启动后,让用户选择专区,每个专区用单独的函数实现,提供的专区如下:

      • 下载 花瓣网图片专区
      • 下载 抖音短视频专区
      • 下载 NBA锦集 专区
    • 在用户选择了某个功能之后,表示进入某下载专区,在里面循环提示用户可以下载的内容选项(已下载过的则不再提示下载)
      提醒:可基于全部变量保存已下载过得资源。

    • 在某个专区中,如果用户输入(Q/q)表示 退出上一级,即:选择专区。

    • 在选择专区如果输入Q/q则退出整个程序。

    • 每个专区实现下载的案例如下:

      • 图片
# 可供用户下载的图片如下
image_dict = {"1":("吉他男神","https://hbimg.huabanimg.com/51d46dc32abe7ac7f83b94c67bb88cacc46869954f478-aP4Q3V"),"2":("漫画美女","https://hbimg.huabanimg.com/703fdb063bdc37b11033ef794f9b3a7adfa01fd21a6d1-wTFbnO"),"3":("游戏地图","https://hbimg.huabanimg.com/b438d8c61ed2abf50ca94e00f257ca7a223e3b364b471-xrzoQd"),"4":("alex媳妇","https://hbimg.huabanimg.com/4edba1ed6a71797f52355aa1de5af961b85bf824cb71-px1nZz"),
}
 # 下载图片示例import requestres = requests.get(url="https://hbimg.huabanimg.com/4edba1ed6a71797f52355aa1de5af961b85bf824cb71-px1nZz",headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"})with open("alex媳妇.png",mode="wb") as f:f.write(res.content)
  - 短视频
 # 可供用户下载的短视频如下video_dict = {"1":{"title":"东北F4模仿秀",'url':"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f570000bvbmace0gvch7lo53oog"},"2":{"title":"卡特扣篮",'url':"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34q1g"},"3":{"title":"罗斯mvp",'url':"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg"},}
  # 下载视频示例import requestsres = requests.get(url="https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg",headers={"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"})with open('罗斯mvp.mp4', mode='wb') as f:f.write(res.content)
  - NBA
  # 可供用户下载的NBA视频如下nba_dict = {"1":{"title":"威少奇才首秀三双","url":"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300fc20000bvi413nedtlt5abaa8tg&ratio=720p&line=0"},"2":{"title":"塔图姆三分准绝杀","url":"https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0d00fb60000bvi0ba63vni5gqts0uag&ratio=720p&line=0"}}
  # 下载示例import requestsres = requests.get(url="https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0d00fb60000bvi0ba63vni5gqts0uag&ratio=720p&line=0",headers={"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"})with open('塔图姆三分准绝杀.mp4', mode='wb') as f:f.write(res.content)
import requestsDB = {"1": {"area": "花瓣网图片专区","total_dict": {"1": ("吉他男神", "https://hbimg.huabanimg.com/51d46dc32abe7ac7f83b94c67bb88cacc46869954f478-aP4Q3V"),"2": ("漫画美女", "https://hbimg.huabanimg.com/703fdb063bdc37b11033ef794f9b3a7adfa01fd21a6d1-wTFbnO"),"3": ("游戏地图", "https://hbimg.huabanimg.com/b438d8c61ed2abf50ca94e00f257ca7a223e3b364b471-xrzoQd"),"4": ("张三三", "https://hbimg.huabanimg.com/4edba1ed6a71797f52355aa1de5af961b85bf824cb71-px1nZz"),},"ext": "png","selected": set()},"2": {"area": "抖音短视频专区","total_dict": {"1": {"title": "东北F4模仿秀",'url': "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f570000bvbmace0gvch7lo53oog"},"2": {"title": "卡特扣篮",'url': "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f3e0000bv52fpn5t6p007e34q1g"},"3": {"title": "罗斯mvp",'url': "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg"},},"ext": "mp4","selected": set()},"3": {"area": "NBA锦集专区","total_dict": {"1": {"title": "威少奇才首秀三双","url": "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300fc20000bvi413nedtlt5abaa8tg&ratio=720p&line=0"},"2": {"title": "塔图姆三分准绝杀","url": "https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0d00fb60000bvi0ba63vni5gqts0uag&ratio=720p&line=0"}},"ext": "mp4","selected": set()},
}def download(file_path, url):res = requests.get(url=url,headers={"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"})with open(file_path, mode='wb') as f:f.write(res.content)def handler(area_info):# 进入专区提醒summary = "欢迎进入{}".format(area_info['area'])print(summary)# 专区中选择下载while True:text_list = []for num, item in area_info['total_dict'].items():if num in area_info['selected']:continueif type(item) == tuple:data = "{}.{}".format(num, item[0])else:data = "{}.{}".format(num, item["title"])text_list.append(data)if text_list:text = ";".join(text_list)else:text = "无可下载选项"print(text)index = input("请输入要选择的序号(Q/q退出):")if index.upper() == "Q":returnif index in area_info['selected']:print("已下载,无法再继续下载,请重新选择!")continuegroup = area_info['total_dict'].get(index)if not group:print("序号不存在,请重新选择")continueif type(group) == tuple:title, url = groupelse:title, url = group['title'], group['url']file_path = "{}.{}".format(title, area_info['ext'])download(file_path, url)area_info['selected'].add(index)print("欢迎使用xxx系统")
while True:print("1.花瓣网图片专区;2.抖音短视频专区;3.NBA锦集 专区 ")choice = input("请选择序号(Q/q退出):")if choice.upper() == "Q":break# 选择序号: 去db中找对应的字典信息area_dict = DB.get(choice)if not area_dict:print("输入错误,请重新选择!")continue# 进入专区(area_dict选择的专区信息)handler(area_dict)

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

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

相关文章

【关于C++中----特殊类设计和单例模式】

文章目录 一、设计一个类&#xff0c;不能被拷贝1.1C98的实现方法及其弊端1.2 C11的实现方法 二、设计一个类&#xff0c;只能在堆上创建对象三、设计一个类&#xff0c;只能在栈上创建对象四、设计一个类&#xff0c;不能被继承五、设计一个类&#xff0c;只能创建一个对象(单…

Leecode316: 去除重复字母

下面这里使用有序map——TreeMap来实现Map接口&#xff0c;但是相对顺序是不能改变的&#xff01;这样会使得后面的跑到前面去&#xff0c;所以有问题 最简单的思想肯定是暴力思想&#xff0c;就是从前往后寻找&#xff0c;一旦遇到存在的情况就. 重点在于明确两点&#xff1a…

【Docker】了解和使用Docker

文章底部有投票活动&#xff0c;赶快参与进来吧&#x1f603; 相信大家在开发过程中都听说过 Docker 一词&#xff0c;至于 Docker 在开发中扮演的角色&#xff0c;估计好多人都说不上来&#xff0c;今天就让阿Q带大家一起揭开它神秘的面纱&#xff01; 文章目录 什么是容器&a…

dolphinscheduler伪分布式安装

1、上传安装包 2、安装 #解压 重命名 [rootdatacollection conf]# cd /opt/modules/ [rootdatacollection modules]# tar -zxf apache-dolphinscheduler-2.0.6-bin.tar.gz -C /opt/installs/ [rootdatacollection modules]# cd ../installs/ [rootdatacollection installs]# m…

【HDFS】与单测编写相关的一些工具类及方法(大纲篇)持续更新

集群创建启动 MiniDFSCluster 可以用这个类创建一个单进程的DFS集群用来进行单元测试。 一般是采用MiniDFSCluster$Builder去建造出一个MiniDFSCluster对象。builder可以指定很多参数 MiniRouterDFSCluster 用来模拟一个有多台Router的HDFS联邦集群。 写一个测试文件 副…

【广州华锐互动】智慧交通3D可视化交互平台

智慧交通3D可视化交互平台由广州华锐互动开发&#xff0c;是一种基于现代科技的智能交通管理系统&#xff0c;它能够实现对车站内部人员和车辆的实时监控和管理。该平台采用了先进的三维可视化技术&#xff0c;将车站内部的结构和设备以立体、直观的方式呈现在用户面前&#xf…

LangChain大型语言模型(LLM)应用开发(四):QA over Documents

LangChain是一个基于大语言模型&#xff08;如ChatGPT&#xff09;用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…

springboot整合ehcache和redis实现多级缓存实战案例

一、概述 在实际的工作中&#xff0c;我们通常会使用多级缓存机制&#xff0c;将本地缓存和分布式缓存结合起来&#xff0c;从而提高系统性能和响应速度。本文通过springboot整合ehcache和redis实现多级缓存案例实战&#xff0c;从源码角度分析下多级缓存实现原理。 二、实战案…

赛效:如何将PDF文件免费转换成Word文档

1&#xff1a;在网页上打开wdashi&#xff0c;默认进入PDF转Word页面&#xff0c;点击中间的上传文件图标。 2&#xff1a;将PDF文件添加上去之后&#xff0c;点击右下角的“开始转换”。 3&#xff1a;稍等片刻转换成功后&#xff0c;点击绿色的“立即下载”按钮&#xff0c;将…

做私域选个微还是企微,哪个有优势?

做私域&#xff0c;你必须要有一个&#xff0c;引流新客户及留存老客户的地方。 于是&#xff0c;就有很多人讨论或者纠结&#xff1a;做私域&#xff0c;选择个人微信&#xff1f;还是企业微信&#xff1f; 让我们一起来看看个人微信和企业微信在功能和使用上有哪些区别&…

QT学习之旅 - Udp编程注意点

我们在QT中可以使用信号和槽来判断UDP接收数据 connect(qudpsocket,&QUdpSocket::readyRead,this,[](){});接收到的是一个完整的包&#xff0c;但是这个包的大小是不固定的&#xff0c;你可以传入任何格式的包&#xff0c;也不受限制&#xff0c;所以&#xff0c;任何ip端口…

[SpringBoot]单点登录

关于单点登录 单点登录的基本实现思想&#xff1a; 当客户端提交登录请求时&#xff0c;服务器端在验证登录成功后&#xff0c;将生成此用户对应的JWT数据&#xff0c;并响应到客户端 客户端在后续的访问中&#xff0c;将自行携带JWT数据发起请求&#xff0c;通常&#xff0c…

NDK编译系列:手机终端运行可执行文件的方法

NDK编译系列&#xff1a;手机终端运行可执行文件的方法 前言总体流程补充说明 前言 该方式为PC上的NDK工具生成的可执行文件和库&#xff0c;利用adb导入到手机&#xff08;未采用Andriod Studio生成带界面的apk文件&#xff09;&#xff0c;直接通过windows的命令窗在安卓原生…

一篇搞懂steam/csgo搬砖原理

接触csgo游戏搬砖项目三年了&#xff0c;也有在别的论坛交流心得。让我无语的是有些已经游戏搬砖差不多半年&#xff0c;却还告诉我没有赚到钱&#xff0c;又或者说时常到可出售的时候利润少的可怕&#xff0c;总是说这个行业说水太深了&#xff01;那么请你告诉我&#xff0c;…

快快快快快快快快快快排

作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;Python等 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 望小伙伴们点赞&#x1f44d;收藏✨加关注哟&#x1f495;&#x1f495; C语言实现快排☺️ ℹ️…

Python爬虫学习笔记(六)————BeautifulSoup(bs4)解析

目录 1.bs4基本简介 &#xff08;1&#xff09;BeautifulSoup简称 &#xff08;2&#xff09;什么是BeatifulSoup&#xff1f; &#xff08;3&#xff09;优缺点 2.bs4安装以及创建 &#xff08;1&#xff09;安装 &#xff08;2&#xff09;导入 &am…

一个月学通Python(十二):Python发送电子邮件及图像办公文档处理

专栏介绍 结合自身经验和内部资料总结的Python教程,每天3章,1个月就能全方位的完成Python的学习并进行实战开发。加油吧!卷起来! 全部文章请访问专栏:《Python全栈教程(0基础》 文章目录 专栏介绍网络应用开发发送电子邮件发送短信图像和办公文档处理操作图像计算机图像…

Ceph 块存储系统 RBD 接口

-创建 Ceph 块存储系统 RBD 接口- 1、创建一个名为 rbd-demo 的专门用于 RBD 的存储池 ceph osd pool create rbd-demo 64 642、将存储池转换为 RBD 模式 ceph osd pool application enable rbd-demo rbd3、初始化存储池 rbd pool init -p rbd-demo # -p 等同于 --pool4、…

jenkins手把手教你从入门到放弃01-jenkins简介(详解)

一、简介 jenkins是一个可扩展的持续集成引擎。持续集成&#xff0c;也就是通常所说的CI&#xff08;Continues Integration&#xff09;&#xff0c;可以说是现代软件技术开发的基础。持续集成是一种软件开发实践&#xff0c; 即团队开发成员经常集成他们的工作&#xff0c;通…

STM32 Proteus仿真LCD12864火灾检测烟雾火焰温度报警器MQ2 -0064

STM32 Proteus仿真LCD12864火灾检测烟雾火焰温度报警器MQ2 -0064 Proteus仿真小实验&#xff1a; STM32 Proteus仿真LCD12864火灾检测烟雾火焰温度报警器MQ2 -0064 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD12864 液晶显示DS18B20 温度传感器多个按键电位…