七 Python 函数,方法,知识,思想详解

目录

1 Python 内部函数

2 自定义函数

3 参数

4 返回值

5 函数的注释

6 换个角度来看待函数

7 什么是方法


在我用拼音打字的时候,我脑海里只有文字,甚至有某些常用的句子,至于某个字的拼音是怎么打的,我根本不用考虑这些。比如我脑袋里想到了 “我” 这个字,我的手很自然就打出了 “wo” 这个拼音,这样的肌肉记忆让我可以专注在我想描述的思路和文字上,至于这些文字是怎么被拼写出来的我根本不用考虑。

我在开车的时候,看到红灯我都会下意识的去踩刹车,把车速控制住,然后让车辆平稳地停下来,等待交通灯变绿。在整个刹车的过程中,我只要专注于脚踩刹车,并不用去了解车辆刹车系统是怎么工作的。

img

当你用微信发消息给朋友的时候,你只需要专注于要发送的内容。至于这条信息是如何通过手机传送到网络然后精准的通过网络传送到你朋友手机上的,你不用关心。

img

在生活中类似上面的场景很多,它们都有一个共同的特点,通过肌肉记忆、通过汽车刹车总成、通过模块化,让我们的精力集中在我们当下要做的事情上面,不用去考虑 “操作” 和 “结果” 之间是如何建立起因果关系的(比如:踩刹车,汽车减速这样的因果关系)。

这样的机制大大降低了我们人脑的开销,毕竟我们人脑的算力是有限的。假如我们踩刹车的时候,脑袋里都要过一遍刹车的原理、刹车油泵是怎么工作的,ABS是如何介入的,什么时候会介入? 那么我们的反应速度至少会降低100倍。

这样的机制对我们人脑是一种保护,我们能遨游太空、建设庞大的建筑群、管理国家、管理公司、开发复杂的软件都离不开这种机制-----层级思维的机制(这和软件设计有关,后面会讲到),也可以把它叫做函数思维。在 Python 里面,我们该去如何定义函数这个概念呢?

编程语言里的函数就是把一堆代码 “打包成” 一个 “命令” ,其他人可以直接使用它,不用关心“包”里面的细节。

我们来看看在Python里使用函数的一些例子:

img

  1. 第2行代码,我们创建了一个整数列表 num_list。

  2. 第3行、第8行,我们分别用Python自带的max,min函数来计算这个列表里的最大值和最小值。

  3. 第11行、我们用 print() 函数打印被 str() 函数字符串处理的列表,这是一种函数调用嵌套的用法。

我们结合上面的例子来总结一下函数的构成: 函数的计算结果 = 函数名(传给函数的外部参数)。在上面的第5行,max_num是函数的计算结果,max是函数名,num_list 是传给函数的外部参数。第8行的函数用法也是这样的套路。这里我们需要注意,函数的计算结果不仅仅会是一个数值,它可能是一个动作,比如第11行代码,str()函数的计算结果是一个字符串,这个字符串是 print()这个函数的外部参数,然后print()函数运算的结果不是字符串,而是一种行为,把参数里的字符串打印到屏幕上。

1 Python 内部函数

我们可以看看Python默认给我们提供了哪些内部函数?

img

很多函数我们通过函数名就知道它的作用是什么,比如 sum()它是一个求和的函数、format()它是一个格式化函数,并且像 max()、min()、type()等这些函数我们已经用过。

其实Python那么多的内置函数,我们根本不用去死记硬背它们,我们只要大概浏览一遍,有一个印象就好,在具体需要使用的时候去用它就可以了,具体它的用法和注意的细节可以点击上表中的函数名,然后跳转到相应的官方文档查看详细说明便可以。

2 自定义函数

在我们面对实际开发任务的时候,也许Python的内置函数不好用、或者就根本没有这样的函数,这时该如何是好? 这时就需要使用所有编程语言的精髓,自定义函数! 我们先来看看下面这个例子:

img

我们先来看第16,17行代码。16行代码我们定义了一个 num_list。第17行代码我们使用了一个叫做 num_list_avg 的函数,把刚刚创建好的 num_list 传给了它,然后它会返回给我们传入整数列表的平均数。第19行代码,打印这个计算结果。

在使用num_list_avg函数时我们只要知道它能给我们带来什么就行,具体它内部是如何工作的我们不需要知道。num_list_avg 并不是Python的内部函数,之所以它可以使用是因为我们在第2~12行定义了它,现在我们具体来看看在Python里是如何定义一个函数的。

