Python基础之函数详解

一、函数的定义

到现在为止,我们已经掌握了Python的基本语法和数据类型等相关基础知识了,以进行一个项目的编写了,这个时候,就会发现,很多代码需要我们进行复制粘贴,这简直就是损害我们小文艺的脸面啊,这个时候,我们就需要向函数求助了。

函数的三大优点:

代码不冗余

可阅读性高

可维护性,可拓展性得到提升。

函数定义会发生什么呢?

先向内存申请一块空间将函数体的内容存放进去

将函数体的内容和函数名绑定一起

程序在执行到定义函数的阶段,只识别语法,不会执行函数体的内容

那么函数的语法是什么呢?

def 函数名(有无参数都可以):

"""描述性信息"""

代码块

return 值。

def:这是定义函数的关键字

函数名:遵循变量名的定义,最好对函数有一个功能性介绍(动词为主)

括号:内部可以选填参数

“”“描述性信息”“”:可有可无,但是推荐使用,对函数体的功能进行描述

return:可有可无。给出返回值

二、函数的调用

使用函数要遵循一个原则:这个原则就表明了它的内部的一些现象:大家一定要记牢:先定义后使用。是不是很熟悉,没毛病,这就是变量的使用原则。

函数名实际上跟“变量名”有些相似,都是储存一个内存地址,当我们调用函数的时候,最最基本的方式就是函数名加括号的方式。

def func():

pass

# 最最基本的调用方式。

func()

def func(a,b):

pass

# 函数体有参数的方式

func(1,2)

三、函数返回值

这时候就不得不讲一下return的作用了,如果我们把函数当做一个工厂,那么参数就是我们送到工厂的原材料,而工厂生产出来的东西就是我们return的值了。他可以是任意数据类型,当函数运行结束的时候,我们可以对其进行赋值,就可以得到我们的产品了。

# 当函数没有return 值的时候,默认返回值为None

def func():

pass

v= func()

print(v) # None

# 当只有一个return,但是并没有写返回值的时候,默认返回值也是None

def func():

return

v= func()

print(v) # None

# 当函数有返回值的时候,对其调用进行赋值会得到返回值。

# 返回值可以是单个,也可以是多个

def num_sum(a,b):

return a+b

sums = num_sum(1,2)

print(sums) # 3

return 的作用不止如此,他也是函数终结者。当函数体中运行到return的时候,就会终止函数的运行,不会执行return之下的代码。

当然,我们还可以将函数的返回值当做参数传给另外一个函数,因为返回值的本质就是一个变量值。

四、函数的参数

当我们在定义有参函数的时候,括号内部的参数相当于变量名,而在函数调用的时候,传进的值将内存地址绑定给定义阶段的参数。

在定义阶段,函数括号内的参数称为形参。

在调用阶段,函数括号内的参数称为实参。

参数可以有好几种形式表现,比如单个数据类型、key=value、函数等,但是其核心概念就是传入的是个值。

4.1 位置参数

位置形参函数在定义的时候是按照从左到右依次定义的参数。特点是必须被传值,不能多也不能少。

位置实参:函数在调用的时候是按照从左到右依次定义的参数。特点是按顺序与形参一一对应。

# 特点:必须被传值,多少给参数都不行。

def func(x,y): # 括号内就是位置形参

print(x,y)

func(1,2,3) # 过多的位置实参被传

func(1,) # 太少的位置实参

func(1,2) # 完美

4.2 关键字参数

我们还可以利用key=value的形式进行传参,调用阶段的参数是关键字参数。关键字可以指定形参进行传值,可以不参照顺序。

# 关键字参数:按照key=value的形式传入当做参数。

def func(x,y=2): # 默认参数

print(x,y)

# func(y=2,x=1) # 关键字实参

# func(1,2)

默认参数在定义阶段已经被赋值了,所以在调用阶段可以不必为该参数传值,当然,如果要是进行传值的话,会以传的值为准。

实参:位置实参和关键字参数的混合使用

混合使用的话,一定要注意一下两点:

位置实参必须放在关键字实参之前。

不能为同一个形参重复传参。

形参:位置形参和默认参数的混合使用

