Python 第四章 函数
1.函数入门
-
定义:def 函数名(形参列表):
//可执行语句
[return [返回值]]
-
调用:函数名(实参1,实参2。。)
2.递归函数
在一个函数体内调用自身被称为递归。
3.函数的参数
按照形参位置传入的参数叫位置参数,必须按照定义函数时指定的顺序传入参数值。
如果根据参数名来传入参数值,可以不按顺序,叫关键字参数。
关键字参数必须在位置参数的后面。
在形参前面加一个*,意味着该参数可以接收多个实参,会被当做元组放入;当把个数可变的形参放在前面时,后面必须使用关键字参数。
使用**的关键字参数收集成字典
def test(x,y,z=3,*books,**scores):
print(x,y,z)
print(books)
print(scores)
test(1,2,3,'A','B',语文=89,数学=98)
# 输出
1 2 3
('A', 'B')
{'语文': 89, '数学': 98}
4.逆向参数收集
指在程序已有列表、元组、字典等对象的前提下,把他们的元素拆开后传给函数的参数
def foo(name,*nums):
print(name)
print(nums)
my_tuple = (1,2,3)
foo('fkit',my_tuple)
# 输出
fkit
((1, 2, 3),)
foo(*my_tuple)
# 输出
1
(2, 3)
5.变量作用域
每个函数执行时,系统都会为该函数分配一块临时内存空间,所有的局部变量都被保存在这块临时内存空间。当函数执行完,这块内存空间就被释放,局部变量也就失效了,因此离开函数后就不能在访问局部变量了。
全局变量意味着在所有函数中都能被访问。
变量和它的值就像一个字典。
Python提供以下3种函数获取指定范围内的变量字典。
- 局部变量:在函数中定义的变量,包括参数,都被称为局部变量。
- 全局变量:在函数外面、全局范围内定义的变量,叫全局变量。
- globals():该函数返回全局变量内所有变量组成的变量字典。
- locals():返回当前局部范围内所有变量组成的变量字典。如果在全局范围内调用locals函数,同样会获得全局范围内所有变量组成的变量字典。
- vars(object):获取在指定对象范围内所有变量组成的变量字典。
- 通过locals获取的局部范围内的变量字典,即使对他修改也不会影响局部变量
def test():
age = 20
# 访问函数局部范围内的变量数组
print(locals()) # {'age':20}
# 通过函数局部范围内的变量数组访问age变量
print(locals()['age']) # 20
# 修改变量的值
locals()['age'] = 12
print('xxx',age) # 依然输出20
globals()['x'] = 19
test()
x = 5
y = 20
print(globals()) # {...,'x':5,'y':20}
print(locals()) # {...,'x':5,'y':20}
print(x) # 5
print(globals()['x']) # 5
globals()['x'] = 39
print(x) # 39
locals()['x'] = 99
print(x) # 99
全局变量默认可以在所有函数内被访问,但如果在函数中定义了与全局变量同名的变量,此时就会发生局部变量遮蔽全局变量的情形。例如
name = 'Jim'
def test():
# 直接访问全局变量
print(name) # Jim
# name = 'Kyrie'
test()
print(name) # Jim
如果加了第五行会报错,python语法规定:在函数内部对不存在的变量赋值时,默认就是重新定义了新的局部变量,这里就会把全局变量name遮蔽,所以会报错,说name变量还未定义。
有以下两种方法:
-
访问被遮蔽的全局变量
如果程序希望第四行能访问name全局变量,且在之后能重新定义name局部变量——也就是在函数中可以访问被遮蔽的全局变量,可以通过globals函数实现:
name = 'Jim' def test(): # 通过函数访问全局变量 print(globals()['name']) # Jim name = 'Kyrie' test() print(name) # Jim
-
在函数中声明全局变量
为了避免在函数中对全局变量赋值(不是重新定义局部变量),可使用global语句声明全局变量:
name = 'Jim' def test(): # 声明name是全局变量,后面的赋值语句不会重新定义局部变量 global name print(name) # Jim name = 'Kyrie' # 修改了全局变量 test() print(name) # Kyrie
6.局部函数
局部函数:放在函数体内定义的函数。
默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭函数内有效,其封闭函数也可以返回局部函数,以便程序在其他作用域中使用局部函数。
def get_math_func(type,nn):
def square(n):
return n*n
def cube(n):
return n*n*n
def factorial(n):
result = 1
for index in range(2,n+1):
result *= index
return result
if type == 'square':
return square(nn)
elif type == 'cube':
return cube(nn)
else:
return factorial(nn)
print(get_math_func('square',3)) # 9
print(get_math_func('cube',3)) # 27
print(get_math_func('',3)) # 6
局部函数内的变量也会遮蔽它所在函数内的局部变量;python提供nonlocal关键字声明访问赋值语句只是访问该函数所在函数内的局部变量。
7.函数高级内容
-
使用函数变量
-
使用函数作为函数形参
代码需要动态改变,即fn会改变
def map(data, fn): result = [] for e in data: result.append(fn(e)) return result def square(n): return n * n def cube(n): return n * n * n def factorial(n): result = 1 for index in range(2,n+1): result *= index return result data = [3,4,9,5,8] print(data) #[3, 4, 9, 5, 8] print(map(data,square)) #[9, 16, 81, 25, 64]
-
lambda表达式
语法格式:lambda x,y:x+y
冒号左边的是参数列表,右边是表达式的返回值。
-
计算代码计算时间
import time start = time.time() # 可以获取当前的时间戳 # 时间戳是从1970-01-01 00:00:00 UTC ~现在的秒数 print('start = ',start) # start = 1615089126.3489187 是现在UTC的秒数,要减8个小时 x = 0 for i in range(1,100000000): x +=i print(x) end = time.time() print('代码耗时{}秒'.format(end-start)) #start = 1615089358.4768794 #4999999950000000 #代码耗时7.5029296875秒
优化:
def cal_time(fn): start = time.time() fn() end = time.time() print('代码耗时{}秒'.format(end-start)) def demo(): x = 0 for i in range(1,100000000): x +=i print(x) cal_time(demo)
8.装饰器
import time
def cal_time(fn):
print('首先调用外部函数。')
def inner():
start = time.time()
fn()
end = time.time()
print('代码耗时{}秒'.format(end-start))
return inner
@cal_time # 首先会调用cal_time,然后把被装饰的函数传递给fn
def demo():
x = 0
for i in range(1,100000000):
x +=i
print(x)
# 当再次调用demo函数,它是cal_time函数的返回值inner
demo()
装饰器详解:
def funA(fn):
print('A')
fn() #执行传入的fn参数
return 'fkit'
'''
下面的装饰效果相当于funA(funB)
funB将会被替换(装饰)成该语句的返回值
由于funA函数返回fkit,因此funB就是fkit
'''
@funA # 相当于funA(funB)
def funB():
print('B')
print(funB) # funB被替换成fkit
# 输出
A
B
fkit
当程序使用@函数(比如函数A)装饰另一个函数,比如函数B,实际上完成以下两步:
- 将被修饰的函数(函数B)作为参数传给@符号引用的函数,比如函数A。
- 将函数B替换成第一步的返回值。
def foo(fn):
# 定义一个嵌套函数
def bar(*args):
print('===1===', args)
n = args[0]
print('===2===', n * (n - 1))
# 查看传给foo函数的fn函数
print(fn.__name__)
fn(n * (n - 1))
print("*" * 15)
return fn(n * (n - 1))
return bar
'''
下面的装饰效果相当于foo(my_test)
my_test将会被替换成该语句的返回值,也就是bar函数
'''
@foo
def my_test(a):
print("==my_test函数==", a)
# 打印my_test函数,将会看到就是bar函数
print(my_test) # <function foo.<locals>.bar at 0x00000264804C09D0>
# 下面代码看上去是调用my_test函数,实际是调用bar函数
my_test(10)
my_test(6, 5)
# 输出结果
<function foo.<locals>.bar at 0x00000264804C09D0>
===1=== (10,)
===2=== 90
my_test
==my_test函数== 90
***************
==my_test函数== 90
===1=== (6, 5)
===2=== 30
my_test
==my_test函数== 30
***************
==my_test函数== 30
上面程序定义了一个装饰器foo函数,该函数执行完返回bar函数,这意味着该@foo修饰的函数最终都会被替换为bar函数。
上面程序使用@foo修饰my_test函数,因此程序同样会执行foo(my_test),并将my_test替换成foo函数的返回值bar函数。