1 函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
1.1过程与函数
1.面向对象 : 类(class)
2.面向过程: 过程 (def)
, 没有返回值的函数即过程
3函数式编程 : 函数(def)
过程与函数
#函数
def fun1():
print("this is a testing1")
return 0
#过程
def fun2():
print("this is a testing2")
x = fun1()
y = fun2()
运行结果:
this is a testing1
this is a testing2
我们说在编程中,过程和函数都是可以调运的实体,将没有返回值的函数叫做过程。
#函数
def fun1():
"""testing01"""
print("this is a testing1")
return 0
#过程
def fun2():
"""testing02"""
print("this is a testing2")
x = fun1()
y = fun2()
print("from func1 return is %s"%x)
print("from func2 return is %s"%y)
运行结果:
this is a testing1
this is a testing2
from func1 return is 0
from func2 return is None
由此可见,python中默认的不再将函数个过程区分。
1.2 为什么使用函数
1.代码重用
2.保持一致性
3.可扩展
import time
def logger():
time_format = '%Y-%m-%d %X'
time_current = time.strftime(time_format)
print(time_current)
def test1():
print("in the test1")
logger()
def test2():
print("in the test2")
logger()
def test3():
print("in the test3")
logger()
test1()
test2()
test3()
运行结果:
2019-07-17 11:23:09
in the test1
2019-07-17 11:23:09
in the test2
2019-07-17 11:23:09
in the test3
上述代码实现了,在不同函数执行时打印日志的功能,一次说明函数实现的 “一致性,以及可扩展性”
1.3定义一个函数
你可以定义一个由自己想要功能的函数,以下是简单的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回None。
1.4 语法
Python 定义函数使用 def 关键字,一般格式如下:
def 函数名(参数列表):
函数体
默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的。
def hello():
print("hello world")
hello()
运行结果:
hello world
def area(width,heigt):
return width * heigt
def print_welcome(name):
print("Welcome",name)
print_welcome("anliu")
w = 4
h = 5
print("width=",w,"heigt=",h,"area=",area(w,h))
运行结果:
Welcome anliu
width= 4 heigt= 5 area= 20
1.5 函数返回值
1.5.1 返回值的作用
后续程序需要函数值的确认。
1.5.2 返回值的效果
return执行的效果是:
- 返回函数值,
- 终止当前程序运行。
def test1():
print("in the test1")
return 0
print("test end")
x = test1()
print(x)
运行结果:
test end
in the test1
0
1.5.3 返回值的类型
def test1():
print("in the test1")
def test2():
print("in the test2")
return 0
def test3():
print("in the test2")
return 1,"hello",["dajiang","dajiangliu","xxxx"],{"jingling:shibachai"}
def test4():
print("in the test3")
return test1
x = test1()
y = test2()
z = test3()
w = test4()
print(x)
print(y)
print(z)
print(w)
运行结果:
in the test1
in the test2
in the test2
in the test3
None
0
(1, 'hello', ['dajiang', 'dajiangliu', 'xxxx'], {'jingling:shibachai'})
<function test1 at 0x000001F2CDEC6048>
- 当没有返回值时,返回0。
- 当返回值时一个特定值时,返回这个值。
- 当返回值是多个值时,返回的是一个元组。
- 当返回值是一个函数名时,返回的是函数的内存地址。
- 当返回值是一个函数时,返回值是函数的执行结果
1.6 函数调运
1.6.1 参数类型
位置参数
关键字参数
默认参数
参数组
def test(x,y): #形参
print(x)
print(y)
test("x","y") #位置实参调运,形参和实参是一一对应
test(y=2,x=3) #关键字参数调运,与形参位置无关。
# test(x=2,3) #关键参数不能写在位置参数之前。(报错)
test(3,y=2) #位置参数和关键字参数同时存在,以位置参数调运为主。
运行结果:
x
y
3
2
3
2
(1)位置实参调运,形参和实参是一一对应
(2)关键字参数调运,与形参位置无关。
(3)关键字参数不能写在位置参数之前。
(4)位置参数和关键字参数同时存在,以位置参数调运为主。
def test(x,y=2):
print(x)
print(y)
test(1,3)
运行结果:
1
3
默认参数的特点:
调运函数的时候,默认参数非必须传递
用途:
参数在不修改其值的情况下,使用默认值
def test (x,y,z=2):
print(x)
print(y)
print(z)
test(1,3)
运行结果:
1
3
2
实参不能大于形参数目。
实际上,生产环境中,实参是需要存在多个,如何去定义实参呢?局需要参数组的概念。
def test(*args):
print(args)
test(1,3,4,5,6,7,8,9,)
test(*[1,2,2,4,5,7,])
运行结果:
(1, 3, 4, 5, 6, 7, 8, 9)
(1, 2, 2, 4, 5, 7)
def test(x,y,*args):
print(x)
print(y)
print(args)
test(1,3,4,5,6,7,8,9,)
test(*[1,2,2,4,5,7,])
运行结果:
1
3
(4, 5, 6, 7, 8, 9)
1
2
(2, 4, 5, 7)
参数组和形式参数可以混合使用。
def test(**kwargs):
print(kwargs)
test(name='anliu',age=8,sex='F')
test(**{'name':"anliu","age":18})
#把N个关键字参数,转换为字典的形式
运行结果:
{'name': 'anliu', 'age': 8, 'sex': 'F'}
{'name': 'anliu', 'age': 18}
可将关键字或者字典传递,并得到一个字典的输出结果。
参数和参数组同时使用也是没有问题的。
def test(name,age=8,**kwargs):
print(name)
print(age)
print(kwargs)
test("anliu",age=28,hobby='paoniu',sex='F')
运行结果:
anliu
28
{'hobby': 'paoniu', 'sex': 'F'}
*args :
接受N个位置参数,转化成元组的形式
**kwargs :
接受N个关键字参数,转化成字典的形式
1.7 变量
1.7.1 局部变量
def change_name(name):
print("before change",name)
name = "Auliu" #局部变量,这个函数就是变量的作用域
# print("after change",name)
name = "anliu"
change_name(name)
print(name)
运行结果:
before change anliu
anliu
局部变量,这个函数就是变量的作用域
1.7.2 全局变量
在函数内是不能修改全局变量的值
school = "xiyunkeji" #全局变量
def change_name(name):
school = "baiyijishu" #在函数内是不能修改全局变量的值
print("before change",name,school)
name = "Auliu" #局部变量,这个函数就是变量的作用域
print("after change",name)
name = "anliu"
change_name(name)
print(name,school)
运行结果:
before change anliu baiyijishu
after change Auliu
anliu xiyunkeji
定义global之后,全局变量也是可以在函数内修改的。
school = "xiyunkeji" #全局变量
def change_name(name):
global school #定义global之后,全局变量也是可以在函数内修改的。
school = "baiyijishu" #在函数内是不能修改全局变量的值
print("before change",name,school)
name = "Auliu" #局部变量,这个函数就是变量的作用域
print("after change",name)
name = "anliu"
change_name(name)
print(name,school)
运行结果:
before change anliu baiyijishu
after change Auliu
anliu baiyijishu
names = ["anliu","anttech","xingyun"]
def change_name():
names[0] = "Anliu"
print(names)
change_name()
print(names)
运行结果:
['Anliu', 'anttech', 'xingyun']
['Anliu', 'anttech', 'xingyun']
需要说明的是,当全局变量为列表,字典,集合包括类,这种复杂的数据类型及数据结果,函数中也是可以修改全局变量的。
1.7.3变量内存的回收机制
python解释器当变量名不存在时,变量内存会被回收。
1.8 递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身,这个函数就是递归函数。
递归的特性:
1.必须要有一个明确的结束条件。
2.每次进入更深的一层递归时,问题规模相比上次都应有所减少。
3.递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调运是通过栈(stack)这种数据结构实现的,每当进入一个函数调运,栈就会加一层栈帧。每当函数返回,栈就会减少一层栈帧。由于栈的大小不是无限的,所以,递归调运的次数过多,会导致栈溢出)
def calc(n):
print(n)
if int(n/2) > 0:
return calc(int(n/2))
print("-->",n)
calc(10)
运行结果:
10
5
2
1
--> 1
1.9 高阶函数
变量可以指向函数,函数的参数支持能接受变量,那么一个函数就可以接受另一个函数作为参数,这种函数就称之为高阶函数。
def add(a,b,f):
return f(a) + f(b)
x = add(1,-6,abs)
print(x)
运行结果:
7
以上长须abs是求确定值的函数。
1.10 匿名函数
没有函数名
calc = lambda x:x*3
print(calc(2))
运行结果:
6
1.11 高阶函数
(1)把一个函数名当做实参传递给另一个函数
(2)返回值中包含函数名
def test1(func):
print(func)
def bar():
print("in the bar")
test1(bar)
func = bar
func()
运行结果:
<function bar at 0x00000115EB530A60>
in the bar
import time
def bar():
time.sleep(3)
print("in the bar")
def test2(func):
print(func)
return func
t = test2(bar)
print(t)
t()
运行结果:
<function bar at 0x000001332BC16048>
<function bar at 0x000001332BC16048>
in the bar
1.12 嵌套函数
在一个函数体内定义另一个函数。
def foo():
print("in the foo ")
def bar():
print("in the bar")
bar()
foo()
运行结果:
in the foo
in the bar
2、 函数式编程介绍
函数是python内建支持的一种封装,我们通过把大段代码折成函数,通过一层一层的函数调运,就可以把复杂的任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程设计的基本单元。
而函数式编程,虽然可以归结到面向过程的的程序设计,但是其思想更接近科学计算。
这使得我们要明确计算机和计算的概念。
在计算机层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断,和跳转指令,所以汇编语言是最贴近计算机的语言。
而计算则指的是数学意义上的计算,越是抽象的计算,里计算机硬件越远。
对应到编程语言,就是越低级的语言,越贴近计算机,执行效率高,比如C语言;级别越高的语言,越贴近计算,抽象程度高,执行效率低,比如lisp语言。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入确定,输出就是确定的,这种纯函数我们称之为没有副作用。而容许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可以得到不同的输出,因此这种函数是有副作用的。
python对函数式编程提供部分支持。由于python容许使用变量,因此python不是纯函数式编程式语言。
3 、装饰器
定义:
装饰器:装饰器本质是函数,(函数是有特定的功能),装饰器的功能就是装饰他功能。就是为其他函数添加附加功能。
原则:
- 不能修改被装饰函数的源代码
- 不能修改被装饰的函数的调运方式。(装饰器对被装饰函数来说是透明的)
实现装饰器知识储备:
1.函数即“变量”
2.高阶函数
(1)把一个函数名当做实参传递给另一个函数。
(2)返回值中包含函数名。
3.嵌套函数
装饰器=高阶函数+嵌套函数
看以下装饰器示例:
import time
def timmer(func):
def warpper(*args,**kwargs):
start_time=time.time()
func()
stop_time=time.time()
print("the func run time is %s"%(stop_time-start_time))
return warpper
@timmer
def test1():
time.sleep(3)
print("in the test1")
test1()
为什么我们需要装饰器?
比如我们有两个函数,现在需要给这两个函数添加打印日志的功能:
def test1():
print("this is a test1")
test1()
def test2():
print("this is a test2")
test2()
运行结果:
this is a test1
this is a test2
装饰后效果:
方法一:
import time
def test1():
print("this is a test1")
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
print(time_current)
def test2():
print("this is a test2")
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
print(time_current)
test1()
test2()
运行结果:
this is a test1
2019-07-17 18:42:14
this is a test2
2019-07-17 18:42:14
方法二:
import time
def logger():
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
print(time_current)
def test1():
print("this is a test1")
logger()
def test2():
print("this is a test2")
logger()
test1()
test2()
运行结果:
this is a test1
2019-07-17 18:45:40
this is a test2
2019-07-17 18:45:40
此法看似可行。但是问题来了,如果实在给程序已经应用到了生产环境,test函数已经承载业务,此时代码时不容许改变的,修改源代码,是有风险存在的。该当如何是好?利用高阶函数实现对test1()进行装饰一个打印日志功能。
import time
def test1():
'''xxxxx'''
print("this is a test1")
def logger(func):
'''xxxxx'''
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
func()
print(time_current)
logger(test1)
运行结果:
this is a test1
2019-07-17 18:47:27
这样也能实现对函数的装饰。即在不修改被装饰函数源代码的情况下为其添加功能。
但是问题来了,虽然没有修改源代码,但是修改了函数的调运方式。
再看如下代码:
import time
def test1():
'''xxxxx'''
print("this is a test1")
def test2():
'''xxxxx'''
print("this is a test2")
def logger(func):
'''xxxxx'''
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
print(time_current)
return func
test1 = logger(test1)
test1()
test2 = logger(test2)
test2()
运行结果:
2019-07-17 18:49:39
this is a test1
2019-07-17 18:49:39
this is a test2
这样,我们用高阶函数的第二个特性,返回值中包含函数名,来实现了在不修改函数调运方式的情况下,完成对函数的装饰。
这种方式看起来貌似没有问题,其实很扯淡。因为只要在我们这个实例上生效。那如果是我们的需求变了,不再是先输出实践,再执行函数,而是先执行函数,再打印时间。
python解释题给我们提供了一个“语法糖”,我们可以使用“语法糖”来实现。
import time
def timer (func):
def logger():
'''xxxxx'''
func()
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
print(time_current)
return logger
@timer
def test1():
'''xxxxx'''
print("this is a test1")
test1()
运行结果:
this is a test1
2019-07-17 18:55:09
这样就实现了一个完整的装饰器。
若函数有参数传递,该如何呢?
import time
def timer (func):
def logger(*args,**kwargs):
format_time = "%Y-%m-%d %X"
time_current = time.strftime(format_time)
print(time_current)
func(*args,**kwargs)
return logger
@timer
def test1():
print("this is a test1")
test1()
@timer
def test2():
print("this is a test2")
test2()
运行结果:
2019-07-17 18:53:20
this is a test1
2019-07-17 18:53:20
this is a test2