Python之函数

一 函数是什么?

函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不一样的,具体区别,咱们后面会讲,编程中的函数在英文中也有不少不一样的叫法。在BASIC中叫作subroutine(子过程或子程序),在Pascal中叫作procedure(过程)和function,在C中只有function,在Java里面叫作method。html

函数能提升应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,好比print()。但你也能够本身建立函数,这被叫作用户自定义函数。python

定义: 函数是指将一组语句的集合经过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名便可程序员

特性:面试

1.代码重用编程

2.保持一致性数组

3.可扩展性数据结构

二 函数的建立

2.1 格式:

Python 定义函数使用 def 关键字,通常格式以下:闭包

1
2
def  函数名(参数列表):
     函数体
def hello():
print('hello')

hello()#调用

2.2 函数名的命名规则

  • 函数名必须如下划线或字母开头,能够包含任意字母、数字或下划线的组合。不能使用任何的标点符号;
  • 函数名是区分大小写的。
  • 函数名不能是保留字。

2.3 形参和实参

形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)编程语言

实参:实际参数,调用函数时传给函数的参数,能够是常量,变量,表达式,函数,传给形参   ide

区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参

1
2
3
4
5
import  time
times = time.strftime( '%Y--%m--%d' )
def  f(time):
     print ( 'Now  time is : %s' % times)
f(times)

2.4 实例

实例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def  show_shopping_car():
     saving = 1000000
     shopping_car = [
         ( 'Mac' , 9000 ),
         ( 'kindle' , 800 ),
         ( 'tesla' , 100000 ),
         ( 'Python book' , 105 ),
     ]
     print ( '您已经购买的商品以下' .center( 50 , '*' ))
     for  i ,v  in  enumerate (shopping_car, 1 ):
         print ( '\033[35;1m %s:  %s \033[0m' % (i,v))
 
     expense = 0
     for  in  shopping_car:
         expense + = i[ 1 ]
     print ( '\n\033[32;1m您的余额为 %s \033[0m' % (saving - expense))
show_shopping_car()

实例2:

如今咱们就用一个例子来讲明函数的三个特性:

def action1(n):
    print ('starting action1...')

    with open('日志记录','a') as f:
        f.write('end action%s\n'%n)

def action2(n):
    print ('starting action2...')

    with open('日志记录','a') as f:
        f.write('end action%s\n'%n)

def action3(n):
    print ('starting action3...')

    with open('日志记录','a') as f:
        f.write('end action%s\n'%n)

action1(1)
action2(2)
action3(3)


##***************代码重用