形参中的混合使用需要注意:

位置形参必须在默认参数之前。

默认参数是在定义阶段就被传入的值的内存地址

建议默认值不推荐使用可变数据类型

4.3 可变长度参数

4.3.1 可变长度形参

如果我们在调用函数的时候,如果传入的实参过多,就回出现错误,但是我们可以使用(*+形参名)或者(**形参名)来接受过多的实参。

原则上形参名可以是任意名,但是约定俗成通常为(*args)和(**kwargs)。

*args。用来接受位置实参。多出来的实参会被保存成元祖。

**kwargs。用来接受关键字参数。多出来的实参会被保存成字典。

当然,在函数内部使用的时候,是不带星号的。

在定义的时候请务必放到默认参数之后。*args在**kwargs之前。

# *args的使用

def foo(x,y,*args):

print(x,y,args)

foo(1,2,3,4,5,6) # 1 2 (3,4,5,6)

# **kwargs的使用

def foo(x,y,**kwargs):

print(x,y,kwargs)

foo(1,2,a=3,c=4) # 1 2 {"a":3,"c":4}

4.3.2 星号在实参上的应用

多出来的实参有*args和**kwargs接受,那么如果在调用的时候就给实参加上星号会有什么结果呢?

如果我们给实参前加上星号,本质是将该实参进行循环,然后得出的值当做实参传入形参。当然*args后可以跟多个可循环的数据类型,如果是字典只传入key。**kwargs只能使用字典,代表的是将key=value的格式传入。

# 在字典上的应用。

def func(x,y,z):

print(x,y,z)

func(*{'x':1,'y':2,'z':3}) # func('x','y','z')

func(**{'x':1,'y':2,'z':3}) # func(x=1,y=2,z=3)

当然,传入的实参数量要和形参数量相同,不然会出现错误。

4.3.3 星号的综合应用

def index(x,y,z):

print(x,y,z)

def foo(*args,**kwargs): # (1,2)传给args,{"z":3}传给kwargs

# *args将(1,2)转成1,2。**kwargs将{"z":3}转成z=3,然后传入index函数做参数

index(*args,**kwargs)

foo(1,2,z=3)

当函数定义的参数为*args,**kwargs时,代表函数可以接受任意符合格式的实参,上述的函数调用foo的本质是调用index函数。

4.4 命名关键字参数

这个参数使用的比较少,在定义形参的阶段,如果某些参数出现在星号之后,那么这些参数就被称为命名关键字参数。

# 命名关键字参数

def func(x,y,*,a,b): # a,b就是命名关键字参数

print(x,y,a,b)

def foo(x,y,*,b=1,a): # 命名关键字参数是不用按key=value在单个值之后的顺序

print(x,y,a)

注意,由于命名关键字参数位于星号之后,所以如果不用关键字实参的话,其值是传不进去的,因此必须的用指名道姓的方法传参数,而且调用的时候还必须被传值。

4.5形参和实参的顺序

形参的顺序:位置形参、默认参数、*args、命名关键字参数、**Kwargs。

实参的顺序:位置实参、*args、关键字参数、**kwargs。

五、函数闭包函数

在了解闭包函数之前,我们是需要一些储备知识的。然后,你不知不觉就一边蒙逼一边明白什么是闭包函数了。

5.1 函数的应用。

# 1、函数可以赋值,因为函数名本身引用着函数体的内存地址。

def foo():

pass

f = foo

# 2、可以把函数当做参数传递给另外一个函数。

def func():

print()

def foo(x):

print(x)

foo(func)

# 3、可以当作另一个函数的返回值。

def f1():

return foo

# 4、可以当作容器类型的一个元素。

def foo():

pass

l = [foo,1,a]

其实看着有这么多应用,但是大家发现没有,函数名其实就是一个变量名,凡是变量名能做到的它都能做到。这点很重要哦。

5.2 函数的嵌套

函数的嵌套共有两种。

# 1、函数的嵌套调用。即在调用一个函数的时候,在其内部又调用另外一个函数。

def foo():

pass

def func():

foo()