第2行开头的 def 它是Python定义函数的关键字,它告诉Python接下来的代码就是一个函数的实现。紧跟def 后面的是 函数的名称,给函数取名就像给变量取名一样。因为这个函数是计算整数列的平均数,所以我给它起名叫做 num_list_avg。函数名后面紧跟一个括号,括号里定义了该函数要运作需要的参数。在这个函数里我们把这个参数叫做 arg 。接下来的 “:” 代表要开始写函数里的代码了,注意函数里的代码,相比较def的位置都向右缩进了四个空格,这是Python使用缩进来代替 “{}” 大括号来划分代码块的办法。

第4~9行就是这个函数的代码,或叫做实现、或叫做算法。4~9行的代码很简单,难不倒在我们这里学习的小伙伴。第12行是一个 return 关键字,它的意思是调用这个函数的 “人” 能得到 return 后面的值。

还记得Python在执行代码时是自上而下一行一行的执行的吗? 这意味着我们要使用一个自定义函数,该函数的定义和实现一定要在调用它之前处理好,否则调用的时候,Python识别不出来自定义的函数。

这就是Python最基础定义函数的方式和规则,它定义了函数的名称、函数的参数、函数的结果、函数的实现!

3 参数

函数是一个很灵活的概念,函数的定义也是,我们可以来看看下面的这些例子:

img

上面的代码,我们定义了一个攻击函数 attack()。它不需要参数,也没有返回值。调用这个函数就只会在控制台简单的打印出函数定义的内容,第8行我们调用了这个函数。这是在Python里最简单的函数定义方式,好了我们基于这个函数来做扩张,从而了解函数定义的各种方法和细节。

img

attack_1 函数是 attack 的改进,我们为attack_1 这个函数添加了参数 danage 还有一个返回值 exp,它通过第19行的 return 关键字把这个值返回给调用它的人。第22行代码调用了attack_1,并且传入了291这个整数给 attack_1 函数,结果如下:

img

在 attack_1 里 damage 定义了一个参数,它不一定叫做 damage 它也可以叫做 shanhai 或者其他你觉得可以和 “伤害值” 关联上的名字(甚至一点关联没有也不怕,你自己知道它们之间的关系就行,但然,这是团队开发的禁忌),我们在调用这个函数时 传给 damage 的参数可以传一个字符串、或者一个对象、一个布尔值,但这样的做法会让程序报错,因为我们代码在使用damage这个参数的时候,已经假定了damage是一个整数。

我们接着来改进:

img

从上面的代码可以看出,在 attack_2 的基础上面,我们给函数增加了 crit(暴击) 这个变量。在28行代码里,通过 damage 与 crit 这两个参数的运算,计算出来了这次攻击给对方造成的真实伤害。我们可以给一个函数设计多个参数,只要我们需要。第39行我们调用了 attack_2 这个函数,并且给它了它需要的参数。

我在一开始学习编程的时候就想,如果我们传给函数的参数和函数定义的参数不一样怎么办?这个时候IDE会提示我们该如何正确的调用这个函数,如果我们执意不按照函数定义的参数来调用函数,那么系统会报错。大部分开发语言都是这样处理函数参数使用的,牺牲了灵活性增加了代码语义的一致性。

我们继续改进:

img

attack_3 在 attack_2 的基础上面增加了两个参数 effect_start 、effect_end。并且还使用了一种小技巧,及我们可以给函数的参数设置默认值,开发人员在调用这个函数的时,如果不给有默认值的参数传递值,那么函数内部使用这个参数的时候就是用默认值。

为参数指定默认值很简单,看第43行代码参数部分。crit=0.0,effect_start=0,effect_end=0。这三个参数的默认值分别为 0.0 , 0 , 0。当开发人员调用这个函数,并且没有给出这些参数时,该函数使用默认值来做计算,第64行代码就是这样操作的,它的执行结果如下:

img

在上面的代码第43行,还有一个细节。如果 crit 这个参数设置了默认值,那么这个参数后面的参数都要设置默认值。 Python这样设计其实是迫不得已,因为不这样 Python 就不知道 调用者使用的参数和函数的参数如何对应起来了(Python 会把参数的位置与定义函数时,定义参数的位置进行一一对应),这听起来会有点不对,我建议你自己亲自做个实验,就明白我在说什么了。

有的小伙伴可能已经在思考一个问题了,如果我要传的参数不确定个数,不确定类型,这样是不是就无解了?我就是要这样的灵活性该怎么办? 我们直接来看下面的代码:

img

在 attack_4 里面只有一个参数 dict_attack。从参数的名称便可以看出,这个函数会把整个参数当成字典来使用。第73~76行就解释了 attack_4 是如何使用 dict_attack 这个变量的。这样的参数确实很灵活了,但也变得不严谨了,在一些实际开发场景中,还真需要这样做,print 内置函数的参数就是一个典型的例子。

我们来看一下 attack_4 的参数要怎么传:

img