def logger(n):
    with open('日志记录','a') as f:
        f.write('end action%s\n'%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\n'%(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()
函数的特性展现

 

三 函数的参数

  • 必备参数
  • 关键字参数
  • 默认参数
  • 不定长参数

必需的参数:

必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的同样。
1
2
3
4
5
6
def  f(name,age):
 
     print ( 'I am %s,I am %d' % (name,age))
 
f( 'alex' , 18 )
f( 'alvin' , 16 )

关键字参数:

关键字参数和函数调用关系紧密,函数调用使用关键字参数来肯定传入的参数值。使用关键字参数容许函数调用时参数的顺序与声明时不一致,由于 Python 解释器可以用参数名匹配参数值。

1
2
3
4
5
6
def  f(name,age):
 
     print ( 'I am %s,I am %d' % (name,age))
 
# f(16,'alvin') #报错
f(age = 16 ,name = 'alvin' )

缺省参数(默认参数):

调用函数时,缺省参数的值若是没有传入,则被认为是默认值。下例会打印默认的age,若是age没有被传入:

1
2
3
4
5
6
7
8
9
def  print_info(name,age,sex = 'male' ):
 
     print ( 'Name:%s' % name)
     print ( 'age:%s' % age)
     print ( 'Sex:%s' % sex)
     return
 
print_info( 'alex' , 18 )
print_info( '铁锤' , 40 , 'female' )

不定长参数

你可能须要一个函数能处理比当初声明时更多的参数。这些参数叫作不定长参数,和上述2种参数不一样,声明时不会命名。

1
2
3
4
5
6
7
8
9
10
11
12
# def add(x,y):
#     return x+y
 
def  add( * tuples):
     sum = 0
     for  in  tuples:
         sum + = v
 
     return  sum
 
print (add( 1 , 4 , 6 , 9 ))
print (add( 1 , 4 , 6 , 9 , 5 ))

加了星号(*)的变量名会存放全部未命名的变量参数。而加(**)的变量名会存放命名的变量参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def  print_info( * * kwargs):
 
     print (kwargs)
     for  in  kwargs:
         print ( '%s:%s' % (i,kwargs[i])) #根据参数能够打印任意相关信息了
 
     return
 
print_info(name = 'alex' ,age = 18 ,sex = 'female' ,hobby = 'girl' ,nationality = 'Chinese' ,ability = 'Python' )
 
###########################位置
 
def  print_info(name, * args, * * kwargs): #def print_info(name,**kwargs,*args):报错
 
     print ( 'Name:%s' % name)
 
     print ( 'args:' ,args)
     print ( 'kwargs:' ,kwargs)
 
     return
 
print_info( 'alex' , 18 ,hobby = 'girl' ,nationality = 'Chinese' ,ability = 'Python' )
# print_info(hobby='girl','alex',18,nationality='Chinese',ability='Python')  #报错
#print_info('alex',hobby='girl',18,nationality='Chinese',ability='Python')   #报错

注意,还能够这样传参:

1
2
3
4
5
6
7
8
9
def  f( * args):
     print (args)
 
f( * [ 1 , 2 , 5 ])
 
def  f( * * kargs):
     print (kargs)
 
f( * * { 'name' : 'alex' })

补充(高阶函数):

         高阶函数是至少知足下列一个条件的函数:

      • 接受一个或多个函数做为输入
      • 输出一个函数
1
2
3
4
5
6
7
8
9
10
11
def  add(x,y,f):
     return  f(x)  +  f(y)
 
res  =  add( 3 , - 6 , abs )
print (res)
###############
def  foo():
     x = 3
     def  bar():
         return  x
     return  bar 

四 函数的返回值

要想获取函数的执行结果,就能够用return语句把结果返回

注意:

  1. 函数在执行过程当中只要遇到return语句,就会中止执行并返回结果,so 也能够理解为 return 语句表明着函数的结束
  2. 若是未在函数中指定return,那这个函数的返回值为None  
  3. return多个对象,解释器会把这多个对象组装成一个元组做为一个一个总体结果输出。

五 做用域

5.1 做用域介绍 

python中的做用域分4种状况:

  • L:local,局部做用域,即函数中定义的变量;
  • E:enclosing,嵌套的父级函数的局部做用域,即包含此函数的上级函数的局部做用域,但不是全局的;
  • G:globa,全局变量,就是模块级别定义的变量;
  • B:built-in,系统固定模块里面的变量,好比int, bytearray等。 搜索变量的优先级顺序依次是:做用域局部>外层做用域>当前模块中的全局>python内置做用域,也就是LEGB。
1
2
3
4
5
6
7
8
9
10
11
12
13
=  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) #找不到

固然,local和enclosing是相对的,enclosing变量相对上层来讲也是local。

5.2 做用域产生 

在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的做用域,其它的代码块(如if、try、for等)是不会引入新的做用域的,以下代码:

1
2
3
if  2 > 1 :
     =  1
print (x)   # 1

这个是没有问题的,if并无引入一个新的做用域,x仍处在当前做用域中,后面代码可使用。

1
2
3
def  test():
     =  2
print (x)  # NameError: name 'x2' is not defined

def、class、lambda是能够引入新做用域的。 

5.3 变量的修改 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#################
x = 6
def  f2():
     print (x)
     x = 5
f2()
  
# 错误的缘由在于print(x)时,解释器会在局部做用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,因此报错:
# local variable 'x' referenced before assignment.如何证实找到了x=5呢?简单:注释掉x=5,x=6
# 报错为:name 'x' is not defined
#同理
x = 6
def  f2():
     x + = 1  #local variable 'x' referenced before assignment.