# 2、函数的嵌套定义。即在定义一个函数的时候,在其内部又定义另外一个函数。

def func():

def foo():

print(1)

5.3 闭包函数

首先上面都是闭包函数的铺垫。

明确一个概念:闭包函数= 名称空间与作用域+函数嵌套+函数对象。

闭:该函数是内嵌函数。这字说明了该函数所处的位置,肯定是嵌套函数中的定义函数。

包:指该函数包含对外层函数作用域名字的引用。注意,自己本身不含有,只有外部函数有。

是不是看不懂,接下来看个例子、

# 闭函数:名称空间与作用域+函数嵌套

def outer():

x = 1111

def inter():

print(x)

inter()

x = 22222

outer()

当我们在外界去调用inter函数的时候,由于名称空间与作用域的查找关系(按照定义的时候的查找顺序)的原因,不管我们在外界定义的是否含有x ,他都会使用outer内定义的变量。

# 闭包函数:名称空间与作用域+函数嵌套+函数对象

def outer():

x = 1111

def inter():

print(x)

return inter

f1 = outer()

f1()

注意,是把函数名当做变量来用,return后返回的inter是一个函数的内存地址,如果加上括号就是执行函数功能的意思。所以一定不能加括号。在运行Outer的时候,返回一个内部函数名,然后复制给一个全局变量,然后该全局变量就被绑定了inter函数的内存地址,当该函数名后加括号的话,就相当于执行了inter()。

这样我们就可以在任意地方调用函数的局部名称空间。

当然,你也可以把x当做参数进行传递。但是原理一定要搞清楚,因为这是接下来的重点内容的铺垫。

六、装饰器

当我们的软件已经在线上运行的时候,需要拓展新的功能,那么我们 应该怎么办呢?首先我们要提到开放封闭原则。

开放原则:对拓展功能是开放的。

封闭原则:对修改源代码则是封闭的。

也就是说我们既不能更改函数的源代码,也不能更改调用方式,这个时候,我们就要用到装饰器了。

装饰器:为函数增加额外的功能且不更改调用方式的函数。

6.1 无参装饰器

下面我们进行循序渐进的讲解,究竟是怎么用到装饰器的,他又是怎么做到的符合开放封闭原则的。我们以为一个函数增加一个计算运行时间的功能为例来进行推导。

import time

def foo(x):

print("其实我这里有很多代码,但是你看不到")

time.sleep(3)

return x

# 1、我们直接在函数内部进行更改。

def foo(x):

start_time = time.time()

print("其实我这里有很多代码,但是你看不到")

time.sleep(3)

stop_time = time.time()

process_time = stop_time - start_time

print(process_time)

return x

这时候虽然我们增加了功能,但是我们已经违反了开放封闭原则,所以失败。

# 2、利用函数的嵌套定义来直接增加新功能

def outer(x):

start_time = time.time()

foo(x)

stop_time = time.time()

process_time = stop_time - start_time

print(process_time)

return x

我们同样实现了功能,调用方式改变,源代码没变。

# 3、利用闭包函数来修改

def outer(foo):

def inter(x):

start_time = time.time()

res = foo(x)

stop_time = time.time()

process_time = stop_time - start_time

print(process_time)

return res

return inter

foo = outer(foo)

foo(x)

上述已经实现了不更改函数源码,不更改调用方式了,但是函数被写死了,如果用来修饰多个函数呢?这些函数又有不同个数的参数呢?

# 终极版装饰器的诞生。

def outer(func):

def inter(*args,**kwargs):

start_time = time.time()

res = func(*args,**kwargs)

stop_time = time.time()

process_time = stop_time - start_time

print(process_time)

return res

return inter

foo = outer(foo)

这是利用星号在形参和实参不同的作用来实现的。

自此,装饰器就写好了,遵守开放封闭原则,让用户在不知情的情况下,完成了函数的更新。要想掌握装饰器,必须弄懂闭包函数这些前置只是,如果还是不太懂,就继续看看闭包函数板块和参数的知识。

# 装饰器的模板。

def outer(func):

def inter(*args,**kwargs):

res = func(*args,**kwargs)

return res

return inter

