(内容包括函数、递归、Lambda、作用域等)

1. 函数

1.1 函数概述

函数是对程序逻辑进行结构化和过程化的一种编程方法,用于封装一个特定的功能,表示一个功能或者行为。函数是可以重复执行的语句块,是为了满足高重用和低冗余的最基本的程序结构,一次编写可以多次调用。【高重用、低冗余】

可以简单将函数分为内置函数和自定义函数。内置函数是python或已经导入模块中已经写好了的功能,自定义函数是将一段有规律、可重复使用的代码定义出的一个功能,是程序中一个可管理的部件。【流程分解】

1.2 常用内置函数

python中集成了许多方便的功能,以函数的形式供给使用。官方文档

1.3 自定义函数

自定义函数,也就是自己创建一个函数,带有某些用途,可以使用的一个代码块。自定义函数使用起来和内置函数一样:通过表达式进行调用,输入一些参数,得到一个结果。

定义函数遵循以下规则:

  1. 函数有0个输入、有1个或多个输出。
  2. 使用def关键字定义函数;用return或yield进行返回,无返回默认return None;函数体中实现功能。
  3. 具体形式是:def 加自定义函数名定义函数的名字,名字自己取,做到望文生义;之后加小括号,括号中先加普通参数,再加默认参数,最后加可变参数;在参数列表后加冒号,下一行缩进的就是函数主体,即每次调用时运行的部分;函数主体中需要有一个或多个返回值(如果没有默认返回None)。

1.3.1 自定义函数的定义

自定义函数利用关键字def(即简写的define)来进行定义,def后要写一个函数名,函数名要能做到望文生义,符合取名规则(字母开头,只含字母下划线和数字,非关键字),函数名后是你这个函数所需要的参数(普通参数、默认参数、变参)。接着就是冒号和下面缩进书写的函数体了,函数体中需要带有返回值(没有返回值默认return None),调用时一旦return,意味着调用结束。再说说返回值,返回值可以是任意类型,但只能返回一个对象。

#输入参数日月年,返回和今天差几天
import time
def isLeapYear(y):
if y%4==0 and y%100!=0 or y%400==0 :
return True
else :
return False def daysGone(m,flag):
listGone1 = (365,0,31,59,90,120,151,181,212,243,273,304,334) #平年
listGone2 = (366,0,31,60,91,121,152,182,213,244,274,305,335) #闰年
if flag :
return listGone2[m]
else :
return listGone2[m] def date_apart(d1,m1,y1):
lastest = [int(time.strftime("%d")),int(time.strftime("%m")),int(time.strftime("%Y"))]
daysofMonth = {1:31,2:28,3:31,4:30,5:31,6:30,7:31,8:31,9:30,10:31,11:30,12:31}
leap = total = 0
for i in range(y1+1,lastest[2],1):
total += 365
if i%4==0 and i%100!=0 or i%400==0 :
leap += 1
total += leap
total += daysGone(lastest[1],isLeapYear(lastest[2])) + lastest[0]
total += 365 + isLeapYear(y1) - daysGone(m1,isLeapYear(y1)) - d1
if y1==lastest[2] : total -= 365
return total print(date_apart(17,8,1926))

从上面的函数里,可以看出:

①函数可以作为另一个函数的参数

②返回值的类型可以多样,但运行到return就会停下

③函数体中可以调用其他函数

1.3.2 关于函数参数

函数的参数可以分为实参(普通参数、默认参数)和变参(位置参数、命名参数)。定义时:实参必须在变参的前面,建议默认参数放在普通参数后面。得到的参数可以出现或不出现在函数体中(就是说,参数你要来了,用不用随你自己)。调用时:输入参数的时候,可以根据顺序一个一个输,也可以以【参数名=赋的值】的形式,不按顺序输。默认参数不赋值就按默认值输入。变参就是把不定个数的参数传入函数。

对于可变参数,可以用任意名字前加一个和两个*号,写在参数列表的最后,先单*后双*的顺序,常用【*args,**kargs】和【*vars,**kvars】这两组。*符号表示需要接收的多出来的list和tuple,用**符号表示需要接收的map和dictionary。换而言之,①参数列表中的形如a=b形式(键值对)的命名参数,就传入**kargs;②参数列表中形如a,b,c这种形式(元组)的位置参数,就传入*args。

如果实参不够,会向位置参数(单个*号的变参)借参数;等位置参数借没了,再问命名参数(两个*号的变参)借参数(键值对),但是键必须等于实参的名字,才会把值传进实参。另一方面,如果实参的位置多了参数,键值对就会填进命名参数,成为字典,其他的会按顺序填进位置参数,成为元组。 另外,在参数列表中的变参,位置参数必须在命名参数前被赋值。

在调用函数时,即使定义的都是实参,也可以用*和**号。*号的作用是展开多个元素的类型,区别:*号只显示键keys(),**号显示键值对items()。

