Python全栈开发-Python基础教程-05 函数

函数

一. 函数的定义与调用

1.1 函数的定义

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

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

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

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

在这里插入图片描述
语法格式如下:

def 函数名(参数列表):
    函数体
1.2 函数的调用

语法格式如下:

func() # 函数名 + 括号

下面我们简单实现几个函数实例并对齐进行调用:

程序示例:

1.打印一段文本

def hello() :   #函数定义
    print("Hello World!")

hello()         #函数调用

运行结果:

Hello World

2.计算字符串长度

def my_len():      #函数定义
    s1 = "hello world"
    length = 0
    for i in s1:
        length = length+1
    print(length)

my_len()           #函数调用

运行结果:

11

3.判断一串数字是否为电话号码:

phone = '13345671234'
def func(phone):   #函数定义
    if phone.isdigit() and len(phone) == 11:
	    print('这是一个电话号码')
    else:
	    print('这不是一个电话号码')

func(phone)       #函数调用

运行结果:

这是一个电话号码

二 . 函数的返回值

关键字return的作用

1、返回一个值

2、终止一个函数的继续

程序实例:

def my_len():      # 函数名的定义
    s1='hello world'
    length=0
    for i in s1:
        length=length+1
    return length  # 函数的返回值

str_len=my_len()  #函数的调用以及返回值的接收

print(str_len)

运行结果:

11

在没有返回值的时候:

1、不写return与写入return None的效果相同,返回的只都是None

2、只写一个return后面不加任何东西的时候与写return None的效果一样

返回多个值:

1.当用一个变量接收返回值的时候,收到的是一个元组。这是因为在python中把用逗号分割的 多个值认为是一个元组。

程序实例:

def func():
    return "a" , "b"   #返回多个值时接收到的是一个元组
c = func()             # c接收的是一个元组
print(c)

运行结果:

('a', 'b')

2.当返回值有多个变量接收,那么返回值的个数应该和接收变量的个数完全一致。

程序实例:

#返回多个值,用多个变量接收(接收的变量数与返回值的个数要一致)
def func():
    return "a" , "b" , "c"
d , e , f = func()
print(d , e , f)


def func():
    return [1,2,3]
a , b , c = func() #列表和元组是可以解包的
print(a,b,c)

#返回的字典类型有点意外:
def func():
    return {"name":"span"}
dic = func() #需要字典类型来接收,而不能直接用k,v,字典解包解出来的只是键
print(dic)

运行结果:

a b c
1 2 3
{'name': 'span'}

3. return还有一个特殊的用途,一旦执行到return,后面的语句就不在执行了(结束一个函数)。(和break类似但有区别,break是跳出循环,如果循环后有代码则继续执行。return是结束整个函数)

4.如果在函数中有多个return,只执行第一个return。

三. 函数的参数

以下是调用函数时可使用的正式参数类型:

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

必备参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

程序示例1:

def printme(str):
   print(str)
   return
 
printme('hello world')  #调用printme()函数,你必须传入一个参数,不然会出现语法错误:

运行结果:

hello world

程序示例2:

def my_sum(a,b): 
    res = a + b
    return res
ret = my_sum(1,2)  #有两个参数就该传两个参数,1对应a;2对应b
print(ret)

运行结果:

3
3.2 关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。

使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

程序示例1:

def printme(str):
   print(str)
   return
 
printme( str = "My string") #调用printme函数

运行结果:

My string

程序示例2:

def my_sum(a,b):
    print(a,b)
    res = a + b
    return res

ret = my_sum(b=2,a=1)  #注意此处与参数顺序无关
print(ret)

运行结果:

1 2
3
3.3 默认参数

是可以不传的参数,在不传参数的情况下可以使用默认值;如果传了,就会使用传的值

程序示例1:

def printinfo(name, age = 35):
   print ("Name: ", name)
   print ("Age ", age)
   return
 
#调用printinfo函数
printinfo(age=50, name="miki")  #传入age参数
printinfo(name="miki")          #不传入age参数

运行结果:

Name:  miki
Age  50
Name:  miki
Age  35

程序示例2:
默认参数 魔性用法:默认参数尽量避免使用可变数据类型(默认参数的陷阱)

def func(L=[]):  # 相当于在def之前先定义了一个str=[],再将str赋值给L,如果不传参数就共用这个数据类型
    L.append(2)
    print(L)
    
func()
func()
func()
func()

运行结果:

[2]
[2, 2]
[2, 2, 2]
[2, 2, 2, 2]
3.4 不定长参数