添加装饰器的时候,可以根据模板然后在inter函数里面添加相关功能。

当然,还有一点就是语法糖的使用,我们可以将装饰器的赋值转成在被修饰函数上一行添加@装饰器名。

# 语法糖:在被修饰函数上一行添加@装饰器名。

@outer

def foo(x):

print(x)

6.2 有参装饰器

当用户进行身份验证时,有的函数要从info文件里得到信息,有的则是从数据库等等,那么如何让让一个装饰器实现那么多功能呢?我们都能想到,那就再给装饰器一个状态参数呗,要知道,之前为了伪装成被装饰参数,内部函数的参数已经固定,外部参数虽然也可以进行参数赋值,但是由于语法糖的存在,导致我们如果在外部函数增加参数之后,语法糖就不能使用了。

综上,我们还可以再次利用给函数传参的两种方式中的第二种,在函数外在套一层,此时的炸U那个时期已经是三个函数定义了。

# 语法糖的实际效果相当于执行了这一步:与被修饰函数名相同的全局变量 = 装饰器(被装饰函数名)

@outer # foo = outer(foo)

def foo():

print("1")

# 有参装饰器的模板

def auth(参数):

def outer(func):

def wrapper(*args,**kwargs):

res = func(*args,**kwargs)

return res

return wrapper

return outer

@auth("参数")

def foo():

print(123)

当程序运行到语法糖时,由于函数名加括号,开始执行函数返回outer的内存地址,所以实际上语法糖依然是@outer。不过是多执行了一步函数将参数传到内部函数的名称空间而已。

6.3多个装饰器的运行原理

def outer1(func):

def wrapper1(*args,**kwargs):

res = func(*args,**kwargs)

return res

return wrapper

def outer2(func):

def wrapper2(*args,**kwargs):

res = func(*args,**kwargs)

return res

return wrapper2

def outer3(func):

def wrapper3(*args,**kwargs):

res = func(*args,**kwargs)

return res

return wrapper3

@outer1 # foo = outer1(foo)= wrapper1的内存地址

@outer2 # foo = outer2(foo)= wrapper2的内存地址

@outer3 # foo = outer3(foo) = wrapper3的内存地址

def foo():

print("123")

加载顺序:outer3>outer2>outer1

执行顺序:outer1==>outer2>outer3

七、递归函数

递归函数的本质就是循环,在运行函数体期间一直在不停地调用自己。在Python解释器中,递归有一个最大限制,超过这个限制就会报错。

# 递归的直接调用

def foo():

print("一直循环我会报错")

foo()

# 递归的间接调用

def foo1():

print("我是正常的函数,调用了foo我就不正常了")

foo()

递归函数在调用自己的时候又两个过程

回溯:在函数中不停地调用自己的过程

递推:在函数满足条件退出函数之后,不停地将已打开的函数结束的过程。

# while循环

count = 0

while count <5:

print(count)

count += 1

# 改成递归函数之后

def cou(count):

if count > 4:

return

print(count)

count += 1

cou(count)

cou(0)

据我观察,递归函数应该是主要应用在需要参数且多次循环的场景之上。

八、函数的类型提示

这是一个可用可不用的一个提示。

def foo(x:str,y:"这里面应该写整数",z:list=[1,2,3]):

pass

形参后面加个冒号然后可以写一些提示性信息,声明一下数据类型等,但是如果你不按照规定传参,函数也不会报错,因为这个规定本来就是自己定的 ,但是你打破了这个自己提示的类型,那你声明干嘛呢,,就像调皮的用户,哈哈哈