关于默认参数:

默认参数一旦生成,用户不去手动改变,默认参数就不会变。比如def add(a,b,c=[]), 那一旦第一次调用时对c传入非空列表[1],则c就会被赋值为[1],之后再调用的时候就默认是c=[1]而不是[]。 同样的,如果默认参数传入随机数,第一次会随机,后面就一直取第一次的随机值。因此,参数中需要随机值,一定要用实参去传入,不能用默认参数。

def tt(a,b,z,*c,**d):
print(a)
print(b)
print(z)
print(c)
print(d) tt(*range(1,15,3)) #实参不够,位置参数加入实参
print("---------------------------")
tt(1,*range(2,3),**{"z":20,"bbb":23}) #实参和位置参数不够,命名参数加入
print("---------------------------")
tt(1,3,5,7,9,11,13,15,f=10,x=16,k="ppp") #实参位置多了,放进变参

1.3.3 函数体

函数定义中,缩进的部分就是函数体,函数体是主要负责实现函数功能并返回值的地方。函数体以冒号起始,且缩进。第一行如果用文档字符串(三个冒号),就会被认作是在写函数的说明文档。在函数体中可以写几乎所有语句,甚至再定义一个函数。如果只是写一个函数名,函数体还没有决定,可以用pass关键字先做占位。 函数体中需含有返回值,用return或yield关键字返回。如果没有返回值,或只写了return没跟变量,默认返回空值None。函数体中(包括返回值)可以调用其他函数或自己。

1.3.4 返回值

为什么有了print语句直接输出,还要return呢?这是因为,函数只帮我们解决了一个问题,而不是这整件事。举上面的例子,我想知道是不是闰年,输出是不是没有用,但是返回一个True,却可以作为后续函数的实参输入。

用return和yield两个关键词可以将需要的变量进行返回。返回值在调用函数的时候,作为函数运行后的输出。

同样可以返回值,return和yield两个关键词的区别是什么呢?return一次全部输出,输出后结束;yield配合next或send一起使用,每次输出一步,下次调用再输出下一步。简单的说,return从一而终,yield阻塞在yield这一句,用next调用从这句yield开始,到yield再阻塞。



从上图的结果可以看出,return直接在第一次循环中输出了第一次循环值,就结束了;而next+yield则第一次运行到yield就不再运行,被下一个next激活后,从上一次yield开始运行,循环后到下一个yield又不运行了,这就是yield。

1.4 函数的调用

在自定义函数定义完以后,可以进行调用。只有调用的时候,才会检查自定义函数写的是不是正确。调用的时候,你将自定义函数需要的参数写到函数名后面的参数列表中,可以用位置参数和命名参数的方法,需要注意个数,位置参数需要注意位置。另外,对于帮助文档,用help(NAME)即可(不加参数名)。 函数的结果就是函数运行后的返回值。

1.5 递归 Recursion

在函数定义中,也可以在函数体中调用其他函数。简单地说,如果被调用的是自己或者几个函数相互调用,就叫做递归。严格地说, 适合用递归的是:【能把问题分解成为规模更小的、具有与原问题有着相同解法的问题。】 递归成功有一些先决条件:①随着递归深度的加深(次数变多),问题应该越来越简单;②递归中应该有一个判断(递归出口),在问题最简单的时候返回不再需要调用的返回值;③递归的次数不是无限制的,不做设置递归1000次左右就会栈溢出。【在一定次数中,深度增加规模减小,最后会结束】

递归的特点:写起来简单,容易让人理解,没有复杂嵌套,容易栈溢出,多次调用费时效率低。

数据、数据结构、问题描述是递归形式的,应该想起递归。

递归的两种思路:①执行下一次用到上一次的结果(递推);终止时需要上一层的条件(回溯)。

1.6 Lambda表达式

Lambda表达式是用来简化编程者工作量的一种匿名函数。特点是:允许你快速定义一个单行的最小函数。它的唯一语法形式是:

在式中,冒号前的变量就是函数中的参数列表,冒号后就是函数中的返回值的表达式。Lambda表达式看起来和用起来简单,更pythonic,但内部逻辑和定义一个函数是一样的,且不支持多分支语言和异常处理程序。用法上,它的输入就是参数列表,输出就是返回值的结果。它可以配合filter、sorted、map、reduce等函数一同使用。

1.7 帮助文档

在使用一个函数的时候,就算起名已经望文生义了,但是你还是需要一些更多的支持,比如说帮助文档或说明书。在自定义函数中,只要利用文档字符串,就可以方便的进行帮助文档的书写。

具体来说,想写一个函数的帮助文档,你需要在定义函数后,函数体的第一行利用字符串(一行用普通引号,多行用三个引号)进行输入,整个文档字符串都会被视作帮助文档。调用时,用help(NAME)函数进行查看,只需要写函数名,不要写参数列表。