你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数
语法如下:

def function_print(*args,**kwargs): # 传递不定长参数,即参数个数不固定
    print(args)
    print(kwargs)
 
function_print()

输出结果:

()
{}

代码分析:由输出结果可以看出来,第一个形参*args 是元组tuple类型第二个形参**kwargs是字典dict类型.

1.*args的使用方法 :

a.形参 *args 类型是元组tuple,外部调用函数时传递的参数不论是整数还是BOOL值或者是字符串str,实际上传递的都是元组数据;

b.如果函数形参是不定长参数,外部调用函数传递多个参数时,默认按顺序实参匹配形参,剩余的参数全部作为(元组)不定长参数传递;

c.如果没有为函数的不定长参数传递参数,默认为空元组();

程序示例:
注意:这里代码要一块一块的运行

#普通形参
def function_print1(arg):
    print("普通形参 : arg=",arg)

#不定长形参
def function_print2(*args):
    print("不定长形参 : args=",args)

#普通形参 + 不定长形参
def function_print3(arg,*args):
    print("普通形参 + 不定长形参 : arg=",arg)
    print("普通形参 + 不定长形参 : args=",args)

function_print1(False)
function_print1("hello world")
print("***"*20)

function_print2(False)
function_print2("hello world")
print("***"*20)

function_print3(False) # 只为函数传递了一个形参,不定长参数的形参默认为空元组()
function_print3("hello world")
print("***"*20)

# 如果函数的形参是不定长参数,当外部调用函数传递多个参数时,默认按顺序匹配形参,剩余的参数全部作为不定长参数传递
function_print3(False,1,23,4,5) 
function_print3("hello world",False,0,True,"python教程")

运行结果:

普通形参 : arg= False
普通形参 : arg= hello world
************************************************************
不定长形参 : args= (False,)
不定长形参 : args= ('hello world',)
************************************************************
普通形参 + 不定长形参 : arg= False
普通形参 + 不定长形参 : args= ()
普通形参 + 不定长形参 : arg= hello world
普通形参 + 不定长形参 : args= ()
************************************************************
普通形参 + 不定长形参 : arg= False
普通形参 + 不定长形参 : args= (1, 23, 4, 5)
普通形参 + 不定长形参 : arg= hello world
普通形参 + 不定长形参 : args= (False, 0, True, 'python教程')

2.**kwargs的使用方法

a.形参 **kwargs 类型是字典dict,函数外部调用函数传递参数时需要使用关键字参数,实参写法:a=1,b=2,c=False,d=”hello”;

b.如果函数形参是不定长参数,外部调用函数传递多个参数时,默认按顺序实参匹配形参,关键字参数全部作为(字典)不定长参数传递;

c.如果没有为函数的不定长参数传递参数,默认为空字典{};

程序示例
注意:这里代码要一块一块的运行

#普通函数
def function_print1(arg):
    print("普通函数形参 : arg=",arg)
 
#普通函数不定长形参
def function_print2(**kwargs):
    print("不定长形参 : args=",kwargs)
 
#普通函数形参 + 不定长形参
def function_print3(arg,**kwargs):
    print("普通函数形参 + 不定长形参 : arg=",arg)
    print("普通函数形参 + 不定长形参 : args=",kwargs)
 
function_print1(False)
function_print1("hello world")
print("***"*20)
 
function_print2(a=False)
function_print2(c="hello world")
print("***"*20)
 
function_print3(False)
function_print3("hello world")
print("***"*20)
 
function_print3(False,a=1,b=23,h=4,v=5)
function_print3("hello world",y=False,i=0,a=True,j="python教程")

运行结果:

普通函数形参 : arg= False
普通函数形参 : arg= hello world
************************************************************
不定长形参 : args= {'a': False}
不定长形参 : args= {'c': 'hello world'}
************************************************************
普通函数形参 + 不定长形参 : arg= False
普通函数形参 + 不定长形参 : args= {}
普通函数形参 + 不定长形参 : arg= hello world
普通函数形参 + 不定长形参 : args= {}
************************************************************
普通函数形参 + 不定长形参 : arg= False
普通函数形参 + 不定长形参 : args= {'a': 1, 'b': 23, 'h': 4, 'v': 5}
普通函数形参 + 不定长形参 : arg= hello world
普通函数形参 + 不定长形参 : args= {'y': False, 'i': 0, 'a': True, 'j': 'python教程'}

3.函数不定长参数args和kwargs只能放在形参的末尾,顺序不能错.*

