python的函数
函数一词起源于数学,但是在编程中的函数和数学中的有很大不同。编程中的函数式组织好的,可重复使用的,用于实现单一功能或相关联功能的代码块。
我们在学习过程中已经使用过一些python内建的函数,如print()。但我们也可以自己创建函数,这被叫做用户自定义函数。
函数的特点:
1 代码重用
2 保持一致性
3 便与修改,易扩展
1. 定义一个函数
1.1 python定义函数用def关键字,一般格式如下
- def 函数名(参数列表):
- 函数体
1.2 函数名的命名规则
- 函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合。不能使用任何的标点符号;
- 函数名是区分大小写的。
- 函数名不能是保留字。
- def hello():
- print('Hello World')
- hello()
- >>>:
- Hello World
1.3 形参和实参
形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)
实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参
区别:形参是虚拟的,不占用内存空间,.形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参
- def area(width, height):
- return width * height
- w = 4
- h = 5
- print(area(w, h))
- >>>:
- 20
width和height是形参,w=4和h=5是实参。
我们就用一个例子来说明函数的三个特性:
- 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()
特性
2 函数的参数
- 必备参数
- 关键字参数
- 默认参数
- 不定长参数
必需的参数:
- def f(name,age):
- print('I am %s,I am %d'%(name,age))
- f('alex',18)
- f('alvin',16)
关键字参数:
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。
- def f(name,age):
- print('I am %s,I am %d'%(name,age))
- # f(16,'alvin') #报错
- f(age=16,name='alvin')
默认参数:
调用函数时,缺省参数的值如果没有传入,则被认为是默认值。下例会打印默认的age,如果age没有被传入:
- 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种参数不同,声明时不会命名。
- def add(*tuples):
- sum=0
- for v in tuples:
- sum+=v
- return sum
- print(add(1,4,6,9))
- print(add(1,4,6,9,5))
加了星号(*)的变量名会存放所有未命名的变量参数。而加(**)的变量名会存放命名的变量参数
- def print_info(**kwargs):
- print(kwargs)
- for i 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') #报错
注意,还可以这样传参:
- def f(*args):
- print(args)
- f(*[1,2,5])
- def f(**kargs):
- print(kargs)
- f(**{'name':'alex'})
高阶函数:
高阶函数是至少满足下列一个条件的函数:
- 接受一个或多个函数作为输入
- 输出一个函数
- 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
3 函数的返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
- return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。
4 作用域
4.1 作用域介绍
python中的作用域分4种情况:
- L:local,局部作用域,即函数中定义的变量;
- E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
- 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) #找不到
当然,local和enclosing是相对的,enclosing变量相对上层来说也是local。
4.2 作用域产生
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:
- if 2>1:
- x = 1
- print(x) #
这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。
- def test():
- x = 2
- print(x) # NameError: name 'x2' is not defined
def、class、lambda是可以引入新作用域的。
4.3 变量的修改
- 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()
4.4 global关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:
- count = 10
- def outer():
- global count
- print(count)
- count = 100
- print(count)
- outer()
- #
- #
4.5 nonlocal关键字
global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
- def outer():
- count = 10
- def inner():
- nonlocal count
- count = 20
- print(count)
- inner()
- print(count)
- outer()
- #
- #20
4.6 小结
(1)变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;
(2)只有模块、类、及函数才能引入新作用域;
(3)对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;
(4)内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。
5 递归函数
定义:在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
例1(阶乘)
- def factorial(n):
- result=n
- for i 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(斐波那契数列)
- 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
递归函数的优点: 是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归特性:
1. 必须有一个明确的结束条件
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)
6 内置函数
重要的内置函数:
1 filter(function, sequence)
对sequence中的item依次执行function(item),将执行结果为True的item做成一个filter object的迭代器返回。可以看作是过滤函数。
- str = ['a', 'b','c', 'd']
- def fun1(s):
- if s != 'a':
- return s
- ret = filter(fun1, str)
- print(list(ret))# ret是一个迭代器对象
2 map(function, sequence)
对sequence中的item依次执行function(item),将执行结果组成一个map object迭代器返回。
- str = ['a', 'b','c','d']
- def fun2(s):
- return s + "alvin"
- ret = map(fun2, str)
- print(ret) # map object的迭代器
- print(list(ret))# ['aalvin', 'balvin', 'calvin', 'dalvin']
map也支持多个sequence,这就要求func也支持相应数量的参数输入:
- 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)
对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用。
4 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在创建时不需要命名,所以,叫匿名函数
7 函数式编程
学会了上面几个重要的函数后,我们就可以来聊一聊函数式编程到底是个什么鬼
一 概念(函数式编程)
函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperativeprogramming),函数式编程,常见的面向对象编程是也是一种命令式编程。
命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。
而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。
函数式编程的本质
函数式编程中的函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如y=x*x函数计算x的平方根,只要x的平方,不论什么时候调用,调用几次,值都是不变的。
纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。
函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的。
严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。
函数式编程关心数据的映射,命令式编程关心解决问题的步骤,这也是为什么“函数式编程”叫做“函数式编程”。
二 实例
现在,让你把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]
三 函数式编程的好处
1)代码简洁,易懂。
2)无副作用
由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。没有可变的状态,函数就是引用透明(Referential transparency)的和没有副作用(No Side Effect)。
python的函数的更多相关文章
- python strip()函数 介绍
python strip()函数 介绍,需要的朋友可以参考一下 函数原型 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾处,位于 rm删除 ...
- python split()函数
Python split()函数 函数原型: split([char][, num])默认用空格分割,参数char为分割字符,num为分割次数,即分割成(num+1)个字符串 1.按某一个字符分割. ...
- Python数学函数
1.Python数学函数 1.abs(x):取绝对值,内建函数 2.math.ceil(x):向上取整,在math模块中 3.cmp(x,y):如果 x < y ,返回-1:如果 x == y ...
- Python回调函数用法实例详解
本文实例讲述了Python回调函数用法.分享给大家供大家参考.具体分析如下: 一.百度百科上对回调函数的解释: 回调函数就是一个通过函数指针调用的函数.如果你把函数的指针(地址)作为参数传递给另一个函 ...
- Python之函数与变量
本节内容 函数介绍及其作用 函数的定义与调用 函数的参数说明 全局变量与局部变量 值传递和引用传递 一.函数的介绍及其作用 编程语言中的函数与数学中的函数是有区别的:数学中的函数有参数(输入),就会有 ...
- Python基础-函数篇
本节内容 1. 函数基本语法及特性 2. 参数与局部变量 3. 返回值 嵌套函数 4.递归 5.匿名函数 6.函数式编程介绍 7.高阶函数 8.内置函数 函数与函数式编程 1.面向对象: 华山派-- ...
- 【C++实现python字符串函数库】strip、lstrip、rstrip方法
[C++实现python字符串函数库]strip.lstrip.rstrip方法 这三个方法用于删除字符串首尾处指定的字符,默认删除空白符(包括'\n', '\r', '\t', ' '). s.st ...
- 【C++实现python字符串函数库】二:字符串匹配函数startswith与endswith
[C++实现python字符串函数库]字符串匹配函数startswith与endswith 这两个函数用于匹配字符串的开头或末尾,判断是否包含另一个字符串,它们返回bool值.startswith() ...
- 【C++实现python字符串函数库】一:分割函数:split、rsplit
[C++实现python字符串函数库]split()与rsplit()方法 前言 本系列文章将介绍python提供的字符串函数,并尝试使用C++来实现这些函数.这些C++函数在这里做单独的分析,最后我 ...
随机推荐
- Linux驱动学习 —— 在/sys下面创建目录示例
有时我们需要在/sys下面创建一些目录, 下面给出了一个示例. 在加载驱动模块后, 在/sys下面会创建一个名为sysfs_demo的目录,并在其中在创建几个文件和目录. [root@tiny4412 ...
- ASP.NET MVC 登录验证
好久没写随笔了,这段时间没 什么事情,领导 一直没安排任务,索性 一直在研究代码,说实在的,这个登录都 搞得我云里雾里的,所以这次我可能也讲得不是 特别清楚,但是 我尽力把我知道的讲出来,顺便也对自 ...
- linux su和sudo命令的区别
一. 使用 su 命令临时切换用户身份 1.su 的适用条件和威力 su命令就是切换用户的工具,怎么理解呢?比如我们以普通用户beinan登录的,但要添加用户任务,执行useradd ,beinan用 ...
- 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍
一.pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主要目的是为了数据分析.它提供了大量高级的数据结构和对数据处理的方法. pandas 有两个主要的数据结构 ...
- 前端打包构建工具gulp快速入门
因为之前一直有人给我推荐gulp,说他这里好哪里好的.实际上对我来说够用就行.grunt熟悉以后实际上他的配置也不难,说到效率的话确实是个问题,尤其项目大了以后,目前位置遇到的项目都还可以忍受.不过不 ...
- 谈谈枚举的新用法——java
问题的由来 前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了! 事情是这样的,做过游戏的都知道,buff,需要分类型 ...
- 简单封装分页功能pageView.js
分页是一个很简单,通用的功能.作为一个有经验的前端开发人员,有义务把代码中类似这样公共的基础性的东西抽象出来,一来是改善代码的整体质量,更重要的是为了将来做类似的功能或者类似的项目,能减少不必要的重复 ...
- TeamCity : Build 版本控制系统配置
VCS (版本控制系统) 是用来跟踪项目源文件版本变化的系统.它还有其它的名字,比如 SCM(源代码管理).当前 TeamCity 内置支持的 VCS 类型有:Git, Subversion, Mer ...
- 翻译:使用 ASP.NET MVC 4, EF, Knockoutjs and Bootstrap 设计和开发站点 - 1
原文地址:http://ddmvc4.codeplex.com/ 原文名称:Design and Develop a website using ASP.NET MVC 4, EF, Knockout ...
- 运算符.png