python之函数总结
为什么使用函数
问题:代码重复
后期维护成本太高
代码可读性不高
解决问题:函数
在一个完整的项目中,某些功能会被反复使用,那么将这部分功能对应的代码提取出来,当需要使用功能的时候直接使用
本质:对一些特殊功能的封装
优点:
a.简化代码结构,提高应用的效率
b.提高代码复用性
c.提高代码的可读性和可维护性
建议:但凡涉及到功能,都尽量使用函数实现
定义函数
语法:
def函数名(参数1,参数2,参数3.…)
函数体
返回值(可以有也可以没有,根据具体的场景)
说明:
a.函数由两部分组成:声明部分和实现部分
b.def,关键字,是define的缩写,表示定义的意思
c.函数名:类似于变量名,遵循标识符的命名规则,做到见名知意
d.():表示的参数列表的开始和结束
e.参数1,参数2,参数3…:参数列表[形式参数,简称为形参],其实本质上就是一个变量
名,参数列表可以为空
f.函数体:封装的功能的代码
g.返回值:一般用于结束函数,可有可无,如果有返回值,则表示将相关的信息携带出去,携带给调用者,如果没有返回值,则相当于返回None#test() name'test'isnotdefined #函数调用不能在函数定义的上面 def test():print("平安!")
函数调用
test()#函数调用
test()
test()
函数调用的格式:函数名()
1.函数是条狗,哪里需要哪里吼。函数定义以后不会自动执行,若想执行函数中的函数体,需要调用函数
2.函数必须先定义,后调用。调用函数必须在定义函数的下边。
3.函数可以多次调用。
4.在同一个文件中,出现了重名的函数,后面的函数会把前面的函数覆盖。调用的时候执行后面的函数。
函数的分类
1.按照函数是否是自己定义分为:
内置函数:比如:print()、int()、str()、upper()、lower()
python语言内部已经定义好的,可以直接使用的
自定义函数:根据自己的需求定义的函数
2.根据函数中有没有参数分为:
有参数的函数:在定义函数时传入的参数叫做形式参数(形参)在调用函数时传入的参数叫做实际参数(实参)
没有参数的函数:
3.根据定义的函数有没有函数名分为:
有名字的函数:def函数名():
函数体
匿名函数:通过lambda定义
4.根据函数中是否有返回值分为:
有返回值的函数:使用return返回数据
没有返回值的函数:
无参数的函数
def test():print("年少青春!")
有参数的函数
# 有参数的函数: def my_sum(a, b): # a,b就是形参print(a + b)my_sum(12, 23) # 12,23就是实参 35
没有返回值的函数
# 没有返回值的函数 def say():print("我是没有返回值的函数")say()
有返回值的函数
# 有返回值的函数:通过return关键字返回数据 def demo():return "我是有返回值的函数"str = demo() print(str)
''' 1.return后面可以返回一个或者多个数据,返回多个数据,以元组的形式展示出来 2.return下面的代码不会执行 '''def demo1():print("哈你妹!")return 12, 34, 578print("haha") # 不会执行tuple1 = demo1() print(tuple1) # (12,34,578)# 函数中没有return或者return后面没有数据返回,则默认返回的是None def demo2():returnprint(demo2()) # None
注意:
1.有返回值的函数,使个变量接收返回的数据.用return关键字将数据返回,若想使用返回的数据,需要在调用函数的位置定义一个变量接收返回的数据
2.return返回的数据可以是一个或者多个,返回的多个数据是以元组的形式展示
3.return关键词下面的代码不会执行
4.在函数中若不写return或者return后面没有返回具体的数据,则返回的是None
函数嵌套函数
函数之间可以进行相互嵌套
def test():test1()print(11111)def test1():test2()print(22222)def test2():test3()print(333333)def test3():print(44444)test()
函数中的参数
参数列表:如果函数所实现的功能涉及到未知项参与运算,此时就可以将未知项设置为参数
格式:参数1,参数2…
分类:
形式参数:在函数的声明部分,本质就是一个变量,用于接收实际参数的值【形参】
实际参数:在函数调用部分,实际参与运算的值,用于给形式参数赋值【实参】
传参:实际参数给形式参数赋值的过程,形式参数=实际参数
def get_max(num1, num2): # num1,num2是形参if num1 > num2:return num1else:return num2max_value = get_max(12, 345) # 12,345是实参
print(max_value)
形参和实参之间的关系:
1.一般请况下,形参的数量和实参的数量保持一致
2.在函数中,形参的名字和实参的名字一样不会产生任何影响
参数的类型
# 必需参数
def student(name, age):print("我的姓名是%s,年龄是%d" % (name, age))student("py小王子", 21)
student(21, "py小王子") # TypeError: %d format: a real number is required, not str
def student(name, age):print("我的姓名是%s,年龄是%d" % (name, age))student(age=21, name="py小王子")
def get_sum(num1, num2=12):print(num1 + num2)get_sum(23) # 35
get_sum(23, 87) # 110
注意:如果函数中有多个形参 , 给该函数设置默认参数时 , 一般把这个默认参数放在形参列表的最后面
d.不定长参数(可变参数)
可以处理比当初声明时候更多的参数元组、字典
args: 用来接收多个位置参数 argments 得到的形式是 元组
kwargs:用来接收多个关键字参数 keyword arguments 得到的形式是 字典
# 不定长参数:*args **kwargs
def fn(*args):print(args) # (34, 533, 4, 3)fn(34, 533, 4, 3)
# 注意:
"""
在自定义函数时,若函数中有多个参数,某一个参数是不定长参数,一般把不定长参数放在参数列表的最后面。
110"""def fn1(name, *args):print(name, args) # py小王子 (12, 345, 6, 'hehe', True)fn1("py小王子", 12, 345, 6, "hehe", True)# kwargs接收的是关键字参数,格式是key=value这种形式
def fn2(**kwargs):print(kwargs) # {'x': 10, 'y': 12, 'z': 88}fn2(x=10, y=12, z=88)
函数使用练习:
1.封装函数,计算1-100之间所有的数字的和
# 计算1-100之间所有的数字的和 def total():sum = 0for i in range(101):sum = sum + ireturn sumif __name__ == '__main__':print(f'1-100的和为:{total()}')
2.封装函数,将1-100之间个位数为3的数字输出
# 将1-100之间个位数为3的数字输出 def three():list = []for i in range(101):if i == 3:list.append(i)elif i % 10 == 3:list.append(i)return listif __name__ == '__main__':print(f'1-100之间个位数为3的有{three()}')
匿名函数(lqmbda)
不再使用def这种的形式定义函数,使用lambda来创健匿名函数
特点:
a.lambda只是一个表达式,比普通函数简单
b.lambda一般情况下只会书写一行,包含参数,实现体,返回值
语法:lambda参数列表:实现部分
# 匿名函数:lambda
def fn(n):return n ** 2print(fn(3)) # 9# 匿名函数
f1 = lambda n: n ** 2
print(f1(3)) # 9# 匿名函数
f2 = lambda x, y: x * y
print(f2(12, 3)) # 36# 有名字的函数的写法
def ji(x, y):return x * yprint(ji(12, 3)) # 36
回调函数
def fn(a, b):print(a + b)fn(12, 34) # 46test = fn # 将函数fn赋值给一个变量test,那这个变量test能够实现和函数fn一样的功能
print(type(test), type(fn)) # <class 'function'><class function'>
test(12, 34) # 46# 函数名:f既是函数的名称,同时也指向了该函数的对象(变量)
# 函数调用的格式:函数名()====>变量名)
# 回调函数:把一个函数(a)作为一个参数传递到另外一个函数(b)中去,那么函数就叫做回调函数。
def add(x, y):print(x + y)def cha(x, y):print(x - y)def ji(x, y):print(x * y)def shang(x, y):print(x / y)
add(56, 23)
cha(78, 21)# 封装一个函数,实现加减乘除运算。
def demo(x, y, func):func(x, y)
demo(56, 23, add)
# 此时add函数就是一个回调函数
demo(78, 12, cha)
闭包
如果在一个函数的内部定义另外一个函数,外部的函数叫做外函数,内部的函数叫做内函数如果在一个外部函数中定义一个内部函数,并且外部函数的返回值是内部函数,就构成了一个闭包,则这个内部函数就被称为闭包【closure 】实现函数闭包的条件 :1. 必须是函数嵌套函数2. 内部函数必须引用一个定义在闭合范围内的外部函数的变量 ,---- 内部函数引用外部变量3. 外部函数必须返回内部的函数
# 最简单的闭包
# 外部函数
def outer():# 内部函数def inner():print("lala")return inner # 将内部函数返回
fn = outer()
# fn=====>inner函数
fn()# 相当于调用了inneri函数输出lala# 内部函数使用外部函数的变量
def outerl(b):a = 10def innerl():# 内部函数可以使用外部函数的变量print(a+b)return innerl
fun1=outerl(12)
fun1()#22
注意:1.当闭包执行完毕后,仍然能够保存住当前的运行环境2.闭包可以根据外部作用域的局部变量得到不同的效果,类似于配置功能,类似于我们可以通过修改外部变量,闭包根据变量的改变实现不同的功能.应用场景:装饰器
装饰器
def test():print("你好啊!")test()# 需求:给上面的函数test增加一个功能,输出我很好# 第一种方式:修改了原来的函数
def test():print("你好啊!")print("我很好")test()# 第二种方式:定义一个新函数,在新函数中调用原函数,然后追加功能
def test1():test()print("我很好")
test1()
在代码运行期间,可以动态增加函数功能的方式,被称为装饰器
[Decorator]通过闭包函数实现。也就是说,在不修改原函数的基础上,给原函数增加功能
好处:在团队开发中,如果两个或者两个以上的程序员会用到相同的功能,但是功能又有细微的差别,采用装饰器:相互不影响,代码简化
使用装饰器
装饰器是Python中用一个函数装饰另外一个函数或类并为其提供额外功能的语法现象。装饰器本身是一个函数,它的参数是被装饰的函数或类,它的返回值是一个带有装饰功能的函数。很显然,装饰器是一个高阶函数,它的参数和返回值都是函数。
简单装饰器
#原函数
def test():print("你好啊!")#需求:给上面的函数test增加一个功能,输出我很好
#第三种方式:通过装饰器的方式给函数追加功能
"""
装饰器使用闭包实现
闭包函数:
1.函数嵌套函数
2.内部函数使用外部函数的变量
3.外部函数中返回内部函数
"""
#a.书写闭包函数 此处的outer函数就是装饰器函数
def outer(fn):#b.fn表示形参,实际调用的时候传递的是原函数的名字def inner():fn() # 先调用原函数print("我很好") # 后打印“我很好”return innerprint("添加装饰器之前:",test,__name__)#<function test at 0x00000273421BA2A0>
test = outer(test)
print("添加装饰器之后:",test,__name__)#<function outer.<locals>.inner at 0x00000273423058A0>
test()
系统的简写
# a.书写闭包函数
# 此处的outer函数就是装饰器函数
def outer(fn): # fn表示形参,实际调用的时候传递的是原函数的名字def inner():fn() # c.调用原函数print("我很好") # d.给原函数添加功能,注意:添加的功能可以写在原函数的上面也可以写在原函数的下面return inner# 装饰器的简写方式@+装饰器名称
@outer
# 等价于=====>test = outer(test)
def test():print("你好啊!")test()
注意:
1.在使用装饰器的简写方式的时候,原函数必须在装饰器函数的下面
2.outeri就是装饰器函数。@outer等价于test outer(test)
不定长参数的装饰器(通用装饰器)
#同一个装饰器装饰多个函数
def jisuan(fn):def inner(*args):print("数学运算的结果是:", end="")fn(*args)return inner@jisuan
def add(a, b):print(a + b)add(12, 34)@jisuan
def cha(a, b, c):print(a - b - c)cha(100, 23, 26)
1. 装饰器
jisuan
的原理
- 闭包实现:
jisuan
函数是一个装饰器,它利用了闭包的特性。在jisuan
函数内部定义了inner
函数,inner
函数使用了外部函数jisuan
的变量fn
(这里fn
是被装饰的函数)。- 功能实现:
inner
函数在执行被装饰函数fn
之前,会先打印"数学运算的结果是:"
,并且通过end=""
设置不换行。这样,当使用这个装饰器装饰其他函数时,这些函数在执行计算并输出结果之前都会先输出这个提示。2. 被装饰函数
add
和cha
的执行过程
add
函数:
- 当定义
add
函数并使用@jisuan
装饰时,add
函数被传递给jisuan
函数,jisuan
函数返回inner
函数,此时add
实际上指向了inner
函数。- 当执行
add(12, 34)
时,实际上是执行inner(12, 34)
。首先会打印"数学运算的结果是:"
,然后执行fn(*args)
,这里fn
就是原来的add
函数,会计算并打印12 + 34
的结果,即46
。cha
函数:
- 同理,
cha
函数被jisuan
装饰后,当执行cha(100, 23, 26)
时,先执行inner(100, 23, 26)
,会先打印"数学运算的结果是:"
,然后执行原来cha
函数的功能,计算并打印100 - 23 - 26
的结果,即51
带返回值的装饰器
def outer(fn):def inner():print("我的爱好是:", end="")return fn()return inner@outer
def swim():return "i like swimming!"love = swim()
print(love)
- 首先定义了装饰器
outer
,它的内部函数inner
在调用被装饰的函数fn
之前会先打印"我的爱好是:"
。- 然后
swim
函数被outer
装饰,swim
函数返回"i like swimming!"
。- 当执行
love = swim()
时,实际上是先执行inner
函数(因为swim
被outer
装饰),inner
函数先打印"我的爱好是:"
,然后调用swim
函数,swim
函数返回"i like swimming!"
,这个结果被赋值给love
,最后print(love)
输出我的爱好是:i like swimming!
多个装饰器作用同一个函数
- 当多个装饰器修饰一个函数的时候,装饰器从上往下依次执行,并且原函数只执行一次(这里是先
outer2
,再outer1
)。- 当调用
show()
时,首先执行outer2
装饰器中的inner
函数,打印-2222
,然后执行outer1
装饰器中的inner
函数,打印-1111
,最后执行原函数show
,打印今晚我的好朋友从广州过来了,好开心!。。..
。
#多个装饰器作用于一个函数
def outer1(fn):def inner():print("-1111")fn()return innerdef outer2(fn):def inner():print("-2222")fn()return inner#原函数
@outer2
@outer1
def show():print("今晚我的好朋友从广州过来了,好开心!。。..")show()
用模块管理函数
不管用什么样的编程语言来写代码,给变量、函数起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。最简单的场景就是在同一个.py文件中定义了两个同名的函数
def foo():print('hello, world!')def foo():print('goodbye, world!')foo() # goodbye, world!
如果项目是团队协作多人开发的时候,团队中可能有多个程序员都定义了名为foo的函数,这种情况下怎么解决命名冲突呢?答案其实很简单,Python中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过import关键字导入指定的模块再使用完全限定名的调用方式就可以区分到底要使用的是哪个模块中的foo函数。
module1.py
def foo():print('hello, world!')
module2.py
def foo():print('goodbye, world!')
test.py
方法1、模块名.函数名
import module1 import module2# 用“模块名.函数名”的方式(完全限定名)调用函数, module1.foo() # hello, world! module2.foo() # goodbye, world!
方法2、使用as关键字对模块进行别名
import module1 as m1 import module2 as m2m1.foo() # hello, world! m2.foo() # goodbye, world!
方法3、用from...import..语法从模块中直接导入需要使用的函数
from module1 import foofoo() # hello, world!from module2 import foofoo() # goodbye, world!
方法4、如果想在上面的代码中同时使用来自两个模块中的foo函数也是有办法的,还是用as关键字对导入的函数进行别名
from module1 import foo as f1 from module2 import foo as f2f1() # hello, world! f2() # goodbye, world!
标准库中的模块和函数
经典小案例
案例1:设计一个生成验证码的函数(说明:验证码由数字和英文大小写字母构成,长度可以用参数指定。)
# 案例1:设计一个生成验证码的函数(说明:验证码由数字和英文大小写字母构成,长度可以用参数指定。) import random import stringdef captcha(n):all_characters = string.ascii_letters + string.digits # 包含所有大小写字母和数字的字符串captcha_code = ''.join(random.choice(all_characters) for _ in range(n))return captcha_codeif __name__ == '__main__':n = int(input('请输入验证码长度:'))print(f"验证码为:{captcha(n)}")
案例2:设计一个函数返回给定文件的后缀名。
说明:文件名通常是一个字符串,而文件的后缀名指的是文件名中最后一个`.`后面的部分,也称为文件的扩展名,它是某些操作系统用来标记文件类型的一种机制,例如在Windows系统上,后缀名`exe`表示这是一个可执行程序,而后缀名`txt`表示这是一个纯文本文件。需要注意的是,在Linux和macOS系统上,文件名可以以`.`开头,表示这是一个隐藏文件,像.gitignore这样的文件名,`.`后面并不是后缀名,这个文件没有后缀名或者说后缀名为`''
def get_suffix(filename, ignore_dot=True):"""获取文件名的后缀名:param filename: 文件名:param ignore_dot: 是否忽略后缀名前面的点:return: 文件的后缀名"""# 从字符串中逆向查找.出现的位置pos = filename.rfind('.')# 通过切片操作从文件名中取出后缀名if pos <= 0:return ''return filename[pos + 1:] if ignore_dot else filename[pos:]print(get_suffix('readme.txt')) # txt print(get_suffix('readme.txt.md')) # md print(get_suffix('.readme')) # print(get_suffix('readme.')) # print(get_suffix('readme')) #
案例3:写一个判断给定的正整数是不是质数的函数。
def is_prime(num: int) -> bool:"""判断一个正整数是不是质数:param num: 正整数:return: 如果是质数返回True,否则返回False"""for i in range(2, int(num ** 0.5) + 1):if num % i == 0:return Falsereturn num != 1if __name__ == '__main__':num = int(input("请输入数字:"))if is_prime(num):print(f"{num}为质数")else:print(f"{num}不为质数")
案例4:写出计算两个正整数最大公约数和最小公倍数的函数
方法1、
def gcd_and_lcm(x: int, y: int) -> int:"""求最大公约数和最小公倍数"""a, b = x, ywhile b % a != 0:a, b = b % a, areturn a, x * y // a
方法2、
def gcd(x: int, y: int) -> int:"""求最大公约数"""while y % x != 0:x, y = y % x, xreturn xdef lcm(x: int, y: int) -> int:"""求最小公倍数"""return x * y // gcd(x, y)
案例5:写出计算一组样本数据描述性统计信息的函数。
import mathdef ptp(data):"""求极差"""return max(data) - min(data)def average(data):"""求均值"""return sum(data) / len(data)def variance(data):"""求方差"""x_bar = average(data)temp = [(num - x_bar) ** 2 for num in data]return sum(temp) / (len(temp) - 1)def standard_deviation(data):"""求标准差"""return math.sqrt(variance(data))def median(data):"""找中位数"""temp, size = sorted(data), len(data)if size % 2 != 0:return temp[size // 2]else:return average(temp[size // 2 - 1:size // 2 + 1])
总结
“代码有很多种坏味道,重复是最坏的一种!”
恭喜你学会了函数,快去试试吧!!!