热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python之路【第四篇】:函数、递归、内置函数

一.背景提要现在老板让你写一个监控程序,监控服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,你掏空了所有的知识量,写出了以下代码whileT

一. 背景提要

现在老板让你写一个监控程序,监控服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,你掏空了所有的知识量,写出了以下代码

while True:
    if cpu利用率 > 90%:
        #发送邮件提醒
        连接邮箱服务器
        发送邮件
        关闭连接
     
    if 硬盘使用空间 > 90%:
        #发送邮件提醒
        连接邮箱服务器
        发送邮件
        关闭连接
     
    if 内存占用 > 80%:
        #发送邮件提醒
        连接邮箱服务器
        发送邮件
        关闭连接

上面的代码实现了功能,但即使是邻居老王也看出了端倪,老王亲切的摸了下你家儿子的脸蛋,说,你这个重复代码太多了,每次报警都要重写一段发邮件的代码,太low了,这样干存在2个问题:

  1. 代码重复过多,一个劲的copy and paste不符合高端程序员的气质
  2. 如果日后需要修改发邮件的这段代码,比如加入群发功能,那你就需要在所有用到这段代码的地方都修改一遍

你觉得老王说的对,你也不想写重复代码,但又不知道怎么搞,老王好像看出了你的心思,此时他抱起你儿子,笑着说,其实很简单,只需要把重复的代码提取出来,放在一个公共的地方,起个名字,以后谁想用这段代码,就通过这个名字调用就行了,如下:

def 发送邮件(内容)
    #发送邮件提醒
    连接邮箱服务器
    发送邮件
    关闭连接
     
while True:
     
    if cpu利用率 > 90%:
        发送邮件('CPU报警')
     
    if 硬盘使用空间 > 90%:
        发送邮件('硬盘报警')
     
    if 内存占用 > 80%:
        发送邮件('内存报警')

你看着老王写的代码,气势恢宏、磅礴大气,代码里透露着一股内敛的傲气,心想,老王这个人真是不一般,突然对他的背景更感兴趣了,问老王,这些花式玩法你都是怎么知道的? 老王亲了一口你儿子,捋了捋不存在的胡子,淡淡的讲,“老夫,年少时,师从京西沙河淫魔银角大王 ”, 你一听“银角大王”这几个字,不由的娇躯一震,心想,真nb,怪不得代码写的这么6, 这“银角大王”当年在江湖上可是数得着的响当当的名字,只可惜后期纵欲过度,卒于公元2016年, 真是可惜了,只留下其哥哥孤守当年兄弟俩一起打下来的***。 此时你看着的老王离开的身影,感觉你儿子跟他越来越像了。。。

总结使用函数的好处:

1.代码重用

2.保持一致性,易维护

3.可扩展性

二. 什么是函数?

函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的。

初中数学函数定义:一般的,在一个变化过程中,如果有两个变量x和y,并且对于x的每一个确定的值,y都有唯一确定的值与其对应,那么我们就把x称为自变量,把y称为因变量,y是x的函数。自变量x的取值范围叫做这个函数的定义域

例如  y=2*x

2.1 python函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

python中函数定义:函数是逻辑结构化和过程化的一种编程方法。   可以理解为: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可;

特性:

  1. 减少重复代码
  2. 使程序变的可扩展
  3. 使程序变得易维护

2.2 定义一个函数

你可以定义一个由自己想要功能的函数,以下是简单的规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()
  • 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

语法定义:

def sayhi():                 # def:定义函数的关键字,函数名:sayhi,()内可以指定形参
  """The function definitions"""     # 文档描述(非必要,但是强烈建议为你的函数添加描述信息)
    print("Hello, I'm nobody!")       # 泛指代码块或程序处理逻辑
sayhi()                   # 调用函数,通过函数名()的形式

2.3 函数和过程

过程定义:过程就是简单特殊没有返回值的函数

这么看来我们在讨论为何使用函数的的时候引入的函数,都没有返回值,没有返回值就是过程,没错,但是在python中有比较神奇的事情:

def test01():
    msg = 'hello The little green frog'
    print
    msg

def test02():
    msg = 'hello WuDaLang'
    print
    msg
    return msg

t1 = test01()
t2 = test02()

print('from test01 return is [%s]' % t1)
print('from test02 return is [%s]' % t2)

总结: 当一个函数/过程没有使用return显示的定义返回值时,python解释器会隐式的返回None,

所以在python中即便是过程也可以算作函数。

def test01():
    pass

def test02():
    return 0

def test03():
    return 0, 10, 'hello', ['alex', 'lb'], {'WuDaLang': 'lb'}

