一、函数定义
函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
计算机函数 subroutine 子程序 procedures 过程
作用:
1.减少重复代码
2.方便修改,更易扩展
3.保持代码一致性
二、创建函数
1.格式:
def 函数名(参数列表):
函数体
2.命名规则:
① 函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合。
不能使用任何的标点符号;
② 函数名是区分大小写的。
③ 函数名不能是保留字。
3.实参和形参
形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用
形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)
实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,
函数,传给形参
区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单
元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不
能形参传给实参
#日志记录:
def action1(n):
print ('starting action1...')
with open('日志记录','a') as f:
f.write('end action%s
'%n)
def action2(n):
print ('starting action2...')
with open('日志记录','a') as f:
f.write('end action%s
'%n)
def action3(n):
print ('starting action3...')
with open('日志记录','a') as f:
f.write('end action%s
'%n)
action1(1)
action2(2)
action3(3)
##***************代码重用
def logger(n):
with open('日志记录','a') as f:
f.write('end action%s
'%n)
def action1():
print ('starting action1...')
logger(1)
def action2():
print ('starting action2...')
logger(2)
def action3():
print ('starting action3...')
logger(3)
action1()
action2()
action3()
##***************可扩展和保持一致
##为日志加上时间
import time
def logger(n):
time_format='%Y-%m-%d %X'
time_current=time.strftime(time_format)
with open('日志记录','a') as f:
f.write('%s end action%s
'%(time_current,n))
def action1():
print ('starting action1...')
logger(1)
def action2():
print ('starting action2...')
logger(2)
def action3():
print ('starting action3...')
logger(3)
action1()
action2()
action3()
三、函数参数
必需参数:必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
关键字参数:关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传
入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一
致,因为 Python 解释器能够用参数名匹配参数值。
def print_info( name , age ):
print('Name : %s' %name)
print('Age : %s' %age)
print_info('冷大爷',23) #必须参数
print_info(age = 23 , name = "Jayce")#关键字参数
缺省参数(默认参数):调用函数时,缺省参数的值如果没有传入,则被认为是默
认值。下例会打印默认的sex,如果sex没有被传入:默认打印 male
另:默认参数一定要跟在其他参数后面
def print_info( name , age , sex = 'male'):#sex 默认参数
print('Name : %s' %name)
print('Age : %s' %age)
print('Sex : %s' %sex)
print_info('冷大爷',23)
print_info('王俊杰',23,'female')
不定长参数:①你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做
不定长参数,和上述几种参数不同,声明时不会命名。
def add(*args):
print(args)
sum = 0
for i in args:
sum += i
print(sum)
add(1,2,3)
add(5,4)
②加了星号(*)的变量名会存放所有未命名的变量参数。而加(**)的变
量名会存放命名的变量参数
def print_info( *args ,**kwargs):
print(args) #无命名参数被处理成一个元组:('Jayce', 23, 'male')
print(kwargs)#有命名参数被放入一个字典里:{'job': 'IT', 'hobby': 'girls', 'height': 173}
for i in kwargs:
print('%s : %s' %(i , kwargs[i]))
print_info("Jayce" , 23 , "male" ,job = 'IT',hobby = 'girls', height = 173 )
#print_info(hobby='girls','Jayce',23,nationality='Chinese',ability='Python') #报错
#print_info('Jayce',hobby='girls',23,nationality='Chinese',ability='Python') #报错
下面代码也会报错
def print_info( **kwargs , *args ):
print(args) #无命名参数被处理成一个元组:('Jayce', 23, 'male')
print(kwargs)#有命名参数被放入一个字典里:{'job': 'IT', 'hobby': 'girls', 'height': 173}
for i in kwargs:
print('%s : %s' %(i , kwargs[i]))
print_info("Jayce" , 23 , "male" ,job = 'IT',hobby = 'girls', height = 173 )
关于不定长参数的位置:*args参数放在左边 , **kwargs参数放在右边,如果有默认参数,放在最左边
优先级:
关键字参数(必须参数)放在最左边,默认参数参数在关键字参数之后,然后是 args, kwargs
def func(name , age = 23 , *args , **kwargs):
高阶函数是至少满足下列一个条件的函数:
接受一个或多个函数作为输入
输出一个函数
def f():
print('ok')
foo = f #函数名可以进行赋值
#函数名可以作为函数参数,也可以蚤为函数的返回值
def bar(a,b,func):
func()
return 1
bar(1,2,f)
# def foo2():
# x = 5
# return x
#
# print(foo2())
def foo3():
def inner():
return 8
#return inner()
return inner #对象的引用
ret = foo3()
print(ret)
print(ret())
def f(n):
return n*n
print(f(4))
def foo(a,b,func):
return func(a) + func(b)
print(foo(1,2,f))
四、函数返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
1.函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解
为 return 语句代表着函数的结束
2.如果未在函数中指定return,那这个函数的返回值为None
3.return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。
def f():
print('OK')
return 10 #作用 1.结束函数 2.返回某个对象
a = f()
print(a)
def add(*args):
sum = 0
for i in args:
sum += i
print(sum)
a = add(1,4)
print(a)#None 函数里如果没有return 默认返回None
def foo():
return 0,1,'234',5
print(foo()) #(0, 1, '234', 5)return多个对象,解释器会把这多个对象封装成一
#个元组作为一个一个整体结果输出
五、作用域
1. 作用域介绍
python中的作用域分4种情况:
L:local,局部作用域,即函数中定义的变量;
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
local和enclosing是相对的,enclosing变量相对上层来说也是local
G:globa,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:
作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
x = int(2.9) # int built-in
g_count = 0 # global
def outer():
o_count = 1 # enclosing
def inner():
i_count = 2 # local
print(o_count)
# print(i_count) 找不到
inner()
outer()
# print(o_count) #找不到
2.作用域产生
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它
的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:
if 2>1:
x = 1
print(x) # 1
这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。
def test():
x = 2
print(x) # NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的。
3.变量的修改
x = 10
def outer():
print( x ) #UnboundLocalError: local variable 'x' referenced before assignment
x = 100
outer()
4. global关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量
是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:
count = 10
def outer():
global count
print(count)
count = 100
print(count)
outer()
#10
#100
5. nonlocal关键字
global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域
(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
def outer():
count = 10
def inner():
nonlocal count
count = 20
print(count)
inner()
print(count)
outer()
#20
#20
6.小结
(1)变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;
(2)只有模块、类、及函数才能引入新作用域;
(3)对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部
作用域的变量;
(4)内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域
变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就
能完美的实现闭包了。
六、递归函数
定义:在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递
归函数
非递归阶乘:
def factorial(n):
result = n
for i in range(1,n):
result *= i
return result
print(factorial(5))
递归:
def fact(n):
if n == 1:
return 1
return n*fact(n-1)
print(fact(5))
递归的特点:
1.调用自身函数
2.有一个结束条件
但是递归可以写的循环都可以解决,递归地效率在很多时候会很低
斐波那契数列
def fibo(n):
before=0
after=1
for i in range(n-1):
ret=before+after
before=after
after=ret
return ret
print(fibo(3))
#**************递归*********************
def fibo_new(n):#n可以为零,数列有[0]
if n <= 1:
return n
return(fibo_new(n-1) + fibo_new(n-2))
print(fibo_new(3))
print(fibo_new(30000))#maximum recursion depth exceeded in comparison
七.内置函数
filter(function, sequence):
filter(object):
"""
filter(function or None, iterable) --> filter object
Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.
"""
str = ['a', 'b','c', 'd']
def fun1(s):
if s != 'a':
return s
ret = filter(fun1, str)
print(ret)#<filter object at 0x01B5BED0>
print(list(ret))# ret是一个迭代器对象
对sequence中的item依次执行function(item),将执行结果为True的item做成一个filter
object的迭代器返回。可以看作是过滤函数。
map(function, sequence) :
map(object):
"""
map(func, *iterables) --> map object
Make an iterator that computes the function using arguments from
each of the iterables. Stops when the shortest iterable is exhausted.
"""
str = [1, 2, 'a', 'b']
def fun2(s):
return s + "alvin"
ret = map(fun2, str)
print(ret) # map object的迭代器
print(list(ret)) # ['aalvin', 'balvin', 'calvin', 'dalvin']
reduce():
reduce(function, sequence, initial=None): # real signature unknown; restored from __doc__
"""
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
"""
from functools import reduce
def add1(x,y):
return x+y
print(reduce(add1 , range(1,10)))#reduce的结果就是一个值
lambda:
普通函数与匿名函数的对比:
#普通函数:
def add(a,b):
return a + b
print(add(2,3))
#匿名函数:
add = lambda a,b : a + b
print(add(2,3))
#========输出===========
5
5
匿名函数的命名规则,用lamdba 关键字标识,冒号(:)左侧表示函数接收的参数(a,b),
冒号(:)右侧表示函数的返回值(a+b)。
因为lamdba在创建时不需要命名,所以,叫匿名函数
八、函数式编程
1.概念(函数式编程)
函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),
函数式编程,常见的面向对象编程是也是一种命令式编程。
命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指
令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一
个冯诺依曼机的指令序列。而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,
一句话,函数式程序就是一个表达式。
函数式编程的本质
函数式编程中的函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。
也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如y=x*x函数计算x的平方
根,只要x的平方,不论什么时候调用,调用几次,值都是不变的。
纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中
的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不允许像命令式编
程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变
状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。
函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,
比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的。
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤,这也是为什么“函数式编程”叫
做“函数式编程”。
2.实例
假如,现在你来到 baidu面试,面试官让你把number =[2, -5, 9, -7, 2, 5, 4, -1, 0, -3, 8]
中的正数的平均值,你肯定可以写出:
#计算数组中正整数的平均值
number =[2, -5, 9, -7, 2, 5, 4, -1, 0, -3, 8]
count = 0
sum = 0
for i in range(len(number)):
if number[i]>0:
count += 1
sum += number[i]
print sum,count
if count>0:
average = sum/count
print (average)
#========输出===========
30 6
5
首先循环列表中的值,累计次数,并对大于0的数进行累加,最后求取平均值。这就是命令式
编程——你要做什么事情,你得把达到目的的步骤详细的描述出来,然后交给机器去运行。这也
正是命令式编程的理论模型——图灵机的特点。一条写满数据的纸带,一条根据纸带内容运动的
机器,机器每动一步都需要纸带上写着如何达到。
不用这种方式如何做到呢?
number =[2, -5, 9, -7, 2, 5, 4, -1, 0, -3, 8]
positive = filter(lambda x: x>0, number)
average = reduce(lambda x,y: x+y, positive)/len(positive)
print (average)
#========输出===========
5
这段代码最终达到的目的同样是求取正数平均值,但是它得到结果的方式和 之前有着本质的差
别:通过描述一个列表->正数平均值 的映射,而不是描述“从列表得到正数平均值应该怎样做”
来达到目的。
再比如,求阶乘:
通过Reduce函数加lambda表达式式实现阶乘是如何简单:
from functools import reduce
print (reduce(lambda x,y: x*y, range(1,6)))
又比如,map()函数加上lambda表达式(匿名函数)可以实现更强大的功能:
squares = map(lambda x : x*x ,range(9))
print (squares)# <map object at 0x10115f7f0>迭代器
print (list(squares))#[0, 1, 4, 9, 16, 25, 36, 49, 64]
3.函数式编程有什么好处呢?
1)代码简洁,易懂。
2)无副作用
由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处
主要是不可变性带来的。没有可变的状态,函数就是引用透明(Referential transparency)
的和没有副作用(No Side Effect)。