1.8 作用域

先说说赋值,赋值是将你取的变量名和这个对象建立连接。也就是说,赋值是名字指向新的对象,而不是通过名字直接改变对象。

如果在自己的函数中和脚本主函数中的变量重名了,怎么办呢? 如果仅仅是引用外部变量,那么按LEGB顺序在不同作用域查找该名字。

顺序:localsenclosing functionglobals__builtins__,(即:内部嵌套函数包含内部嵌套函数的函数自身全局作用域内置作用域),作用域的优先级从高到低,作用域的范围由小到大。 其中:

  • locals:函数内部名字空间,包括局部变量和形参
  • enclosing function: 外部嵌套函数的名字空间
  • globals: 函数定义所在模块的名字空间
  • __builtins__: 内置模块的名字空间

内置作用域是预先定义好的,在__builtins__模块中。这些名称主要是一些关键字,例如open、range、quit等;全局作用域是文件/模块级别的,每个.py文件中处于顶层的变量都是全局作用域范围内的变量;本地作用域是函数内部属于本函数的作用范围,因为函数可以嵌套函数,嵌套的内层函数有自身的内层范围;嵌套函数的本地作用域是属于内层函数的范围,不属于外层。

当一条命令需要用到某个对象时,他会按照LEGB顺序,先找本层函数中有没有。

从上面图中可以看出,在1,4两次print中,打印的是全局变量x,在调用了k以后,打印的是k中的函数内部名字空间,而m无法被脚本读取。 解释一下,参数列表和函数中定义的变量叫做局部变量,只能在这个局部调用,而且不能改变更大一级的变量的值。更大一级也只能调用自己同级定义的函数,不能调用在函数中定义的函数。

既然参数列表中传进参数的改变不能使全局变量改变,那这样是不是说,函数只能通过返回值改变全局变量呢? 并不是,引入global关键字,就可以从函数中改变全局变量的值。如图:

1.9 函数设计理念

函数应力求独立于外部,输入尽量用参数,输出用return ; 只有在真正需要的时候,才去用全局变量;函数的目标应该单一、统一; 每个函数应该相对地小;尽量不去改变其他模块文件中的变量。

1.10 函数类型检查

Python3.5以后,加入了函数类型检查功能。在定义函数的参数列表所列举的参数后加入冒号和类型规定参数列表中形参的输入类型,在参数列表后用->符号规定返回值类型。

def sum(a:int,b:[int])->float:
for i in b:
c += i
return a*1.0+c

在上面的代码定义中,要求了参数a的类型必须是int型, 参数b的类型必须是list类型,且b中的每个元素必须是整数,返回值的类型必须是浮点型小数。 如果调用函数时,输入的实参和形参类型不相同,在IDE中就会进行警告,但是不影响函数的正常运行。

1.11方法命名

在Pyhton中,不仅可以对属性进行命名,如a=b对于方法也可以进行命名,如c=math.sqrt,这样,c就和math.sqrt等效,print(c(4))会输出2.0。

最新文章

  1. 敏捷转型历程 - Sprint3 Planning
  2. spine实现预加载(一)
  3. Frameset 框架集 导航栏 的使用
  4. Allegro16.3约束设置 (转载)
  5. 通过CSS禁用页面模块的复制和粘贴功能
  6. ural 1123
  7. java答疑
  8. CentOS LNMP安装phpMyAdmin
  9. plsql连接oracal数据库
  10. Linux网络管理——linux网络配置
  11. 牛逼的验证码,printf返回值
  12. 分享基于Qt5开发的一款故障波形模拟软件
  13. Winform外包团队 项目案例展示
  14. 小L的项链切割 (回文串)
  15. tesorflow - create neural network+结果可视化+加速神经网络训练+Optimizer+TensorFlow
  16. SublimeText3追踪函数工具CTags设置及使用
  17. C# ConcurrentQueue实现
  18. SAP ABAP: Error Message "Statement already exist" when creating a function module.
  19. 学会了ES6,就不会写出那样的代码
  20. week1day01 认识python 变量 数据类型 条件if语句

热门文章

  1. [前端web安全]XSS漏洞基础入门
  2. 常见的名片尺寸如何在CorelDRAW预设
  3. guitar pro系列教程(十三):Guitar Pro教程之打谱使用技巧
  4. Thread.start() ,它是怎么让线程启动的呢?
  5. Java基础教程——封装
  6. 拿到这份 Java、C++ 软件开发完整学习路线图,我面试再也没挂过..
  7. Arduion学习(三)驱动温度传感器
  8. 网络管理监视很重要!学编程的你知道哪些不错的网络监控工具?2020 最好的Linux网络监控工具分享给你
  9. 给集合null,filter结果空集合
  10. 微前端大赏二-singlespa实践