def function_print(arg1,*args,**kwargs): # *args,**kwargs 必须在形参的末尾,顺序不能乱
    pass

四. 函数的作用域

4.1 作用域介绍

python中的作用域分4种情况:

  • L:local,局部作用域,即函数中定义的变量;
  • E: enclosing,父级函数的局部作用域,即此函数的上级函数的局部作用域。
  • G: globa,全局变量
  • B: build-in,系统模块,如:int,max函数等
    优先级顺序为:局部作用域(L) > 父级函数作用域(E) > 全局作用域(G) > 系统模块(B)
4.2 全局作用域

如果想要在函数内部改变外面不可变对象变量的值,则需要在函数内部使用global关键字
global是全局变量声明,声明之后可以在全局使用,这里的全局指的是当前py文件中
注意:函数内外名字要一致,否则就是一个新变量

程序示例:

variable = 223
def fun3():
	global variable
	variable += 1
	return variable

print(fun3())
print(variable)

运行结果:

224
224
4.3 局部作用域

程序示例:

def f4():
	var1 = 10
	def func5():
		nonlocal var1
		var1 += 1
		print('***',var1)
		return var1
	func5()
	var1 += 2
	print(var1)
	return var1

f4()

运行结果:

*** 11
13
4.4 闭包函数

1.必须嵌套函数。

2.内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量

3.外部函数必须返回内嵌函数——必须返回那个内部函数

闭包的作用:可以保持程序上一次运行后的状态然后继续执行。

程序示例:

# 闭包传参
def foo():
    num=1
    def add(n):
        nonlocal  num
        num += n
        return num
    return add
f=foo()
print(f(1))  #2
print(f(2))  #4

运行结果:

2
4

五. 递归函数和匿名函数

5.1 递归函数
5.1.1 概念

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
递归函数特性:

  • 必须有一个明确的结束条件
  • 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  • 相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)。
  • 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
5.1.2 代码实例
# 循环方式 
def sum_cycle(n): 
    sum = 0 
    for i in range(1,n+1) : 
        sum += i
    print(sum)
 
# 递归方式 
def sum_recu(n): 
    if n>0: 
       return n +sum_recu(n-1) 
    else: 
       return 0 
 
sum_cycle(100) 
sum = sum_recu(100)
print(sum)

运行结果:

5050
5050

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

把上面的递归求和函数的参数改成10000就导致栈溢出!

RecursionError: maximum recursion depth exceeded in comparison

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的

一般递归

def normal_recursion(n):
    if n == 1:
        return 1
    else:
        return n + normal_recursion(n-1)

执行过程:

normal_recursion(5)
5 + normal_recursion(4)
5 + 4 + normal_recursion(3)
5 + 4 + 3 + normal_recursion(2)
5 + 4 + 3 + 2 + normal_recursion(1)
5 + 4 + 3 + 3
5 + 4 + 6
5 + 10
15

可以看到, 一般递归, 每一级递归都需要调用函数, 会创建新的栈,随着递归深度的增加, 创建的栈越来越多, 造成爆栈:boom

尾递归

尾递归基于函数的尾调用, 每一级调用直接返回函数的返回值更新调用栈,而不用创建新的调用栈, 类似迭代的实现, 时间和空间上均优化了一般递归!

def tail_recursion(n, total=0):
    if n == 0:
        return total
    else:
        return tail_recursion(n-1, total+n)

执行过程:

tail_recursion(5)
tail_recursion(4, 5)
tail_recursion(3, 9)
tail_recursion(2, 12)
tail_recursion(1, 14)
tail_recursion(0, 15)
15
5.2 匿名函数
5.2.1 匿名函数的概念

在定义函数的时候,不想给函数起一个名字。这个时候就可以用lambda来定义一个匿名函数。

语法:

lambda 参数:表达式

注意:

  • 表达式中不能包含 循环,return
  • 可以包含 if…else…语句.
  • 表达式计算的结果直接返回
5.2.2 匿名函数与普通函数的对比

程序示例:

def sum_func(a, b, c):
    return a + b + c
 
 
sum_lambda = lambda a, b, c: a + b + c
 
print(sum_func(1, 100, 10000))
print(sum_lambda(1, 100, 10000))

运行结果:

10101
10101

可以看到,lambda适用于多个参数、一个返回值的情况,可以用一个变量来接收,变量是一个函数对象,执行这个函数对象的结果与执行一个普通函数的结果一样。