我们通过这样的小聪明手段,用字典作为参数来定义函数,做到给函数传参数时能灵活自由。当然Python作为一门受欢迎的语言,用另外一种方式给我们提供解决方案,叫做不定长参数,我们直接来看例子:

img

第102行我们定义了一个函数 exa_1 它的参数定义成了 args。 “” 是这种定义方式的重点,它告诉Python 此处可以任性输入参数。exa_1 这个函数很简单,我们输入什么给它,它就把什么打印到屏幕上。第108~110行,我们尝试给这个函数塞入各种各样的参数,我们来看看它输出的结果:

img

看到输出结果,大家肯定就明白了,原来我们任意输入的参数,被Python放到了一个元祖里。这个和我们用字典来做参数的原理一样。参数传进来以后,要怎么使用就看我们要如何设计这个函数了。这样的参数定义方法也可以和普通的定义方法混用,比如:

img

在 exa_2 里,定义了两个参数 一个普通的 name 和一个 万能参数 *other。函数的实现很简单,就是把这两个参数打印出来。第120~121行就是exa_2这个函数的调用方法。可以看到 *other 这个参数其实可用可不用。我们来看看调用 exa_2 的效果:

img

从上面的输出结果可以看到,有一个空元祖“()”,这是因为我们第一次调用exa_2的时候只传了一个参数。这里需要注意,不定长参数和普通参数混用时,不定长参数永远放在最后!这样Python才能明白那个参数用于什么地方,大家想想看这是为什么?

Python还给我们提供了一种传参的方式,叫做字典传参,它有点像我们之前耍小聪明用字典对象实现多个参数的传递,但它比直接用字典传参更直观一些,我们看代码:

img

上面的代码和 exa_1 这个函数很像,一个参数传进来然后打印它完事。唯一不同就是不用 * 来做参数的前缀,而是用 “”。在Python里函数参数定义时,参数名称前面加一对 “” 代表这个参数不但可以接受任意的参数,并且Python会把这些参数打包成字典来处理。

我们在第131行代码调用了这个函数,给他传了3个参数,这里的形式有点像字典里的键值对(key:value),我们看看 第131 行代码的的执行结果:

img

从结果来看,我们传入的参数被python转换成了一个字典。

假如我们要定义的函数,它的参数是一般形式 + 不定长形式 + 字典形式,这样我们该如何处理,直接看代码:

img

exa_4 这个函数定义了三个参数 name,*contact,**other。分别是一般形式的参数、不定长形式的参数、字典形式的参数,这里需要注意这个顺序不能乱,一乱Python就找不到该如何对应参数了。第137~139行代,是逐个把这些参数打印出来。

第143行,我们调用 exa_4 函数,并且传入了相应的参数 ,参数和传入的值对应如下:

  1. 参数 name ,对应的值 "张三"。

  2. 参数 *contact , 对应的值 “135771XXXX” 和 "2849888XXXX"。

  3. 参数 **other, 对应的值 hight = "172cm",weight = "65kg"。

我们来看函数带这些参数执行下来的效果:

img

好了Python传递参数的用法常用的就是这些了,我们现在来看看 return。

4 返回值

函数的传参还是有很多花样的,但函数的返回值、输出就简单多了。上面的代码 return 都只返回了一个值,这也是大部分开发场景使用的模式。我们在实际开发中会返回一个对象、或者列表、或者字典、又或者是某种特殊的数据结构描述语言比如Json、XML等。我们来看看下面的一些例子,为了节省空间这些例子中的函数都只有一个 Return 构成:

img

看上面的代码。

  1. r_1 : 返回了一个整数 100。

  2. r_2:返回了一个列表 [1,2,3,4,5,6]。

  3. r_3:返回了一个生成器。

  4. r_4 : 返回了一个字典。

  5. r_5:这里看似返回了多个值,其实这里返回的是 (1,2,3) 对象元祖。

5 函数的注释

注释,在我们介绍最基本的语法时介绍过它,它就是我们代码的 “旁白” 。在那些标榜的开源项目里面,优秀的代码都少不了注释。好的注释能让其他人能更快速的理解你的代码,理解你的思想之美。在一些大神的代码里,你可能经常会看到有大于百行的注释,实际代码可能才10几行,这说明在大神的眼里注释的重要性不比代码本身小。还记得之前提过的一句话吗? “优秀的代码,是别人能很快看得懂的代码”。

在函数的定义和编写里,大神们经常会很仔细地去编写注释,告诉调用者或者其他程序员这个函数的目的,它传入的参数到底有什么意义,这个函数最终会输出什么等。我们来看看具体例子:

img

attack_5 的代码实现和 attack_4 是完全一样的,只不过它多了 第173~180行代码。这部分代码是 attack_5 的注释,它清晰的描述了这个函数的作用、传入的参数描述、返回的结果描述。

