- return后面的值如果有逗号,则默认会将返回值转换成元组再返回。
def func():
return 1,2,3
value = func()
print(value) # (1,2,3)
- 关于地址
v1 = [11,22,33] v2 = v1 print( id(v1),id(v2) ) #指向同一块地址 v1 = [11,22,33] v2 = [11,22,33] print( id(v1),id(v2) ) #指向不同地址 # 对于不可变类型如str等 python内部优化存储在缓存中 v1 = 3 v11 = 3 print(id(v1),id(v11)) #指向同一块地址
- Python的参数默认传递的是内存地址
def func(data):
print(data, id(data)) # xxx 140247057684592
v1 = "xxx"
print(id(v1)) # 140247057684592
func(v1)
Python参数的这一特性有两个好处:
-
节省内存
-
对于可变类型且函数中修改元素的内容,所有的地方都会修改。可变类型:列表、字典、集合。
# 可变类型 & 修改内部修改
def func(data):
data.append(999)
v1 = [11,22,33]
func(v1)
print(v1) # [11,22,33,666]
# 特殊情况:可变类型 & 重新赋值
def func(data):
data = ["卢本伟","alex"] #data指向的地址从[11,22,33]改变为["卢本伟","alex"]
v1 = [11,22,33]
func(v1)
print(v1) # [11,22,33]
# 特殊情况:不可变类型,无法修改内部元素,只能重新赋值。
def func(data):
data = "alex"
v1 = "卢本伟"
func(v1)
其他很多编程语言执行函数时,默认传参时会将数据重新拷贝一份,会浪费内存。
提示注意:其他语言也可以通过 ref 等关键字来实现传递内存地址。
- global关键字
默认情况下,在局部作用域对全局变量只能进行:读取和修改内部元素(可变类型),无法对全局变量进行重新赋值。
如果想要在局部作用域中对全局变量重新赋值,则可以基于 global关键字实现,例如:
COUNTRY = "中国"
CITY_LIST = ["北京","上海","深圳"]
def download():
url = "http://www.xxx.com"
global CITY_LIST
CITY_LIST = ["河北","河南","山西"]
print(CITY_LIST)
global COUNTRY
COUNTRY = "中华人民共和国"
print(COUNTRY)
def upload():
file_name = "rose.zip"
print(COUNTRY)
print(CITY_LIST)
download()
upload()
其实,还有一个nolocal关键字,用的比较少,此处作为了解即可。
name = 'root'
def outer():
name = "武沛齐"
def inner():
nonlocal name
name = 123
inner()
print(name)
outer()
print(name)
name = 'root'
def outer():
name = 'alex'
def func():
name = "武沛齐"
def inner():
nonlocal name
name = 123
inner()
print(name)
func()
print(name)
outer()
print(name)
'''
123
alex
root
'''
name = 'root'
def outer():
name = 'alex'
def func():
nonlocal name
name = "武沛齐"
def inner():
nonlocal name
name = 123
inner()
print(name)
func()
print(name)
outer()
print(name)
'''
123
123
root
'''
当然,如果你不想让外部的变量和函数内部参数的变量一致,也可以选择将外部值拷贝一份,再传给函数。
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]
- 函数的返回值是内存地址
def func():
data = [11, 22, 33]
return data
v1 = func()
print(v1) # [11,22,33]
上述代码的执行过程:
-
执行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 data
v1 = 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内存地址)
- 函数的参数问题
坑
# 在函数内存中会维护一块区域存储 [1,2,666,666,666] 100010001
def func(a1,a2=[1,2]):
a2.append(666)
print(a1,a2)
# a1=100
# a2 -> 100010001
func(100) # 100 [1,2,666]
# a1=200
# a2 -> 100010001
func(200) # 200 [1,2,666,666]
# a1=99
# a2 -> 1111111101
func(99,[77,88]) # 66 [77,88,666]
# a1=300
# a2 -> 100010001
func(300) # 300 [1,2,666,666,666]
大坑
# 在内部会维护一块区域存储 [1, 2, 10, 20,40 ] ,内存地址 1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
print(v1) # [1, 2, 10]
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
print(v2) # [1, 2, 10, 20 ]
# a1=30
# a2 -> 11111111111 [11, 22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
print(v3) # [11, 22,30]
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
v4 = func(40)
print(v4) # [1, 2, 10, 20,40 ]
深坑
# 内存中创建空间存储 [1, 2, 10, 20, 40] 地址:1010101010
def func(a1, a2=[1, 2]):
a2.append(a1)
return a2
# a1=10
# a2 -> 1010101010
# v1 -> 1010101010
v1 = func(10)
# a1=20
# a2 -> 1010101010
# v2 -> 1010101010
v2 = func(20)
# a1=30
# a2 -> 11111111111 [11,22,30]
# v3 -> 11111111111
v3 = func(30, [11, 22])
# a1=40
# a2 -> 1010101010
# v4 -> 1010101010
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]
- 动态参数
动态参数,定义函数时在形参位置用 *或** 可以接任意个参数。
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] )
func( **{"a1":11,"a2":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中)。
例如:
def func(*args,**kwargs):
print(args,kwargs)
params = {"k1":"v2","k2":"v2"}
func(params) # ({"k1":"v2","k2":"v2"}, ) {}
func(**params) # (), {"k1":"v2","k2":"v2"}
所以,在使用format字符串格式化时,可以可以这样:
v1 = "我是{},年龄:{}。".format("武沛齐",18)
v2 = "我是{name},年龄:{age}。".format(name="武沛齐",age=18)
v3 = "我是{},年龄:{}。".format(*["武沛齐",18])
v4 = "我是{name},年龄:{age}。".format(**{"name":"武沛齐","age":18})
- 函数名可以做参数,注意是函数名,加括号后相当于执行函数了。
def send_message(phone,content):
"""发送消息"""
pass
def send_image(img_path, content):
"""发送图片"""
pass
def send_emoji(emoji):
"""发送表情"""
pass
def send_file(path):
"""发送文件"""
pass
function_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("输入选择的序号") # 1
item = function_dict.get(choice) # [ send_message, ['15131255089', '你好呀']],
if not item:
print("输入错误")
else:
# 执行函数
func = item[0] # send_message
param_list = item[1] # ['15131255089', '你好呀']
func(*param_list) # send_message(*['15131255089', '你好呀'])
- 将函数名赋值给其他变量,函数名其实就个变量,代指某函数;如果将函数名赋值给另外一个变量,则此变量也会代指该函数,例如:
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函数
func(11,22)
# func重新赋值成一个字符串
func = "lbw"
print(func)
def func(a1,a2):
print(a1+a2)
func(1,2)
def func():
print(666)
func()
注意:由于函数名被重新定义之后,就会变量新被定义的值,所以大家在自定义函数时,不要与python内置的函数同名,否则会覆盖内置函数的功能,例如:
# len内置函数用于计算值得长度
v1 = len("武沛齐")
print(v1) # 3
# len重新定义成另外一个函数
def len(a1,a2):
return a1 + a2
# 以后执行len函数,只能按照重新定义的来使用
v3 = len(1,2)
print(v3)
- 局部作用域和全局作用域变量同名
以内部为主 内部没有,去上一级/全局寻找
a = 222
b = 333
def fuc():
b = 123
c = 456
print(a, b, c)
fuc()
print(b, '全局')
'''
222 123 456
333 全局
'''
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)
'''
(11, 22, 33) {}
完毕
([11, 22, 33],) {}
完毕
(11, 22, 33) {}
完毕
() {'k1': 123, 'k2': 456}
完毕
({'k1': 123, 'k2': 456},) {}
完毕
() {'k1': 123, 'k2': 456}
完毕
([11, 22, 33],) {'k1': 123, 'k2': 456}
完毕
(11, 22, 33) {'k1': 123, 'k2': 456}
完毕
'''
Process finished with exit code 0



