文章目录
Day6
函数也是对象,内存底层分析
Python中,“一切都是对象”。实际上,执行def定义函数后,系统就创建了相应的函数对象。
def func1():
print("new pants")
func1()
c = func1 # 把func1的内存地址赋值给了c
print(func1, c)
# <function func1 at 0x0000028D9FCFC1E0> <function func1 at 0x0000028D9FCFC1E0>
c() # c()也可以调用fucn1函数
变量的作用域(全局变量和局部变量)
def func1():
b = 4 # 局部变量
print(b * 10)
print(a) # local variable 'a' referenced before assignment,此处a被当作局部变量
a = 300 # 在print(a)之前,还未定义a=300,所以出现如上报错
print(a)
func1()
# 当执行函数fucn1()时,在栈内创建栈帧(stack frame),执行结束,栈帧就不存在了
func1()
# 不能在函数外调用局部变量,如print(b),则报错
- 输出局部变量和全局变量
a = 3 # 全局变量
def func1():
b = 4 # 局部变量
print(b * 10)
global a # 如果要在函数内改变全局变量的值,增加global关键字声明
a = 300 # 在print(a)之前,还未定义a=300,所以出现如上报错
print("局部", locals()) # 打印输出的局部变量
#局部 {'b': 4}
print("全局", globals()) # 打印输出的全局变量
'''
全局 {'__name__': '__main__', '__doc__': '\n@author: z&y\n@file: 函数变量的作用域.py\n@time: 2020/06/29\n@desc:\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000020B8EB21CC0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Tools/PythonProject/MyTest/Day_6/函数变量的作用域.py', '__cached__': None, 'a': 300, 'func1': <function func1 at 0x0000020B8EE95378>}
'''
func1()
print(a) # 此时全局变量a的值变为300
局部变量和全局变量效率测试
import math
import time
# 局部变量的查询和访问速度比全局变量快
def func1():
start = time.time()
for i in range(10000000):
math.sqrt(30) # 调用了全局的import math
end = time.time()
print("耗时{0}".format((end - start)))
# 耗时1.1883010864257812
func1()
def func2():
start = time.time()
b = math.sqrt
for i in range(10000000):
b(30) # 从局部去引用math.sqrt
end = time.time()
print("耗时{0}".format((end - start)))
# 耗时0.8516945838928223
func2()
参数的传递
函数的参数传递本质上就是:从实参到形参的赋值操作。Python中“一切皆对象”,所有的赋值操作都是“引用的赋值”。所以,Python中参数的传递都是“引用传递”,不是“值传递”。具体操作时分为两类:
- 对“可变对象”进行“写操作”,直接作用于原对象本身
# 传递可变对象
a = [10, 20]
print(id(a))
print(a)
print("********")
def func1(m):
print(id(m))
m.append(30)
print(id(m))
func1(a)
print(id(a))
print(a)
# 三次打印id(a),内存地址都是2093959504456
传递不可变对象的引用
浅拷贝和深拷贝
-
浅拷贝:不拷贝子对象的内容,只是拷贝对象的引用。
# 浅拷贝 import copy a = [10, 20, [5, 6]] b = copy.copy(a) print(id(a), id(b)) # 列表a与b的内存空间地址不同,但指向的对象相同 b.append(30) # 仅对b增加了30,因此a中未增加30 b[2].append(7) # a与b同时修改 print("浅拷贝......") print("a", a) # a [10, 20, [5, 6, 7]] print("b", b) # b [10, 20, [5, 6, 7], 30]
-
深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象。
# 深拷贝 a = [10, 20, [5, 6]] b = copy.deepcopy(a) print(id(a), id(b)) # 列表a与b的内存空间地址不同,但指向的对象也不相同 b.append(30) b[2].append(7) print("深拷贝......") print("a", a) # a [10, 20, [5, 6]] print("b", b) # b [10, 20, [5, 6, 7], 30]
-
传递不可变对象时,不可对象中包含的子对象是可变的,则方法内修改了这个可变对象,源对象也发生了改变
# 传递不可变对象时,不可对象中包含的子对象是可变的,则方法内修改了这个可变对象,源对象也发生了改变 a = (10, 20, [5, 6]) print("a:", id(a)) def func1(m): print("m:", id(m)) # m[0] = 15 #报错:'tuple' object does not support item assignment,不可变对象不能修改 m[2][0] = 888 print(m) print("m:", id(m)) func1(a) print(a)
参数的几种类型
位置参数
默认值参数
命名参数
def func1(a, b, c, d):
print("{0}--{1}--{2}--{3}".format(a, b, c, d))
def func2(a, b, c=10, d=15): # 默认值参数(c,d),必须位于其他参数后面!
print("{0}--{1}--{2}--{3}".format(a, b, c, d))
func1(10, 20, 30, 40) # 位置参数
# func1(10,20) #参数个数不匹配,报错
func1(d=20, b=40, a=10, c=100) # 命名参数,通过形参名称来匹配,不用考虑参数循序
func2(2, 3) # 2--3--10--15
func2(2, 3, 4) # 2--3--4--15
可变参数
可变参数指的是”可变数量的参数“。分两种情况:
- *args,将多个参数收集到一个”元组“对象中;
- **kwargs,将多个参数收集到一个”字典”对象中;
def func1(a, b, *args):
print(a, b, args) # 1 2 (3, 4, 5, 6)
print(a, b, *args) # 1 2 3, 4, 5, 6
func1(1, 2, 3, 4, 5, 6)
def func2(a, b, **kwargs):
print(a, b, kwargs) # 1 2 {'name': 'gaoqi', 'age': 18}
# print(a, b, **kwargs) #报错
func2(1, 2, name="gaoqi", age=18)
def func3(a, b, *args, **kwargs):
print(a, b, args, kwargs) # 1 2 (3, 4, 5) {'name': 'gaoqi', 'age': 18}
func3(1, 2, 3, 4, 5, name="gaoqi", age=18)
lambda表达式和匿名函数
f = lambda a, b, c, d: a * b * c * d
print(f(2, 3, 4, 5))
# func1等同于lambda
def func1(a, b, c, d):
return a * b * c * d
# 把函数对象作为列表的元素,放到列表中
g = [lambda a: a * 2, lambda b: b * 3, ]
print(g[0](6)) # g[0]指的是函数对象lambda a: a * 2,g[0]()则对lambda进行调用
h = [func1, func1]
print(h[0](3, 4, 5, 6))
eval()函数
# 用途:可以直接使用从外部(server,文件等)接收的字符串
s = "print('abcde')"
eval(s)
a = 10
b = 20
c = eval("a+b")
print(c)
dic1 = dict(a=100, b=200)
d = eval("a+b") # 此处的a,b为之前定义的变量,即a=10,b=20
print(d)
d = eval("a+b", dic1)
print(d) # 此处的a,b为字典dic1中的a,b,因此a=100,b=200
递归函数
def func1(n):
print("func01:", n)
if n == 0:
print("over")
else:
func1(n - 1)
print("func01***", n)
func1(4)
- 使用递归函数,计算阶乘
# 使用递归函数计算阶乘
def factorial(n):
if n == 1:
return 1
else:
return n * factorial(n - 1)
res = factorial(5)
print(res)