t1 = test01()
t2 = test02()
t3 = test03()

print('from test01 return is [%s]: ' % type(t1), t1)
print('from test02 return is [%s]: ' % type(t2), t2)
print('from test03 return is [%s]: ' % type(t3), t3)

总结:

   返回值数=0:返回None

   返回值数=1:返回object

   返回值数>1:返回tuple

2.4 函数实例:

实例1:

以下为一个简单的Python函数,它将一个字符串作为传入参数,再打印到标准显示设备上。

def printme( str ):
   "打印传入的字符串到标准显示设备上"
   print str
   return

# 调用函数
printme("我要调用用户自定义函数!");

实例2:

传递参数且有返回值的函数

a,b = 5,8
c = a**b
print(c)
 
#改成用函数写
def calc(x,y):
    res = x**y
    return res #返回函数执行结果
 
c = calc(a,b) #结果赋值给c变量
print(c)

注: 函数定义阶段只检测函数体的语法,并不会执行

2.5 函数的调用

def foo():
    print('from foo')

def bar(name):
    print('bar===>',name)

foo() #调用函数foo
bar() #调用函数bar

按照有参和无参可以将函数调用分两种

foo()      # 定义时无参,调用时也无需传入参数
bar('egon') # 定义时有参,调用时也必须有参数

按照函数的调用形式和出现的位置,分三种

foo() #调用函数的语句形式

def my_max(x,y):
    res=x if x >y else y
    return res
res=my_max(1,2)*10000000 #调用函数的表达式形式
print(res)

res=my_max(my_max(10,20),30) #把函数调用当中另外一个函数的参数
print(res)

2.6 函数的返回值

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

注意:

  1. 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
  2. 如果未在函数中指定return,那这个函数的返回值为None
  3. return 一个值 函数调用返回的结果就是这个值
  4. return 值1,值2,值3,... 返回结果:(值1,值2,值3,...)

2.7 自定义函数

定义函数的三种形式

  • 无参数函数:如果函数的功能仅仅只是执行一些操作而已,就定义成无参函数,无参函数通常都有返回值
  • 定义有参函数:函数的功能的执行依赖于外部传入的参数,有参函数通常都有返回值
  • 空函数

 

三. 函数参数

形参: 变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。

实参: 可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值。

Python之路【第四篇】: 函数、递归、内置函数

 

参数分类

从大的角度去看,函数的参数分两种:形参(变量名),实参(值)

详细的区分函数的参数分为五种:

  1. 位置参数                #标准调用:实参与形参位置一一对应
  2. 关键字参数          #关键字调用:位置无需固定
  3. 默认参数
  4. 可变长参数(*args,**kwargs)
  5. 命名关键字参数

位置参数

def foo(x,y,z):#位置形参:必须被传值的参数
    print(x,y,z)

foo(1,2,3) #位置实参数:与形参一一对应

关键字参数

def foo(x,y,z):
    print(x,y,z)

foo(z=3,x=1,y=2)      #正确 实参通过key=value的形式给形参传值
foo(1,z=3,y=2)        #正确 位置参数与关键字参数混用
foo(x=1,2,z=3)        #错误 关键字实参必须在位置实参后面
foo(1,x=1,y=2,z=3)    #错误 不能重复对一个形参数传值

注意:

1. 关键字实参必须在位置实参后面
2. 不能重复对一个形参数传值

默认参数

def register(name,age,sex='male'): #形参:默认参数
    print(name,age,sex)

register('asb',age=40)
register('a1sb',39)
register('a2sb',30)
register('a3sb',29)

注意:

1. 默认参数必须跟在非默认参数后

def register(sex='male',name,age): #在定义阶段就会报错
    print(name,age,sex) 

2. 默认参数在定义阶段就已经赋值了,而且只在定义阶段赋值一次

3. 默认参数的值通常定义成不可变类型

非固定参数

若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数

第一种: *args

def foo(x,y,*args): #*会把溢出的按位置定义的实参都接收,以元组的形式赋值给args
    print(x,y)
    print(args)

第二种: **kwargs

def stu_register(name,age,*args,**kwargs): # **会把溢出的按关键字定义的实参都接收,以字典的形式赋值给kwargs
    print(name,age,args,kwargs)
 
stu_register("Alex",22)
#输出
#Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空
 
stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong")
#输出
# Jack 32 ('CN', 'Python') {'province': 'ShanDong', 'sex': 'Male'}

命名关键字参数

def foo(name,age,*,sex='male',height):  #*后定义的参数为命名关键字参数,这类参数,必须被传值,而且必须以关键字实参的形式去传值
    print(name,age)
    print(sex)
    print(height)