关于这段描述我们使用了Python推荐的描述方法,第174行是这个函数的作用。 :param damage: 是这个函数 damage 参数的说明。:return: 是这个函数返回值的说明。当然函数的注释,我们也可以用自己喜欢的方式来编写,不同的公司、不同的组织都会有自己的一些编写规范,无论什么方式注释是不会影响代码运行的。

之所以Python推荐这样的模式,是因为 Pycharm 这类的IDE可以识别这种模式的注释,让开发人员在调用该方法的时候 IDE 能够提示开发人员这个函数该怎么用,我们来看一下例子:

img

当然最重要的是编写注释习惯是每一个开发人员都需要的习惯。

6 换个角度来看待函数

我们先来看例子,然后再总结:

这是一个模拟做早餐的函数。

img

这是一个模拟做晚饭的函数。

img

我们仔细看一下这两个函数,会发现他们有很多代码都一样。做菜前的洗菜,吃完饭后的洗碗,这两部分的代码都完全一样,完全重复的。那是不是我们要设计 下午茶、夜宵 等函数也得这样?假如洗菜。洗碗流程发生了改变,是不是我要在两个地方进行修改?

其实有经验的软件工程师他们不会这样,他们知道一个铁律,“不会重复的造车轮” 因为这样看上去很笨,实际这样做真的很笨,笨的可爱。那这些工程师会怎么做呢?

看下面的代码:

img

经验丰富的程序员把 之前重复的 洗菜操作、洗碗操作 都提取出来了,放到了新的函数里面,它们分别是 wash_food 与 wash_bowls 函数。这个时候我们在看做早点、做晚饭这两个函数:

img

是不是这两个函数瞬间变得清晰了、代码逻辑也舒服多了!更重要的是,假如我们要去修改洗菜、洗碗的步骤,只要在一个地方集中修改就好,不用修改两遍了,若下午茶、夜宵函数也需要洗菜、洗碗,那直接调用它们就可以了,是不是很方便?这就是函数带来的另外一个好处,抽象重复的事物、代码的整理、结构化的设计等等。

7 什么是方法

方法就是函数、函数就是方法,函数有的方法也有、方法有的函数也有,它们其实就是一会事儿。唯一不同的是函数无须依赖于任何对象,但方法始终依赖于某个对象,我们来看看下面的例子:

img

第5行 是 max 函数的使用,这个已经不用太多介绍了。

第9行 是 list_num (一个列表对象) 使用了它的一个方法 remove,把之前找到最大的数 从列表中移除。在这里可以看到,对象是使用 “.” 这个操作符来调用附属于自己的方法的,在对象使用方法时,方法的名称、参数的传递、结果的返回和函数是一模一样的。当然对象的方法还可以这样来调用:

img

第2~3行,我们创建了两个集合。

第6行对集合进行了一系列的方法调用,第一次见到这样用法的小伙伴一定觉得奇怪,其实它的原理很简单,我们来看一下:

set_1.union(set_2) 是返回一个新的字典,这个字典也有和set_1一样的内部方法,所以它同样可以调用其他方法,比如 . difference(set_2),以此类推就不难理解第6行代码的意图了。要再解释得清楚一点的话,我们来看下图:

img

第10~14行代码 和 第6行代码效果等同。关于方法的自定义、方法的更多知识,我们在后面的内容为大家介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
函数装饰器是Python中一种特殊的语法,可以用来修改、扩展或包装其他函数的功能。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数作为结果。 装饰器的语法使用了@符号,它放在要修饰的函数定义之前。当调用被修饰的函数时,实际上是调用了装饰器返回的新函数。 下面是一个简单的装饰器示例: ```python def decorator(func): def wrapper(): print("Before function execution") func() print("After function execution") return wrapper @decorator def say_hello(): print("Hello, world!") say_hello() ``` 在这个例子中,`decorator`是一个装饰器函数,它接受一个函数作为参数,并定义了一个内部函数`wrapper`来包装原始函数。`wrapper`函数在调用原始函数前后分别打印了一些信息。 通过在`say_hello`函数定义之前添加`@decorator`语法,我们将`say_hello`函数传递给`decorator`装饰器,并将返回的新函数赋值给`say_hello`。因此,当我们调用`say_hello`时,实际上是调用了被修饰后的函数`wrapper`。 这样,每次调用`say_hello`函数时,都会在执行前后打印一些信息。 装饰器可以用于很多场景,比如日志记录、性能分析、权限检查等。它们提供了一种简洁而优雅的方式来修改函数的行为,而无需修改函数本身的定义。同时,装饰器还可以堆叠使用,即一个函数可以被多个装饰器修饰。 希望这个简单的示例能够帮助你理解Python函数装饰器的基本概念和用法。如果你有更多的问题,可以继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深思熟虑的羽毛球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值