一篇文章让你让你对python函数的掌握由基础到高级

python中函数由低级到高级

一 函数基础

1.1 函数简介 function

input print 内置函数——》可以直接使用

可复用性非常差

函数就是存代码的

总结函数的优点:
1.遇到重复功能的时候,直接调用即可,减少代码量
2.提升代码的结构性,分工明确,提高代码的可读性
3.遇到扩展功能的时候,修改比较方便

函数的本质就是一段有特定功能、可重复使用的代码,这段代码已经被提前编写好了,并且为其起了一个“好听”的名字,在后续编写程序中,如果需要同样的功能,直接通过起好的名字就可以调用这段代码

def my_len(s):
    leng = 0
    for i in s:
        leng+=1
    return leng

lengt = my_len('sahdjbaodbnao')
print(lengt)  # 13
lengt1 = my_len('xiaochuan')
print(lengt1) # 9

1.2 函数定义

定义函数:

def 函数名(形参1,形参2....形参n):
    代码块(函数体)
def fn():
    print('这是我的第一个函数')
# 函数中保存的代码不会立刻执行,需要我们用户自己调用函数,代码才会执行

1.3 函数调用

必须遵循先定义再调用

def fn():
    print('这是我的第一个函数')
print(fn) # fn是函数地址函数对象<function fn at 0x000002980AEB7430>
print(type(fn)) # <class 'function'>
调用函数语法:
函数名()

def fn():
    print('这是我的第一个函数')
fn()
fn()
fn()
fn()
fn()

这是我的第一个函数
这是我的第一个函数
这是我的第一个函数
这是我的第一个函数
这是我的第一个函数
def fn():
    print('吃了吗')
    print('睡了吗')
    print('你好')
    print('再见')
fn()
fn()
吃了吗
睡了吗
你好
再见
吃了吗
睡了吗
你好
再见
区分:
fn    函数名
fn()  函数调用

1.4 函数参数

1.4.1 形参和实参
def 函数名(): # 括号里面没东西就叫做无参函数
def 函数名(参数1,参数2...参数n): # 括号里面有东西,这个东西就叫做参数,可以有很多个
函数的参数:在定义函数时,可以在函数名后()中定义数量不等的形参,注意可以有,也可以没有,可以有一个也可以有多个,多个形参之间使用逗号隔开

形参(形式参数):定义形参就相当于在函数内部声明了变量,但是并不赋值,这个变量只是先形式的在函数里使用,但是它没有值

实参(实际参数):在函数定义的时候指定了形参,在调用的时候也必须传递实参,我们的实参会赋值给对应的形参,简单来讲就是有几个形参,我们必须传递几个实参
def fn(a,b):
    print('a=',a)
    print('b=',b)
fn(10,20) # 10,20就叫做实参

a= 10
b= 20
# 定义一个函数,可以用来求任意两个数的和
def sum1(a,b):
    print(a+b)
sum1(22,33)
sum1(1,1)
sum1(111,333)
总结:
在定义函数时那个参数叫形参
调用的时候叫实参
定义了多少个形参就要传递多少个实参
1.4.2 参数的传递方式

形参的传递方式

1.4.2.1 默认值参数
def fn(a,b,c=10):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(1,2,3)
a= 1
b= 2
c= 3


def fn(a,b,c=10):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(1,2)

a= 1
b= 2
c= 10

有些参数太常用或者说基本不会去改变它,我们就可以使用默认值参数

实参的传递方式