lambda函数比普通函数更简洁,且没有声明函数名,上面的代码是用一个变量来接收lambda函数返回的函数对象,并不是lambda函数的名字。

5.2.3 匿名函数的多种形式
# 无参数
lambda_a = lambda: 100
print(lambda_a())
 
# 一个参数
lambda_b = lambda num: num * 10
print(lambda_b(5))
 
# 多个参数
lambda_c = lambda a, b, c, d: a + b + c + d
print(lambda_c(1, 2, 3, 4))
 
# 表达式分支
lambda_d = lambda x: x if x % 2 == 0 else x + 1
print(lambda_d(6))
print(lambda_d(7))

运行结果:

100
50
10
6
8

可以看到,lambda的参数可以0个到多个,并且返回的表达式可以是一个复杂的表达式,只要最后的值是一个值就行了。

5.2.4 lambda作为一个参数传递
def sub_func(a, b, func):
    print('a =', a)
    print('b =', b)
    print('a - b =', func(a, b))
 
 
sub_func(100, 1, lambda a, b: a - b)

运行结果:

a = 100
b = 1
a - b = 99

上面的代码中,sub_func中需要传入一个函数,然后这个函数在sub_func里面执行,这时候我们就可以使用lambda函数,因为lambda就是一个函数对象。

5.2.5 lambda作为函数的返回值
def run_func(a, b):
    return lambda c: a + b + c
 
 
return_func = run_func(1, 10000)
print(return_func)
print(return_func(100))

运行结果:

<function run_func.<locals>.<lambda> at 0x00000254E4C94158>
10101

匿名函数可以作为一个函数的返回值,在上面的代码中,run_func返回的是一个匿名函数,返回的是一个函数对象,当我们执行这个函数时,可以得到lambda函数的结果。

六. 作业

本节作业:

1、一个列表由四个元组组成,每个元组都是四个数字组成,现在要求对这个列表排序,排序规则是按照每个元组的第二个元素大小排序
2、实现isPrime()函数,参数是整数,如果整数是质数,返回True,否则返回False
3、利用递归函数,实现一个阶乘函数,支持正数和负数的阶乘
4、定义一个函数,输入字符串,如果字符串是顺序的则返回‘UP’,如果是倒序的则返回‘DOWN’, 如果是乱序的则返回False

答案下节公布

上节答案:
1、找出两个列表中相同元素,并打印出来

li1 = [1,1,3,'b','a','c',9,9,8]
li2 = ['a','b','c','d',1,3,5,7,9]
li_one = set(li1)
li_two = set(li2)
li3 = list(li_one & li_two)
print(li3)

运行结果:

[1, 3, 'c', 9, 'a', 'b']

2、统计一串字符中,每个字母 a~z的出现次数,忽略大小写

st = input('请输入一段字符串:')
st1 = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
st2 = {}
for i in st1:
    if i in st:
        st2.update({i:st.count(i)})
##        print(f'{i}出现的次数为{st.count(i)}')
print(st2)
for key,value in st2.items():
    print(f'{key}出现的次数为:{value}')

运行结果:

请输入一段字符串:abcdeffgasgdddduekfe
{'a': 2, 'b': 1, 'c': 1, 'd': 5, 'e': 3, 'f': 3, 'g': 2, 'k': 1, 's': 1, 'u': 1}
a出现的次数为:2
b出现的次数为:1
c出现的次数为:1
d出现的次数为:5
e出现的次数为:3
f出现的次数为:3
g出现的次数为:2
k出现的次数为:1
s出现的次数为:1
u出现的次数为:1

3、利用26个字母和10个数字,随机生成10个8位密码

import random
a = 'abcdefghigklmnopqrstuvwxyz1234567890'
for i in range(10):
    b = random.choices(a,k=8)
    print(f'第{i+1}个8位密码为:',''.join(b))

运行结果:

18位密码为: aiw3xg4e
第28位密码为: 36xbyd6d
第38位密码为: i3y99h6n
第48位密码为: giaa8tfg
第58位密码为: qx53lowx
第68位密码为: l5tlz6kr
第78位密码为: 753cepvd
第88位密码为: u7yko1xm
第98位密码为: shib8qoo
第108位密码为: h2h8r5zg

4、判断用户输入的是不是一个手机号码,并且是不是以13开头的手机号

num = input('请输入您的手机号:')

if len(num) == 11 and num.isdigit() == True and num[:2] == '13':
    print('是13开头的手机号')
else:
    print('不是13开头的号')

运行结果:

请输入您的手机号:1383654238913开头的手机号
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值