一、为什么要有函数?没有函数有什么问题?
1、组织结构不清晰,可读性差
2、代码冗余
3、可扩展性差
二、函数的分类:
1、内置函数:python解释器已经为我们定义好了的函数即内置函数,我们可以拿来就用而无需事先定义
2、自定义函数:我们自己根据需求,事先定制好我们自己的函数来实现某种功能,如果遇到相同的情景可以直接调用
三、定义函数的三种形式
第一种:无参函数 (应用场景仅仅只是执行一些操作)
1
2
|
def
foo():
print
(
'from foo'
)
|
第二种:有参函数 (需要根据外部传进来的参数,才能执行相应的逻辑)
有参函数分为形参和实参
形参包含:
1、位置形参:必选参数
1
2
3
4
|
def
foo(name,age,sex):
print
(name)
print
(age)
print
(sex)
|
2、默认函数:形参在定义时就已经为其赋值
实参包含:
1、关键字实参:按照key=value的形式定义的实参
2、位置实参:按照位置给形参传值
1
|
foo(
'jim'
,
18
,
'male'
)
|
还有一种是可变长参数:可变长参数指的是实参的个数多了,实参无非位置实参和关键字实参两种
形参处理按照位置定义的实参溢出的情况:*
1
2
3
4
5
|
def
foo(x,y,
*
args):
#*把位置实参多余的赋值给args, args=(3, 4, 5)
print
(x)
print
(y)
print
(args)
foo(
1
,
2
,
3
,
4
,
5
)
|
形参处理按照关键字定义的实参溢出的情况:**
1
2
3
4
5
|
def
foo(x,y,
*
*
awargs):
#**把位置实参多余的赋值给awargs, awargs={'z': 3, 'e': 4}
print
(x)
print
(y)
print
(awargs)
foo(x
=
1
,y
=
2
,z
=
3
,e
=
4
)
|
最后是命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
1
2
3
4
5
6
|
def
foo(x,y,
*
args,a
=
1
,
*
*
kwargs):
print
(x, y)
#处理传的参数后结果是1 2
print
(args)
#处理传的参数后结果是(3, 4, 5)
print
(a)
#处理传的参数后结果是1
print
(kwargs)
#处理传的参数后结果是{'c': 4, 'd': 5}
foo(
1
,
2
,
3
,
4
,
5
,c
=
4
,d
=
5
)
|
第三种:空函数 (设计代码结构)
四、函数调用
1、函数的返回值
return的返回值没有类型限制
1. 没有return:返回None,等同于return None
2. return 一个值:返回该值
3. return 多个值:返回的多个值以元组的形式
2、函数调用的三种形式
1、语句形式
2、表达式形式
3、当做另外一个函数的参数
1
2
3
4
5
6
7
8
9
|
def
foo(x,y):
if
x>
=
y:
return
x
else
:
return
y
foo(
1
,
2
)
#语句形式
res
=
foo(
1
,
2
)
*
10
#表达式形式,取到的结果在表达式里做运算
res2
=
foo(foo(
1
,
2
),
3
)
#函数调用可以当做另外一个函数的参数
print
(res2)
|
五、名称空间与作用域
1、名称空间的加载顺序
#1、python解释器先启动,因而首先加载的是:内置名称空间
#2、执行*.py文件,然后以文件为基础,加载全局名称空间
#3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
2、名称空间的查找顺序
局部名称空间--->全局名称空间--->内置名称空间
3、作用域
作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关
1
2
3
4
5
6
|
x
=
1
def
num():
x
=
2
print
(x)
#x的值为2
x
=
3
num()
|
六、闭包函数
1、闭包函数: 内部函数包含对外部作用域而非全局作用域的引用
2、闭包函数的意义
返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
1
2
3
4
5
6
7
8
9
10
|
import
requests
def
outter(url):
# url = 'https://www.baidu.com'
def
get():
response
=
requests.get(url)
if
response.status_code
=
=
200
:
#取状态码是200的
print
(
len
(response.text))
return
get
baidu
=
outter(
'https://www.baidu.com'
)
baidu()
|
七、装饰器
1、遵循的原则
开放封闭原则:对修改封闭,对扩展开放
1、不修改被装饰对象的源代码
2、不修改被装饰对象的调用方式
2、模拟打开网页的时间
1
2
3
4
5
6
7
8
9
10
|
import
time
def
index():
time.sleep(
3
)
print
(
'hello'
)
def
inner():
start
=
time.time()
#开始的时间
index()
stop
=
time.time()
#网页打开后的时间
print
(stop
-
start)
inner()
|
上面的实现了我们的需要,但是inner函数包含index函数,只能统计index的执行时间,不利于代码的重用,所以要改进一下
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
time
def
index():
time.sleep(
3
)
print
(
'hello'
)
def
timmer(func):
def
inner():
start
=
time.time()
func()
stop
=
time.time()
print
(stop
-
start)
return
inner
index
=
timmer(index)
#index不是以前的index,是重新更名的,这样对用户调用index函数时,操作不会改变
index()
|
改为装饰器语法的写法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
time
def
timmer(func):
def
inner():
start
=
time.time()
func()
stop
=
time.time()
print
(stop
-
start)
return
inner
@timmer
#装饰器的语法,相当于index=timmer(index)
def
index():
time.sleep(
1
)
print
(
'hello'
)
index()
|
最后版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
time
def
timmer(func):
def
wrapper(
*
args,
*
*
kwargs):
start_time
=
time.time()
res
=
func(
*
args,
*
*
kwargs)
stop_time
=
time.time()
print
(
'run time is %s'
%
(stop_time
-
start_time))
return
res
return
wrapper
@timmer
def
foo():
time.sleep(
1
)
print
(
'from foo'
)
foo()
|
执行流程:程序执行时,执行@timmer,会识别成index=timmer(index),跳到timmer函数,执行到return wrapper--> @timmer--->foo()--->执行wrapper函数--->执行之前定义的foo()---->结果赋值给res---->返回res,所以打印的结果会是from foo,然后打印执行的时间