1.4.2.2 位置参数和关键字参数
1.位置参数
就是将对应位置的实参赋值给对应位置的形参
2.关键字参数
可以不按照形参定义的顺序来传递,而是直接根据参数名来传递参数
def fn(a,b,c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(b=1,c=3,a=2)
注意:混合使用关键字和位置参数的时候,位置参数必须写在关键字参数的前面
def fn(a,b,c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(1,2,c=20)

a= 1
b= 2
c= 20

实参可以传递任意类型

def fn2(a):
    print('a=',a)
# fn2(123)
a = 123
fn2(a)
1.4.3 可变参数

也叫作不定长参数

1.4.3.1 *args

*args可以获得我们所有的实参,将所有的实参都保存到一个元组中,实参传递几个值元组就会有几个元素,它会接收所有的参数

我们把散开的数据,装到一个元组或者是字典里面里面这就可以叫做是装包

好处就是你想传几个就传几个,不传都行

def fn(*a):
    print(a)  # ()
    print(type(a)) # <class 'tuple'>
fn()

def fn(*a):
    print(a)  # (1, 2, 3, 4, 5)
fn(1,2,3,4,5)
# 定义一个函数,求任意个数的和
def sum1(*args):
    result = 0
    for i in args:
        result+=i
    print(result)
sum1(1)  # 1
sum1(1,2)  # 3
sum1(1,2,3)  # 6
sum1(1,2,3,4,52,3,4,5,6) # 80

注意:
1.*号的形参只能有一个,因为写多了没办法分
2.*号的参数可以和其他参数配合使用,但是得写在最后面
def fn(a,b,*c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(1,2,3,4,5,6,7,8,9)

a= 1
b= 2
c= (3, 4, 5, 6, 7, 8, 9)

def fn(*a,b,c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(1,2,3,4,5,6,7,8,9,1,1,1,1,1,1,1,1,1,1)  # 报错,*a是无限长的,b和c读取不到值

# 使用下面的写法可以解决可变参数写在前面的情况
def fn(*a,b,c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
fn(1,2,3,4,5,6,7,8,9,b=44,c=55)

a= (1, 2, 3, 4, 5, 6, 7, 8, 9)
b= 44
c= 55
1.4.3.2 **kwargs

*形参有一个弊端就是只能接收位置参数

**形参可以接收任意关键字参数,会将这些参数统一保存到字典里面,字典的键就是参数的名字,字典的值就是参数的值

def fn(**args):
    print('args=',args)
    print(type(args))
fn(a=1,b=2,c=3)

args= {'a': 1, 'b': 2, 'c': 3}
<class 'dict'>

注意:**形参也是只能有一个,并且必须写在所有参数最后,它必须是最后一个

*args和**kwargs是可以同时使用的
要注意的是,同时使用*args和**kwargs的时候,*args必须写在**kwargs之前
def fn(a,b,*args,**kwargs):
    print('a=',a)
    print('b=',b)
    print('args=',args)
    print('kwargs=',kwargs)
fn(1,2,3,4,5,6,7,8,9,1,5,k=1,w=2,aa=3,rr=4)
1.4.3.3 参数解包

传递实参时,也可以在序列类型参数前面添加星号,这样他会自动将序列中的元素依次作为参数传递

注意:序列中的元素必须和我们函数形参的个数一致

def fn(a,b,c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
# t = (1,2,3)
# fn(*t)
fn(*[2,3,4])
# fn(*[2,3,4,5])  # 报错,元素个数必须和形参个数一致

a= 2
b= 3
c= 4
# 注意:字典的解包必须要满足字典的key和参数的名字对上
def fn(a,b,c):
    print('a=',a)
    print('b=',b)
    print('c=',c)
d = {'a':10,'b':20,'c':30}
fn(**d)

a= 10
b= 20
c= 30
总结
1.*args适用于接收多余的未命名的位置参数
**kwargs用于接收关键字参数
其中args是一个元组类型
kwargs是一个字典类型
2.*args是把元组中的数据进行解包,也就是把元组中的数据拆成单个的数据
**kwargs是把字典中的数据进行解包,也就是把字典中的数据拆成单个的键值对

二 函数返回值 return

函数执行以后返回的结果

获取返回值:把函数调用当做一个值来输出

return后面是什么,函数的返回结果就是什么

return后面可以是任意的值

返回值可以是任意类型包括函数

def fn():
    def fn2():
        print('hello')
    return fn2
r = fn()  # fn2
print(r) # <function fn.<locals>.fn2 at 0x00000287B1216670>
r() # hello
# 如果我们不给一个函数return,那么这个函数的返回值就是空的,就是None
def fn2():
    pass
print(fn2()) # None
# NoneType只有一个值就是None
print(type(None)) # <class 'NoneType'>
# 如果给一个函数写return但是return后面没有值那么这个函数的返回值也是None
def fn():
    return 
print(fn())  # None
在函数中,return代表函数执行结束,后面的代码不会再执行

总结

return 语句的作用:结束函数调用、返回值、指定返回值
printreturn的区别是什么:
print仅仅是打印在控制台,把结果打印出来,而return则是将return后面部分作为返回值:作为函数的输出,可以赋值给变量,继续用返回值做其他操作

注意:return语句在同一个函数中可以出现多次,但是只要有一个得到执行,就会直接结束函数的执行

三 作用域与命名空间

3.1 作用域

3.1.1 简介

作用域直白来说就是变量产生作用的区域

程序中就是:指的是变量生效的区域

def fn():
    x = 1  # x定义在函数fn的内部 所以x的作用域就是在函数内部,在函数外部无法访问

print(x) # 无法访问函数内部x


y = 2
def fn():
    x = 1  # x定义在函数fn的内部 所以x的作用域就是在函数内部,在函数外部无法访问
    print('函数内部y=',y)
fn()
print('函数外部y=',y)

函数内部y= 2
函数外部y= 2
3.1.2 全局作用域

全局作用域就是在全局都有效

全局作用域的生命周期:全局作用域在程序执行时创建,在程序执行结束时销毁

哪些位置属于全局作用域?所有函数以外的都是全局作用域
只要代码不是在函数里面都是全局作用域
在全局作用域中定义的变量,都属于是全局变量,全局变量可以在程序的任意位置被访问
# y就是全局变量
y = 2
def fn():
    x = 1  # x定义在函数fn的内部 所以x的作用域就是在函数内部,在函数外部无法访问
    print('函数内部y=',y)
fn()
print('函数外部y=',y)

函数内部y= 2
函数外部y= 2
3.1.3 局部(函数)作用域
函数作用域的生命周期:就是在函数调用时创建,在调用结束时销毁
函数每次调用的时候都会产生一个新的函数作用域
在函数作用域中定义的变量只能在函数内部访问,或者说在函数作用域中定义的变量都可以叫做局部变量
def fn():
    a = 10
    print('a=',a)
fn()
print('a=',a) # 报错,访问不到

可以从小的往大的看,但是不能从大的往小的看

def fn1():
    a = 30
    def fn2():
        print(a)  # 30 可以从里面往外面看
    fn2()
fn1()

def fn1():
    def fn2():
        a = 10
    print(a)  # 报错 不能从外面往里面看
fn1()
def fn1():
    a = 20
    def fn2():
        a = 10
        print(a) # 10
    fn2()
fn1()
使用变量的时候,会优先在当前作用域中寻找该变量,如果有就是用,没有就继续往上级作用域中寻找

global 关键字——》如果希望在函数内部修改全局变量,可以使用global关键字来声明变量

a = 1
def fn():
    a = 2
    print('内部a=',a) # 内部a= 2
fn()
print('外部a=',a) # 外部a= 1

a = 1
def fn():
    global a  # 把变量a声明为全局变量
    a = 2
    print('内部a=',a) # 内部a= 2
fn()
print('外部a=',a)  # 外部a= 2
总结:
1.基本就是 里面能看到外面,但是外面看不到里面
2.如果局部修改全局变量可以加上一个global关键字

3.2 命名空间

就是变量存储的位置

每一个作用域都会有一个对应的命名空间

全局作用域有全局的命名空间

局部作用域有局部的命名空间

命名空间实际上就是一个字典,是一个专门存储变量的字典

locals():获取当前作用域的命名空间,返回一个字典

a = 1
def fn():
    b = 1
    print(locals())  # 函数作用域中的命名空间{'b': 1}
fn()

a = 1
def fn():
    global b  # 把变量b声明给全局了
    b = 1
    print(locals())  # {}
fn()
global  全局 把函数体内部变量声明给全局
a = 1
def fn():
    a = 2
    print('内部a=',a) # 内部a= 2
fn()
print('外部a=',a) # 外部a= 1

a = 1
def fn():
    global a  # 把变量a声明为全局变量
    a = 2
    print('内部a=',a) # 内部a= 2
fn()
print('外部a=',a)  # 外部a= 2

nonlocal  局部 只能在嵌套函数中使用
用来在函数或者其他作用域中使用外层(非全局)变量
def f1():
    a = 1
    def f2():
        a =2
        print('f2中的a=',a) # f2中的a= 2
    f2()
    print('f1中的a=',a)  # f1中的a= 1
f1()

def f1():
    a = 1
    def f2():
        nonlocal a
        a =2
        print('f2中的a=',a)  # f2中的a= 2
    f2()
    print('f1中的a=',a) # f1中的a= 2
f1()
全局 = 大方的人——》我的东西你们都可以用
局部 = 小气的人——》我的东西只有我能用

四 函数进阶

4.1 高级用法

只是让大家知道可以这么去使用,但是不常用

1.函数可以被引用(可以赋值)

def fn():
    print('我是fn')
f = fn
print(f,fn) #<function fn at 0x000001ACA2557430> <function fn at 0x000001ACA2557430>
fn() # 我是fn
f() # 我是fn

2.函数可以作为参数传入另一个函数

def fn():
    print('我是fn')
def fn2(x):
    print(x) # <function fn at 0x000001CB97FF7430>
    x()  # 我是fn
fn2(fn)  

3.可以将函数作为返回值

def fn():
    print('我是fn')
def fn2(x):
    # print(x)
    return x
x = fn2(fn)   # fn2(fn)  ——》fn
x()  # 我是fn

4.函数可以作为容器的元素

def fn():
    print('我是fn')
li1 = [1,2,3,fn]
f = li1[3]
f()  # fn()

4.2 匿名函数 lambda

语法:
lambda 参数列表:运算表达式
def fn(x):
    return x*x


# print(fn(5))  # 25
f = lambda x:x*x
print(f(5))  # 25
总结注意事项:
1.lambda并不会带来程序运行效率的提高,只会使代码更简洁
2.如果使用lambdalambda内不要有循环,因为可读性不好,有的话请使用标准函数完成,目的是为了代码有可重用性和可读性
3.lambda只是为了减少单行函数的定义而存在,如果一个函数,只有一个返回值,只有一句代码,就可以使用lambda

4.3 高阶函数

4.3.1 高阶内置函数简介
所谓的高阶函数就是把函数作为参数传入,就是把一个函数作为另一个函数的参数传入
abs() ——》求绝对值
print(abs(-1))  # 1
sum() ——》求和
print(sum([1, 1, 2, 3, 4])) # 11
round() ——》求四舍五入
print(round(4.3)) # 4
任意两个数字,对2个数字求绝对值后进行求和
def ab_sum(a,b,f):
    return f(a)+f(b)
res = ab_sum(-1,4,abs)
print(res)

求相反数的和
def xfs(x):
    return -x
def ab_sum(a,b,f):
    return f(a)+f(b)
res = ab_sum(1,-4,xfs)
print(res)
4.3.2 map()
map(func,seq)第一个参数是给一个函数,第二个参数是给一个序列类型(要改变的序列)
map的作用就是改变序列中所有的元素,改变规则由我们传入的函数决定
li1 = [-1,2,-3,4,-5]
print(list(map(abs,li1))) # [1, 2, 3, 4, 5]
将列表中序列的各个元素加一
li1 = [1,2,3,4,5]
# 普通做法
li2 = []
for i in li1:
    li2.append(i+1)
print(li2) # [2, 3, 4, 5, 6]
# map()做法
def add1(x): # x就是列表中的每个元素
    return x+1
print(list(map(add1,li1)))
# lambda做法
print(list(map(lambda x:x+1,li1)))
4.3.3 filter()
filter(func,seq)第一个参数是给一个函数,第二个参数是给一个序列类型(要改变的序列)
filter用于过滤序列,过滤掉不符合条件的元素
需求:保留一个序列中所有的偶数
li1 = [1,2,3,4,5,6,7,8,9,10]
# 普通做法
li1 = [1,2,3,4,5,6,7,8,9,10]
for i in li1:
    if i %2 !=0: # i是奇数
        li1.remove(i)
print(li1) # 所有的偶数
# filter用法
li1 = [1,2,3,4,5,6,7,8,9,10]
def os1(x):
    return x%2==0
print(list(filter(os1,li1)))
# lambda用法
print(list(filter(lambda x:x%2==0,li1)))
4.3.4 sorted()
语法:sorted(seq,key=函数) # 第一个是给一个序列类型,第二个是参数key=函数  函数是自己所定义的规则排序
返回一个排序后的序列
li1 = [2,1,4,3,5,6,9,7]
print(sorted(li1))  # 升序
print(sorted(li1,reverse=True)) # 降序
需求:
li1 = ["小零:69","小违:79","小力:89","小荣:100","小川:59"]
def f(x):
    arr = x.split(":")
    return int(arr[1])
print(sorted(li1,key=f)) # ['小川:59', '小零:69', '小违:79', '小力:89', '小荣:100']

# x——》x是列表中每一个元素,每一个字符串"小零:69"
# x.split(":") #["小零","69"],["小违","79"]
# arr[1] 根据下标获取第二个值也就是每个元素的分数 69,79,89,但是他们都还是字符串类型
# int(arr[1])将字符串类型转成int才可以进行排序计算

max(seq,key=函数):根据函数可以获取序列类型的最大值
min(seq,key=函数):根据函数可以获取序列类型的最小值
def f(x):
    return int(x.split(":")[1])
print(max(li1,key=f)) # 小荣:100
print(min(li1,key=f)) # 小川:59

4.4 递归函数

如果一个函数的内部调用了自己,那么这个函数就叫做递归函数
1.如果定义递归函数,不想让它报错的话,必须要有出口
2.设计一个出口,不断的向出口接近
# 打印1-500
def func(x):
    if x == 501:
        return 
    print(x)
    return func(x+1)
func(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值