f2()

5.4 global关键字 

当内部做用域想修改外部做用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局做用域(global做用域)上的,就要使用global先声明一下,代码以下:

1
2
3
4
5
6
7
8
9
count  =  10
def  outer():
     global  count
     print (count) 
     count  =  100
     print (count)
outer()
#10
#100

5.5 nonlocal关键字 

global关键字声明的变量必须在全局做用域上,不能嵌套做用域上,当要修改嵌套做用域(enclosing做用域,外层非全局做用域)中的变量怎么办呢,这时就须要nonlocal关键字了

1
2
3
4
5
6
7
8
9
10
11
def  outer():
     count  =  10
     def  inner():
         nonlocal count
         count  =  20
         print (count)
     inner()
     print (count)
outer()
#20
#20 

5.6 小结 

(1)变量查找顺序:LEGB,做用域局部>外层做用域>当前模块中的全局>python内置做用域;

(2)只有模块、类、及函数才能引入新做用域;

(3)对于一个变量,内部做用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部做用域的变量;

(4)内部做用域要修改外部做用域变量的值时,全局变量要使用global关键字,嵌套做用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。 

六 递归函数

定义:在函数内部,能够调用其余函数。若是一个函数在内部调用自身自己,这个函数就是递归函数。

实例1(阶乘)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def  factorial(n):
 
     result = n
     for  in  range ( 1 ,n):
         result * = i
 
     return  result
 
print (factorial( 4 ))
 
 
#**********递归*********
def  factorial_new(n):
 
     if  n = = 1 :
         return  1
     return  n * factorial_new(n - 1 )
 
print (factorial_new( 3 ))

实例2(斐波那契数列)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def  fibo(n):
 
     before = 0
     after = 1
     for  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 ))
1
print (fibo_new( 30000 )) #maximum recursion depth exceeded in comparison

递归函数的优势:    是定义简单,逻辑清晰。理论上,全部的递归函数均可以写成循环的方式,但循环的逻辑不如递归清晰。

递归特性:

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减小

3. 递归效率不高,递归层次过多会致使栈溢出(在计算机中,函数调用是经过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返     回,栈就会减一层栈帧。因为栈的大小不是无限的,因此,递归调用的次数过多,会致使栈溢出。)

七 内置函数(Py3.5)

  Built-in Functions    
abs() dict() help() min() setattr()
all() dir() hex() next() slice()
any() divmod() id() object() sorted()
ascii() enumerate() input() oct() staticmethod()
bin() eval() int() open() str()
bool() exec() isinstance() ord() sum()
bytearray() filter() issubclass() pow() super()
bytes() float() iter() print() tuple()
callable() format() len() property() type()
chr() frozenset() list() range() vars()
classmethod() getattr() locals() repr() zip()
compile() globals() map() reversed() __import__()
complex() hasattr() max() round()  
delattr() hash() memoryview() set()  

py2内置函数:https://docs.python.org/3.5/library/functions.html#repr

重要的内置函数:

       1 filter(function, sequence)

1
2
3
4
5
6
7
8
9
10
str  =  [ 'a' 'b' , 'c' 'd' ]
 
def  fun1(s):
     if  s ! =  'a' :
         return  s
 
 
ret  =  filter (fun1,  str )
 
print ( list (ret)) # ret是一个迭代器对象       

对sequence中的item依次执行function(item),将执行结果为True的item作成一个filter object的迭代器返回。能够看做是过滤函数。

       2 map(function, sequence) 

1
2
3
4
5
6
7
8
9
10
str  =  [ 1 2 , 'a' 'b' ]
 
def  fun2(s):
 
     return  +  "alvin"
 
ret  =  map (fun2,  str )
 
print (ret)       #  map object的迭代器
print ( list (ret)) #  ['aalvin', 'balvin', 'calvin', 'dalvin']
对sequence中的item依次执行function(item),将执行结果组成一个map object迭代器返回.
map也支持多个sequence,这就要求function也支持相应数量的参数输入:
1
2
3
ef add(x,y):
     return  x + y