foo('egon',17,)

知识点1:

def foo(name,age=10,*args,sex='male',height,**kwargs):
    print(name)
    print(age)
    print(args)
    print(sex)
    print(height)
    print(kwargs)

foo('alex',1,2,3,4,5,sex='female',,a=1,b=2,c=3)
# 输出
alex
1       ----->age被位置参数填充
(2, 3, 4, 5)
female
150
{'a': 1, 'c': 3, 'b': 2}

知识点2:

def foo(*args):
    print(args)
foo(*(1,2,3,4)) = foo(1,2,3,4)    # 传入元组,1,2,3,4 <=====>*(1,2,3,4) 此时,*=* args=(1,2,3,4)
# 输出
(1, 2, 3, 4)
foo(*['A','B','C','D']) = foo('A','B','C','D')  # 传入列表, 此时,['A','B','C','D']相当于('A','B','C','D')
# 输出
('A', 'B', 'C', 'D')
foo(['A','B','C','D'])     #  传入列表,此时,列表作为输出的元组中的一个元素
# 输出
(['A', 'B', 'C', 'D'],)

知识点3:

def foo(**kwargs):
    print(kwargs)

foo(**{'y': 2, 'x': 1,'a':1}) #foo(a=1,y=2,x=1)   #实参传入字典,此时,{'y': 2, 'x': 1,'a':1}相当于a=1,y=2,x=1

知识点4: 接收任意个实参

def wrapper(*args,**kwargs):
    print(args)
    print(kwargs)

wrapper(1,2,3,a=1,b=2)

知识点5:

def foo(x,y,z):
    print('from foo',x,y,z)
def wrapper(*args,**kwargs):
    # print(args)      #args=(1,)
    # print(kwargs)     #kwargs={'y':3,'z':2}
    foo(*args,**kwargs)  #foo(*(1,),**{'y':3,'z':2}) #foo(1,z=2,y=3)
wrapper(1,z=2,y=3)
# 输出
from foo 1 3 2

  

四. 局部变量与全局变量

实例:

name = "ShuKe"
def change_name(name):
    print("before change:", name)
    name = "fengfeng"
    print("after change", name)

change_name(name)
print("在外面看看name改了么?", name)

输出

before change: ShuKe
after change fengfeng
在外面看看name改了么? ShuKe

总结:

  • 在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
  • 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
当全局变量与局部变量同名时:
  • 在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。

 

 五. 前向引用之'函数即变量'

def action():
    print 'in the action'
    logger()
action()
报错NameError: global name 'logger' is not defined

def logger():
    print 'in the logger'
def action():
    print 'in the action'
    logger()
 
action()
 
def action():
    print 'in the action'
    logger()
def logger():
    print 'in the logger'
 
action()

  

 六. 嵌套函数和作用域

看上面的标题的意思是,函数还能套函数?当然可以...

name = "Alex"
 
def change_name():
    name = "Alex2"
 
    def change_name2():
        name = "Alex3"
        print("第3层打印",name)
 
    change_name2() #调用内层函数
    print("第2层打印",name)
 
 
change_name()
print("最外层打印",name)

#输出
第3层打印 Alex3
第2层打印 Alex2
最外层打印 Alex

作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变

# 例一:
name='alex'

def foo():
    name='lhf'
    def bar():
        print(name)
    return bar

func=foo()
func()


# 例二:
name='alex'

def foo():
    name='lhf'
    def bar():
        name='wupeiqi'
        def tt():
            print(name)
        return tt
    return bar

func=foo()
func()

输出

lhf
wupeiqi

 

七. 递归

在函数内部,可以调用其他函数。如果在调用一个函数的过程中直接或间接调用自身本身

def calc(n):
    print(n)
    if int(n/2) ==0:
        return n
    return calc(int(n/2))
 
calc(10)
 
输出:
10
5
2
1
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
import time

person_list=['alex','wupeiqi','yuanhao','linhaifeng']
def ask_way(person_list):
    print('-'*60)
    if len(person_list) == 0:
        return '没人知道'
    person=person_list.pop(0)
    if person == 'linhaifeng':
        return '%s说:我知道,老男孩就在沙河汇德商厦,下地铁就是' %person
    print('hi 美男[%s],敢问路在何方' %person)
    print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list))
    time.sleep(3)
    res=ask_way(person_list)
    # print('%s问的结果是: %res' %(person,res))
    return res



res=ask_way(person_list)

print(res)

递归问路
递归问路
推荐阅读
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
author-avatar
Gala彬
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有