python average函数详解_python基础之函数详解相关推荐

  1. python中使用函数的优点_Python基础之函数基本用法与进阶详解

    本文实例讲述了Python基础之函数基本用法与进阶.分享给大家供大家参考,具体如下: 目标 函数参数和返回值的作用 函数的返回值 进阶 函数的参数 进阶 递归函数 01. 函数参数和返回值的作用 函数 ...

  2. python调用函数的优点_python基础之函数

    1. 函数特点 1.1 定义了之后,可以在任何需要它的地方调用. 1.2 函数名不能变,每次调用实现相同的功能. 定义函数是的参数为形式参数,调用函数时传入的函数为实际参数. 形参和函数体中的参数相同 ...

  3. Python学习笔记---------廖雪峰(基础和函数)

    Python学习笔记---------廖雪峰(基础和函数)

  4. python中symbols函数用法_Python基础之函数用法实例详解

    本文以实例形式较为详细的讲述了Python函数的用法,对于初学Python的朋友有不错的借鉴价值.分享给大家供大家参考之用.具体分析如下: 通常来说,Python的函数是由一个新的语句编写,即def, ...

  5. python自定义函数详解_python基础教程之自定义函数介绍

    函数最重要的目的是方便我们重复使用相同的一段程序. 将一些操作隶属于一个函数,以后你想实现相同的操作的时候,只用调用函数名就可以,而不需要重复敲所有的语句. 函数的定义 首先,我们要定义一个函数, 以 ...

  6. python基础函数及用法意思_Python基础之函数基本用法与进阶详解

    本文实例讲述了Python基础之函数基本用法与进阶.分享给大家供大家参考,具体如下: 目标 函数参数和返回值的作用 函数的返回值 进阶 函数的参数 进阶 递归函数 01. 函数参数和返回值的作用 函数 ...

  7. python多线程详解_python基础:python多线程详解

    前言 多线程类似于同时执行多个不同程序,多线程运行有如下优点:使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进 ...

  8. python中map用法详解_Python:map函数用法详解

    一个简单的例子:将一个list中所有元素平方,常规的做法如下图所示,虽然实现了这个功能,但并没有给人一目了然的感觉.若换成map来实现,则会好很多. 常规方法 map函数 1.map函数介绍及其简单使 ...

  9. 在python中用递归的方法编程_python基础之函数,递归,内置函数

    阅读目录 一 数学定义的函数与python中的函数 初中数学函数定义:一般的,在一个变化过程中,如果有两个变量x和y,并且对于x的每一个确定的值,y都有唯一确定的值与其对应,那么我们就把x称为自变量, ...

最新文章

  1. 南华大学c语言多少分才能过_成人高考难吗?多少分可以过?
  2. python流程控制-实战案例手把手教你Python流程控制技巧
  3. 阿里云MySQL远程登录异常
  4. JZOJ 5399. 【NOIP2017提高A组模拟10.7】Confess
  5. ZooKeeper在HBase集群中的作用
  6. 美国人用什么android手机,美国人最爱的安卓手机:多年稳居榜首,与iPhone共分一杯羹...
  7. hive 取消打印日志信息_Hive及其相关大数据组件
  8. 汉字字符内码查询_计算机等级考试查询系统
  9. 拉扎维RFIC总结与笔记
  10. Elasticsearch常用搜索和分词器
  11. 内外网双网卡同时上网
  12. scp登录The authenticity of host 192.168.0.xxx can't be established. 的问题
  13. 苹果开发者账号申请需要注意的三要素
  14. python筛选出csv满足某条件的行_Python之根据条件筛选特定行
  15. maven相关(1)
  16. 从网络安装debian到使用中遇到的问题
  17. vc++6.0中文乱码的解决方法
  18. 用matlab求状态转移矩阵
  19. 梅宏院士:如何构造人工群体智能?| 智源大会特邀报告回顾
  20. php 图片接受,PHP-图片处理

热门文章

  1. Thinkphp3.2(tp3.2) sql语句表达式查询字符列表【eq,neq,gt,egt,lt,elt,in,between,like,exp】
  2. 采购订单中带账户分配的总账科目确定(M和E的区别)
  3. dword 占用多少信_【优质文档】土地占用投诉信-精选word文档 (5页)
  4. tp5接收前台传的值_tp5数据接收
  5. python三维建模和cad比较_对比Revit和CAD三维建模的不同
  6. 设置页面默认为繁体字
  7. 声音以模拟信号的形式存储在计算机中,08计算机1-2班《计算机基础》自学资料(5)...
  8. 看待FIL能否成功不应该只关注它的币价,而是IPFS能否完全落地应用……
  9. Threejs动态箭头
  10. Web APIS Xmind