print  ( list ( map (add,  range ( 10 ),  range ( 10 )))) ##[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

       3 reduce(function, sequence, starting_value)  

1
2
3
4
5
6
7
8
from  functools  import  reduce
 
def  add1(x,y):
     return  +  y
 
print  ( reduce (add1,  range ( 1 101 ))) ## 4950 (注:1+2+...+99)
 
print  ( reduce (add1,  range ( 1 101 ),  20 )) ## 4970 (注:1+2+...+99+20)

  对sequence中的item顺序迭代调用function,若是有starting_value,还能够做为初始值调用.

       4 lambda

       普通函数与匿名函数的对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#普通函数
def  add(a,b):
     return  +  b
 
print  add( 2 , 3 )
 
  
#匿名函数
add  =  lambda  a,b : a  +  b
print  add( 2 , 3 )
 
 
#========输出===========
5
5

      匿名函数的命名规则,用lamdba 关键字标识,冒号(:)左侧表示函数接收的参数(a,b) ,冒号(:)右侧表示函数的返回值(a+b)。

  由于lamdba在建立时不须要命名,因此,叫匿名函数  

八 函数式编程 

学会了上面几个重要的函数后,咱们就能够来聊一聊函数式编程究竟是个什么鬼

一 概念(函数式编程

函数式编程是一种编程范式,咱们常见的编程范式有命令式编程(Imperative programming),函数式编程,常见的面向对象编程是也是一种命令式编程。

命令式编程是面向 计算机硬件的抽象,有 变量(对应着存储单元), 赋值语句(获取,存储指令), 表达式(内存引用和算术运算)和 控制语句(跳转指令),一句话,命令式程序就是一个 冯诺依曼机指令序列
而函数式编程是面向数学的抽象,将计算描述为一种 表达式求值,一句话,函数式程序就是一个 表达式
 
函数式编程的本质

函数式编程中的 函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其余状态。好比y=x*x函数计算x的平方根,只要x的平方,不论何时调用,调用几回,值都是不变的。

纯函数式编程语言中的 变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是 不可变(immutable),也就是说不容许像命令式编程语言中那样屡次给一个变量赋值。好比说在命令式编程语言咱们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。
函数式语言的如条件语句,循环语句也不是命令式编程语言中的 控制语句,而是函数的语法糖,好比在Scala语言中, if else不是语句而是三元运算符,是有返回值的。
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其余命令式控制结构进行编程。

函数式编程关心数据的映射,命令式编程关心解决问题的步骤,这也是为何“函数式编程”叫作“函数式编程”。

二 实例

假如,如今你来到 baidu面试,面试官让你把number =[2, -5, 9, -7, 2, 5, 4, -1, 0, -3, 8]中的正数的平均值,你确定能够写出:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#计算数组中正整数的平均值
 
number  = [ 2 - 5 9 - 7 2 5 4 - 1 0 - 3 8 ]
count  =  0
sum  =  0
 
for  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的数进行累加,最后求取平均值。  

这就是命令式编程——你要作什么事情,你得把达到目的的步骤详细的描述出来,而后交给机器去运行。

这也正是命令式编程的理论模型——图灵机的特色。一条写满数据的纸带,一条根据纸带内容运动的机器,机器每动一步都须要纸带上写着如何达到。

那么,不用这种方式如何作到呢?

1
2
3
4
5
6
7
8
9
10
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表达式式实现阶乘是如何简单:

1
2
from  functools  import  reduce
print  ( reduce ( lambda  x,y: x * y,  range ( 1 , 6 )))

 

又好比,map()函数加上lambda表达式(匿名函数)能够实现更强大的功能:

1
2
3
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] 

三 函数式编程有什么好处呢?

1)代码简洁,易懂。
2)无反作用

因为命令式编程语言也能够经过相似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。没有可变的状态,函数就是引用透明(Referential transparency)的和没有反作用(No Side Effect)。

相关文章
相关标签/搜索