一、函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率,函数分为内部函数与外部函数。
定义规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
- 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。
下面将分别讲解函数:
- 函数基本语法与调用
- 参数传递与返回值
- 嵌套函数
- 递归函数
- 匿名函数
函数的基本语法与调用
Python 定义函数使用 def 关键字,一般格式如下:
def 函数名(参数列表): 函数体
【实例】
#编写一个函数并输出hello lyshark
>>> import os >>> import sys >>> >>> def lyshark(): #定义了函数 print("hello lyshark!") >>> lyshark() #调用了lyshark()函数 hello lyshark! >>>
#编写一个带参数的函数
>>> import sys >>> >>> def area(width,height): #一个计算面积的函数,其中width,height是形式参数 return width * height >>> def print_me(name): #一个打印函数,其中name是形式参数 print("welcome:",name) >>> print_me("lyshark") #调用打印函数 welcome: lyshark >>> >>> w=10 >>> h=25 >>> print(area(w,h)) #计算平方并打印 250 >>>
函数参数传递与返回值
函数参数:
默认情况下,参数通过其位置进行传递,从左至右,这意味着,必须精确地传递和函数头部参数一样多的参数,但也可以通过关键字参数、默认参数或参数容器等改变这种机制
- 普通参数:定义函数时从左至右
- 默认参数:定义函数时是使用"name=value"的语法直接给变量一个值,从而传入的值可以少于参数个数
- 指定参数:调用函数时指定"name形式参数=value实际参数"的语法通过参数名进行匹配
- 动态参数:定义函数时形式参数中收集任意多基于普通参数【定义函数时使用* :收集普通参数,返回元组,*args】【定义函数时使用**:收集指定参数,返回列表,**kwargs】
- 动态参数解包:调用函数时,使用**开头的参数,从而传递任意多基于普通或指定参数
形参与实参:
- 形式参数:形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
- 实际参数:实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
【实例】
#无参数情况下
#!/usr/bin/env python # -*- coding:utf-8 -*- ''' 1.需要开启邮箱服务sendmail 2.邮箱服务器需要开启SMTP服务 ''' def sendmail(): try: import smtplib from email.mime.text import MIMEText from email.utils import formataddr msg = MIMEText('邮件内容', 'plain', 'utf-8') msg['From'] = formataddr(["发件人", 'lyshark@126.com']) msg['To'] = formataddr(["收件人", '1181506874@qq.com']) msg['Subject'] = "邮件主题" server = smtplib.SMTP("smtp.126.com", 25) server.login("lyshark@126.com", "qew1233") server.sendmail('lyshark@126.com', ['1181506874@qq.com', ], msg.as_string()) server.quit() except: return False else: return True ret = sendmail() if ret == True: print("发送成功") else: print("发送失败"
#有参数情况下
#!/usr/bin/env python # -*- coding:utf-8 -*- def sendmail(email,content): try: import smtplib from email.mime.text import MIMEText from email.utils import formataddr msg = MIMEText(content, 'plain', 'utf-8') msg['From'] = formataddr(["发件人", 'lyshark@126.com']) msg['To'] = formataddr(["收件人", '1181506874@qq.com']) msg['Subject'] = "邮件主题" server = smtplib.SMTP("smtp.126.com", 25) server.login("lyshark@126.com", "qew1233") server.sendmail('lyshark@126.com', [email, ], msg.as_string()) server.quit() except: return "失败" else: return 'cc' while True: msg = input("请输入邮箱地址:") #实际参数 ret = sendmail(msg,"SB") if ret == 'cc': print("发送成功") break else: print("发送失败")
#普通参数传递
>>> def stu(name,age,country): print("姓名:",name) print("年龄:",age) print("国籍:",country) >>> stu("lyshark",22,"CN") 姓名: lyshark 年龄: 22 国籍: CN
#带默认参数的
>>> def stu(age,country,name="none"): print("姓名:",name) print("年龄:",age) print("国籍:",country) >>> stu(23,"CN") 姓名: none 年龄: 23 国籍: CN
注意:此处如果您要使用带默认参数的函数,需要把参数放在函数最后一项;
记住一个要求就是,关键参数必须放在位置参数之后
#动态参数
若你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数(传递一个列表)
>>> def stu(name,age,*args): #*args 会把多传入的参数变成一个元组形式 print(name,age,args) >>> stu("lyshark",22) lyshark 22 () #这个()就是args,只是因为没传值,所以为空 >>> >>> stu("lyshark",22,"a","b","c","d") lyshark 22 ('a', 'b', 'c', 'd') #传值后会把它当作一个列表
>>> def fun(*args): #动态参数返回元祖 print(args,type(args)) >>> lists=[1,2,3,4] >>> fun(lists) #传递一个列表 ([1, 2, 3, 4],) <class 'tuple'> >>> >>> fun(*lists) #执行函数时有*,把所有迭代对象拆分为单个元素作为元组的元素,如传入列表,会把列表中每一个元素遍历添加到元组中当作一个元素 (1, 2, 3, 4) <class 'tuple'> >>>
另一种姿势,**kwargs(传递一个字典)
>>> def stu(name,age,*args,**kwargs): print(name,age,args,kwargs) >>> stu("lyshark",22) lyshark 22 () {} >>> >>> stu("lyshark",22,"a","b",sex="Male",province="山东") lyshark 22 ('a', 'b') {'sex': 'Male', 'province': '山东'} >>>
>>> def fun(name,**kwargs): print(name,kwargs) >>> dic={"k1":"v1","k2":"v2"} >>> >>> fun("lyshark",**dic) lyshark {'k1': 'v1', 'k2': 'v2'} >>>
#万能参数(* 与 ** 通常情况可传递任意值)
>>> def fun(*args,**kwargs): print(args,type(args)) print(kwargs,type(kwargs)) >>> lists=[1,2,3,4,5,6,7,8,9] >>> dic={"a":1001,"b":1002,"c":1003} >>> >>> fun(*lists,**dic) (1, 2, 3, 4, 5, 6, 7, 8, 9) <class 'tuple'> {'a': 1001, 'b': 1002, 'c': 1003} <class 'dict'>
【补充】
其实在python中我们经常看到万能参数,比如str.format()方法,就是一个典型的万能参数的例子
>>> string="hello {0},age {1}" >>> print(string.format("lyshark",22)) hello lyshark,age 22 >>> >>> string="hello {name},age {age}" >>> print(string.format(name="lyshark",age=22)) hello lyshark,age 22 >>> >>> string="hello {0},age {1}" >>> print(string.format(*["lyshark",22])) hello lyshark,age 22 >>> >>> >>> dic ={"name":"lyshark","age":22} >>> >>> string="hello {name},age {age}" >>> print(string.format(**dic)) hello lyshark,age 22 >>>
在多个函数的情况下,默认使用后一个函数,后一个函数名会指向新的内存对象,函数名是函数体在内存中的引用
>>> def fun(a,b): return a+b >>> def fun(a,b): return a*b >>> print(fun(3,3)) 9 >>>
函数传递的是指针,所以我们的数据会被保留下来
>>> def fun(x): x.append(8888) >>> lists=[1,2,3] >>> fun(lists) >>> >>> print(lists) [1, 2, 3, 8888] >>>
由于函数没有定义返回值,所以默认为none
>>> def fun(x): x.append(8888) >>> lists=[1,2,3] >>> lists=fun(lists) >>> >>> print(lists) None >>>
函数返回值:
- return [表达式] 语句用于退出函数,选择性地向调用方返回一个表达式。
- 不带参数值的return语句默认返回None。
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,通俗的将遇到ret说明函数结束
【实例】
#函数的返回
>>> def add(num1,num2): sum=num1+num2 print("函数内返回:",sum) sum=sum+100 return sum >>> temp=add(10,20) 函数内返回: 30 >>> print("函数外返回:",temp) 函数外返回: 130 >>>
#选择性返回(如果输入的是偶数返回0,否则返回-1)
>>> def check(num): if (num %2 ==0): return 0 else: return -1 >>> print(check(2)) 0 >>> print(check(3)) -1 >>>
#函数作为返回值返回
>>> def pot_sum(*args): def sum(): x=0 for y in args: x=x+y return x return sum >>> pot_sum(1,2,3,4) #这时候ppt_sum 并没有执行,而是返回一个指向求和的函数的函数名sum 的内存指针。 >>> >>> f=pot_sum(1,2,3,4) >>> print(type(f)) #调用f()函数,才真正调用了 sum 函数进行求和,这其实就是闭包 <class 'NoneType'> >>> >>> print(f())
#返回一个函数列表
>>> def count(): fs=[] for i in range(1,4): def f(): return i*i fs.append(f) return fs >>> f1,f2,f3=count() >>> print(f1()) 9 >>> print(f2()) 9 >>> print(f3()) 9 >>>
嵌套函数
嵌套函数就是函数中有函数
name = "Alex" def change_name(): name = "Alex2" def change_name2(): name = "Alex3" print("第3层打印",name) change_name2() #调用内层函数 print("第2层打印",name) change_name() print("最外层打印",name)
递归函数
一个函数在其内部调用它自己,就叫做递归,但是递归的时候要设置一个退出递归的条件,不然会一直递归下去,变成一个死循环。
递归的条件:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出
【实例】
#使用递归实现阶乘
def f(n): if 0==n: # n=0 的话直接返回空,对用户输入的零进行判断 return None elif 1==n: # n=1 的话就不再递归 return n else: return n*f(n-1) # 递归在执行f(n-1) 直到f(1) print(f(5)) # 120
''' f(5)的执行过程如下 ===> f(5) ===> 5 * f(4) ===> 5 * (4 * f(3)) ===> 5 * (4 * (3 * f(2))) ===> 5 * (4 * (3 * (2 * f(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120 '''
#一个递归小例子
>>> def fun(x): x+=1 if x >= 5: return 0 return fun(x) >>> r=fun(5) >>> print(r) 0 >>>
#再来一个递归小例子
>>> def fun(x): print("--->%d"%x) if int(x/2)==0: return x return fun(int(x/2)) >>> fun(10) --->10 --->5 --->2 --->1
#使用递归实现二分法(折半查找)
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] def binary_search(dataset,find_num): print(dataset) if len(dataset) >1: mid = int(len(dataset)/2) if dataset[mid] == find_num: #find it print("找到数字",dataset[mid]) elif dataset[mid] > find_num :# 找的数在mid左面 print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid]) return binary_search(dataset[0:mid], find_num) else:# 找的数在mid右面 print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid]) return binary_search(dataset[mid+1:],find_num) else: if dataset[0] == find_num: #find it print("找到数字啦",dataset[0]) else: print("没的分了,要找的数字[%s]不在列表里" % find_num) binary_search(data,66)
匿名函数
python 使用 lambda 来创建匿名函数。所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda表达式会自动return返回值,条件为真返回True,条件为假返回False.
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法:
lambda [arg1 [,arg2,.....argn]]:expression
【实例】
>>> sum=lambda x,y,z: x+y+z >>> >>> print("三个数相加:",sum(10,20,30)) 三个数相加: 60 >>>
>>> res=map(lambda x:x**2,[1,5,4,8]) >>> >>> for i in res: print(i) 1 25 16 64 >>>
二、变量作用域
变量作用域:
- L (Local) 局部作用域
- E (Enclosing) 闭包函数外的函数中
- G (Global) 全局作用域
- B (Built-in) 内建作用域
变量属性:
- 变量的先后顺序是: L –> E –> G –>B 的规则查找
- 在子程序中定义的变量称为局部变量
- 在程序的一开始定义的变量称为全局变量
- 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序
- 当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用
- 当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了
- 局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问
【实例】
#定义一个全局变量
>>> import os >>> import sys >>> >>> sum=100 #这就是一个全局变量 >>> >>> def print_sum(): print("函数中调用sum: ",sum) >>> print_sum() #函数中可以读取到 函数中调用sum: 100 >>> >>> print("函数外调用sum: ",sum) #外部依然可以读取到 函数外调用sum: 100 >>>
#定义一个局部变量
>>> import sys >>> import os >>> >>> def print_sum(): sum=100 print("函数中调用sum: ",sum) >>> print_sum() 函数中调用sum: 100 >>> print("函数外调用sum: ",sum) 函数外调用sum: <built-in function sum>
#将一个局部变量转为全局变量(关键字global)
>>> import os >>> import sys >>> >>> def print_num(): global num num=1000 print("函数内调用: ",num) >>> print_num() 函数内调用: 1000 >>> >>> print("函数外调用: ",num) 函数外调用: 1000 >>>
#内部作用域想修改外部作用域的变量时,就要用到global声明成全局变量
>>> num=100 >>> >>> def print_sum(): global num #使用global声明 print("全局变量: ",num) num=1000 print("局部变量: ",num) >>> >>> print_sum() 全局变量: 100 局部变量: 1000 >>>
#如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字
>>> import sys >>> >>> def outer(): num=100 def inner(): nonlocal num #声明成外层非全局作用域 num=1000 print("inner层:",num) inner() print("outer层:",num) >>> outer() inner层: 1000 outer层: 1000 >>>