【Python学习手册(第四版)】学习笔记16-函数基础

个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。

本文主要介绍Python中函数的基本概念,作用域以及参数传递函数语法以及def和return语句的操作,函数调用表达式的行为,以及Python函数中多态的概念和优点。强调了所有的在函数内部进行赋值的变量名都默认为本地变量,所有的本地变量都会在函数调用时出现,并在函数退出时消失。全文非常基础、易懂,阅读时间约5-10分钟。


目录

函数

为何使用函数

编写函数

def语句

def语句是实时执行的

定义和调用示例

定义

调用

Python中的多态

寻找序列交集示例

定义

调用

多态复习

本地变量


函数

简而言之,一个函数就是将一些语句集合在一起的部件,它们能够不止一次地在程序中运行。函数还能够计算出一个返回值,并能够改变作为函数输入的参数,而这些参数在代码运行时也许每次都不相同。以函数的形式去编写一个操作可以使它成为一个能够广泛应用的工具,让我们在不同的情形下都能够使用它。

更具体地说,函数是在编程过程中剪剪贴贴的替代——不再有一个操作的代码的多个冗余副本,而是将代码包含到一个单独的函数中。如果这个操作之后必须要修改,只需要修改其中的一份拷贝,而不是所有代码。

函数是Python为了代码最大程度的重用和最小化代码冗余而提供的最基本的程序结构。函数也是一种设计工具,使用它我们可以将复杂的系统分解为可管理的部件。

表16-1总结了这一部分中下文将会提到的与函数相关的主要语句和表达式。

为何使用函数

函数是一个通用的程序结构部件。

简而言之,函数主要扮演了两个角色。

最大化的代码重用和最小化代码冗余

Python的函数是一种简单的办法去打包逻辑算法,使其能够在之后不止在一处、不止一次地使用。

函数允许整合以及通用化代码,以便这些代码能够在之后多次使用。因为它们允许一处编写多处运行,Python的函数是这个语言中最基本的组成工具——它让我们在程序中减少代码的冗余成为现实,并为代码的维护节省了不少的力气。

流程分解

函数也提供了一种将一个系统分割为定义完好的不同部分的工具。

函数将会将整个任务分割成为独立的函数来完成整个流程中的每个子任务。独立的实现较小的任务要比一次完成整个流程要容易得多。一般来说,函数讲的是流程:告诉你怎样去做某事,而不是让你使用它去做的事。

编写函数

虽然没有正式介绍,但是在前面已经接触了一些函数。例如,为了创建文件对象,调用了内置函数open。同样地,使用了内置函数len去得到一个集合对象的元素的数目。

这里会解释在Python中如何去编写一个函数。我们编写的函数使用起来就像内置函数一样:它们通过表达式进行调用,传入一些值,并返回结果。

下面是关于Python函数背后的一些主要概念的简要介绍:

def是可执行的代码。Python的函数是由一个新的语句编写的,即def。def是一个可执行的语句——函数并不存在,直到Python运行了def后才存在。在典型的操作中,def语句在模块文件中编写,并自然而然地在模块文件第一次被导入的时候生成定义的函数。

def创建了一个对象并将其赋值给某一变量名。当Python运行到def语句时,它将会生成一个新的函数对象并将其赋值给这个函数名。就像所有的赋值一样,函数名变成了某一个函数的引用。函数对象可以赋值给其他的变量名,保存在列表之中。函数也可以通过lambda表达式创建。

lambda创建一个对象但将其作为结果返回。也可以用lambda表达式创建函数,允许我们把函数定义内联到语法上一条def语句不能工作的地方。(第19笔记介绍)

return将一个结果对象发送给调用者。当函数被调用时,其调用者停止运行直到这个函数完成了它的工作,之后函数才将控制权返回调用者。函数是通过return语句将计算得到的值传递给调用者的,返回值成为函数调用的结果。

yield向调用者发回一个结果对象,但是记住它离开的地方。像生成器这样的函数也可以通过yield语句来返回值,并挂起它们的状态以便稍后能够恢复状态。

global声明了一个模块级的变量并被赋值。在默认情况下,所有在一个函数中被赋值的对象,是这个函数的本地变量,并且仅在这个函数运行的过程中存在。为了分配一个可以在整个模块中都可以使用的变量名,函数需要在global语句中将它列举出来。通常情况下,变量名往往需要关注它的作用域(也就是说变量存储的地方),并且是通过实赋值语句将变量名绑定至作用域的。

nonlocal声明了将要赋值的一个封闭的函数变量。nonlocal语句允许一个函数来赋值一条语法封闭的def语句的作用域中已有的名称。当一个函数调用的时候,信息被记住了——而不必使用共享的全局名称。

函数是通过赋值(对象引用)传递的。在Python中,参数通过赋值传递给了函数(对象引用)。Python的模式中,调用者以及函数通过引用共享对象,但是不需要别名。改变函数中的参数名并不会改变调用者中的变量名,但是改变传递的可变对象可以改变调用者共享的那个对象。

参数、返回值以及变量并不是声明。在函数中并没有类型约束。从一开始函数就不需要声明:可以传递任意类型的参数给函数,函数也可以返回任意类型的对象。其结果就是,函数常常可以用在很多类型的对象身上,任意支持兼容接口(方法和表达式)的对象都能使用,无论它们是什么类型。

下面会有具体的示例

def语句

def语句将创建一个函数对象并将其赋值给一个变量名。Def语句一般的格式如下所示。

def包含了首行并有一个代码块跟随在后边,这个代码块通常都会缩进(在冒号后边)。这个代码块就成为了函数的主体——也就是每当调用函数时Python所执行的语句。

def的首行定义了函数名,赋值给了函数对象,并在括号中包含了0个或以上的参数(有些时候称为是形参)。

在函数调用的时候,在首行的参数名赋值给了括号中的传递来的对象。

函数主体往往都包含了一条return语句。

Python的return语句可以在函数主体中的任何地方出现。它表示函数调用的结束,并将结果返回至函数调用处。

return语句包含一个对象表达式,这个对象给出的函数的结果。return语句是可选的。如果它没有出现,那么函数将会在控制流执行完函数主体时结束。

从技术角度来讲,一个没有返回值的函数自动返回了none对象,但是这个值是往往被忽略掉的。

def语句是实时执行的

Python的def语句实际上是一个可执行的语句:当它运行的时候,它创建一个新的函数对象并将其赋值给一个变量名。

记住,Python中所有的语句都是实时运行的,没有像独立的编译时间这样的流程。

因为它是一个语句,一个def可以出现在任一语句可以出现的地方——甚至是嵌套在其他的语句中。

例如,尽管def往往是包含在模块文件中,并在模块导入时运行,函数还是可以通过嵌套在if语句中去实现不同的函数定义,这样也是完全合法的。

它在运行时简单地给一个变量名进行赋值。与C这样的编译语言不同,Python函数在程序运行之前并不需要全部定义。更确切地讲,def在运行时才进行评估,而在def之中的代码在函数调用后才会评估。

因为函数定义是实时发生的,所以对于函数名来说并没有什么特别之处。关键之处在于函数名所引用的那个对象。

将函数赋值给一个不同的变量名,并通过新的变量名进行了调用。

就像Python中其他语句的一样,函数仅仅是对象,在程

序执行时它清楚地记录在了内存之中。

实际上,除了调用以外,函数允许任意的属性附加到记录信息以供随后使用。

定义和调用示例

函数描绘了两个方面:定义(def创建了一个函数)以及调用(表达式告诉Python去运行函数主体)。

定义

在交互模式下输入的定义语句,它定义了一个名为times的函数,这个函数将返回两个参数的乘积。

def times(x,y):     #创建并定义函数return x * y    #被唤起时调用执行

当Python运行到这里并执行了def语句时,它将会创建一个新的函数对象,封装这个函数的代码并将这个对象赋值给变量名times。

一个典型场景是,这样一个语句编写在一个模块文件之中,当这个文件导入的时候运行。对于这么小的一个程序,用交互提示模式已经足够了。

调用

在def运行之后,可以在程序中通过在函数名后增加括号调用(运行)这个函数。括号中可以包含一个或多个对象参数,这些参数将会传递(赋值)给函数头部的参数名。

times(3.14,4)
12.56

这个表达式传递了两个参数给times函数。参数是通过赋值传递的。函数头部的变量x赋值为3.14,y赋值为4,之后函数的主体开始运行

这个函数,其主体仅仅是一条return语句,这条语句将会返回结果作为函数调用表达式的值。在这里返回的对象将会自动打印出来,但是,如果稍后需要使用这个值,我们可以将其赋值给另一个变量。例如:

x = times(3.14,4)        #保存对象结果
x
12.56

下面再试试第三种,传递两个完全不同种类的对象:

times('hi',4)    #函数是无类型的
'hihihihi'

这次,函数的作用完全不同(Monty Python再次被引用)。在这第三次调用中,将一个字符串和一个整数传递给x和y,而不是两个数字。“*”对数字和序列都有效。因为在Python中,在这一函数里,我们从未对变量、参数或者返回值有过类似的声明,可以把times用作数字的乘法或是序列的重复。

也就是说,函数times的作用取决于传递给它的值。这是Python中的核心概念之一。

Python中的多态

times函数中表达式x*y的意义完全取决于x和y的对象类型,同样的函数,在一个实例下执行的是乘法,在另一个实例下执行的却是赋值。

Python将对某一对象在某种语法的合理性交由对象自身来判断。实际上,“*”在针对正被处理的对象进行了随机应变。

这种依赖类型的行为称为多态,含义就是一个操作的意义取决于被操作对象的类型因为Python是动态类型语言,所以多态在Python中随处可见。实际上,在Python中每个操作都是多态的操作:print、index、*操作符,还有很多。

这是有意而为的,且从很大程度上算Python的简易性、灵活性的表现。作为函数,例如它可以自动地适用于所有类别的对象类型。只要对象支持所预期的接口(a.k.a.protocol),那么函数就能处理。

也就是说,如果对象传给函数的对象有预期的方法和表达式操作符,那么它们对于函数的逻辑来说就是有着即插即用的兼容性的

即使是简单的times函数,任意两个支持*的对象都可以执行,无论它是哪种类型,也不管它是何时编写的。

除此之外,如果传递的对象不支持这种预期的接口,Python将会在*表达式运行时检测到错误,并自动抛出一个异常

这也是Python和静态类型语言(如C++和Java)至关重要不同之处在Python中,代码不应该关心特定的数据类型。如果不是这样,那么代码将只对编写时你所关心的那些类型有效,对以后的那些可能会编写的兼容对象类型并不支持,这样做会打乱代码的灵活性。

这种多态的编程模型意味着必须测试代码去检测错误,而不是开始提供编译器用来为我们检测类型错误的类型声明。

大体上来说,我们在Python中为对象编写接口,而不是数据类型。

寻找序列交集示例

之前的案例中编写了一个loop循环,搜索两个字符串公共元素。那段代码并不是想象的那么有用,因为这个程序被设置为只能列出定义好的变量并且不能继续使用。

当然,可以在需要它的每一个地方都使用拷贝粘贴的方法,但是这样的解决方案既不好也不通用——我们还是得编辑每一份拷贝的内容,将它换成不同的序列名称,并且改变不同拷贝所需要的算法。

定义

现在可能你已经猜到了解决这种困境的办法:将这个for循环封装在一个函数之中。这样做的好处如下。

  • 把代码放在函数中让它能够成为一个想运行多少次就运行多少次的工具。
  • 调用者可以传递任意类型的参数,函数对于任意两个希望寻找其交集的序列(或者其他可迭代的类型)都是通用的。
  • ·当逻辑由一个函数进行封装的时候,一旦需要修改重复性的任务,只需要在函数里进行修改搜索交集的方式就可以了。

实际效果就是,将代码封装在函数中,使它成为一个通用搜索交集的工具。在模块文件中编写函数意味着它可以被计算机中的任意程序来导入和重用。

def intersect(seq1,seq2):res = []for x in seq1:if x in seq2:res.append(x)return res

将第13学习笔记的的简单代码转化为这样的函数是很直接的。就是把原先的逻辑编写在def头部之后,并且让被操作的对象变成被传递进入的参数。为了实现这个函数的功能增加了一条return语句来将最终结果的对象返回给调用者。

调用

在能够调用函数之前,必须先创建它

可以先运行def语句,要么就是通过在交互模式下输入,要么就是通过在一个模块文件中编写好它,然后导入这个文件。

一旦运行了def,就可以通过在括号中传递两个序列对象从而调用这个函数:

s1 = 'spam'
s2 = 'scam'
intersect(s1,s2)
['s', 'a', 'm']

这里,传递了两个字符串,并且得到了一个包含着用逗号分隔的字符的列表。这个函数的算法相当的简单:“对于第一个参数中的所有元素,如果也出现在第二个参数之中,将它增加至结果之中”。

intersect函数相当慢(它执行嵌套循环),并不是真正的数学交集(结果中可能有重复的元素),并且也根本不必要(Python的集合数据类型提供了一个内置的交集操作)。

实际上,这个函数可以用一个单独的列表解析表达式来替代,因为它展示了经典的循环收集器代码模式

[x for x in s1 if x in s2]
['s', 'a', 'm']

作为一个函数的基础示例,它完成了任务——这个单个的代码段可以应用于整个的对象类型范围

多态复习

和所有的Python中的函数一样,intersect是多态的。也就是说,它可以支持多种类型,只要其支持扩展对象接口:

x = intersect([1,2,3],(1,3))
x
[1, 3]

这次,给函数传递了不同类型的对象[一个列表和一个元组(混合类型)],并且仍然是选择出共有的元素。

没有必要去定义预先定义参数的类型,这个intersect函数很容易对传递给它的任何序列对象进行迭代,只要这些序列支持预期的接口就行了

对于intersect函数,这意味着第一个参数必须支持for循环,并且第二个参数支持成员测试。所有满足这两点的对象都能够正常工作,与它们的类型无关——这包括了物理存储的序列,例如,字符串和列表。所有在第14学习笔记见到过的迭代对象,包括文件和字典;甚至编写的支持操作符重载技术的任意基于类的对象。

强调:如果传入了不支持这些接口的对象(例如,数字),Python将会自动检测出不匹配,并抛出一个异常——这正是我们一般所想要的。

intersect(2,3)
Traceback (most recent call last):File "<pyshell#34>", line 1, in <module>intersect(2,3)File "<pyshell#22>", line 3, in intersectfor x in seq1:
TypeError: 'int' object is not iterable

如果希望明确地编写类型检测的话,我们可以利用它来自己实现。通过不编写类型测试,并且允许Python检测不匹配,我们都减少了自己动手编写代码的数量,并且增强了代码的灵活性。

本地变量

这个例子中最有趣的部分是其名称。

证明了,intersect函数中的res变量在Python中叫做本地变量——这个变量只是在def内的函数中是可见的,并且仅在函数运行时是存在的。

实际上,由于所有的在函数内部进行赋值的变量名都默认为本地变量,所以intersect函数内的所有的变量均为本地变量。

·res是明显的被赋值过的,所以它是一个本地变量。

·参数也是通过赋值被传入的,所以seq1和seq2也是本地变量。

·for循环将元素赋值给了一个变量,所以变量x也是本地变量。

所有的本地变量都会在函数调用时出现,并在函数退出时消失——intersect函数末尾的return语句返回结果对象,但是变量res却消失了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392891.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Delphi5实现DLL的编写、调用

效果图 显式跟隐式调用差不多的&#xff0c;就重新画了窗体&#xff0c;画的有点粗糙。 DLL文件 DLL文件是一种包含了可执行代码的库文件&#xff0c;但它不能独立运行&#xff0c;必须由其他程序&#xff08;如EXE文件&#xff09;显式或隐式地加载并调用。DLL文件通常用于实…

全国地铁路线及站点SHP数据

数据是GIS的血液&#xff01; 我们在《126M全球手机基站SHP数据分享》一文中&#xff0c;为你分享过全球手机基站分布数据。 现在再为你分享全国地铁轻轨路线与站点SHP数据&#xff0c;你可以在文末查看该数据的领取方法。 全球地铁路线及站点数据 截至2023年12月31日&…

LAVIS在Mac,M1PRO芯片下的安装实战

LAVIS在Mac,M1PRO芯片下的安装实战 契机 ⚙ 本地想装个图片理解的大模型&#xff0c;看了下blip2感觉比较合适&#xff0c;macos安装的时候有点坑需要注意下&#xff0c;但是最终也无法使用mps加速&#xff0c;比较蛋疼。这里记录下安装步骤。 安装 LAVIS/projects/blip2 a…

【研发日记】Matlab/Simulink技能解锁(十二)——Stateflow中的两种状态机嵌套对比

文章目录 前言 项目背景 两级状态机 函数状态机 分析和应用 总结 参考资料 前言 见《【研发日记】Matlab/Simulink技能解锁(七)——两种复数移相算法》 见《【研发日记】Matlab/Simulink技能解锁(八)——分布式仿真》 见《【研发日记】Matlab/Simulink技能解锁(九)——基…

数据结构(其四)--特殊矩阵的存储

目录 11.特殊矩阵的压缩存储 &#xff08;1&#xff09;.一维数组的储存结构 &#xff08;2&#xff09;.二维数组的存储结构 &#xff08;3&#xff09;.普通矩阵的存储 &#xff08;4&#xff09;.特殊矩阵的压缩存储 i.对称矩阵 ii.三角矩阵 iii.三对角矩阵 iiii.稀疏矩…

Java多商户新零售超市外卖商品系统

解锁新零售奥秘&#xff0c;多商户外卖超市商品系统大揭秘&#xff01; &#x1f31f; 开篇&#xff1a;新零售时代的浪潮 在这个日新月异的数字化时代&#xff0c;新零售已悄然成为商业变革的新风口。想象一下&#xff0c;足不出户就能逛遍全城商家&#xff0c;心仪商品一键…

力扣——238.移动零

题目 思路 利用双指针&#xff0c;先找到第一个为0的地方指向&#xff0c;指针2指向下一个&#xff0c;指针1之前是已经处理好的数据&#xff0c;指针2进行遍历&#xff0c;遇到非零则与指针1数据交换&#xff0c;然后指针1。 代码 class Solution { public:void moveZeroes(…

离心机转子适配器容量转换器的作用

离心机转子是离心机的核心部件&#xff0c;离心机中的所有系统都配置为保证转子在一定条件下安全运行。转子不仅直接影响分离效果&#xff0c;而且也是离心机技术中的主要承力部件&#xff0c;对离心机的安全性极为重要。 简而言之&#xff0c;离心机可分为两部分&#xff1a;…

Java Web——第二天

什么是JavaScript? JavaScript(简称:JS) 是一门跨平台、面向对象的脚本语言。是用来控制网页行为的&#xff0c;它能使网页可交互 JavaScript和Java是完全不同的语言&#xff0c;不论是概念还是设计。但是基础语法类似 JavaScript在1995年由 Brendan Eich 发明&#xff0c;…

【MySQL】索引概念解析

1.什么是索引&#xff1f; MySQL中的索引是一种数据结构&#xff0c;用于帮助MySQL数据库管理系统快速查询数据。索引的主要目的是提高数据检索的速度&#xff0c;减少数据库系统需要扫描的数据量。 优点&#xff1a; 索引可以极大的提高数据检索效率&#xff0c;降低数据库…

C语言——预处理和指针

C语言——预处理和指针 预处理宏宏定义宏的作用域带参的宏 文件包含条件编译 指针指针的概念指针的定义 预处理 编程的流程分为&#xff1a;编辑、编译、运行、调试四个阶段&#xff1b; 预处理属于编译阶段&#xff0c;编译过程又可以分为&#xff1a;预处理、编译、汇编、链…

TikTok达人效应:品牌出海中的文化桥梁与本土化策略

在全球化的浪潮下&#xff0c;品牌出海已成为企业拓展市场的必经之路。然而&#xff0c;跨越文化差异、实现品牌本土化传播一直是企业面临的巨大挑战。TikTok作为一款全球流行的短视频平台&#xff0c;其庞大的用户基础和强大的影响力&#xff0c;为品牌出海提供了新的机遇。在…

大数据技术复习--大数据与云计算、物联网、人工智能

云计算 ** 概念&#xff1a;美国国家标准技术研究院“一种无处不在的、便捷的且按需的对一个共享的可配置的计算资源&#xff08;如网络&#xff0c;服务器、存储、应用和服务&#xff09;进行网络访问的模式&#xff0c;他能够通过少量的管理或服务供应商的互动实现计算资源的…

CTFHub技能树web——XSS——DOM反射

根据框里的内容 直接右键查看网页源代码 看到 了其闭合方式 然后去网页测试一下alert&#xff08;1&#xff09;反射 ;</script><script>alert(1)</script> 看到 确实存在 去xssaq.cn 创建一个项目 把src粘过来 在第一个输入框中 再将返回回来的url 复…

MATLAB计算心理声学烦恼度例子

在这个例子中&#xff0c;您测量发动机噪音&#xff0c;并使用心理声学指标来模拟其感知响度、尖锐度、波动强度、粗糙度和总体烦扰程度。然后&#xff0c;模拟添加隔音材料&#xff0c;重新计算总体噪音水平。最后&#xff0c;比较恼人程度&#xff0c;并显示应用隔音材料后的…

【LabVIEW学习篇 - 12】:通知器

文章目录 通知器案例一案例二案例三&#xff08;在不同VI中用同一个通知器&#xff09; 通知器 同步技术&#xff1a;同步技术用来解决多个并行任务之间的同步或通信问题。 通知器比较适合一对多的操作&#xff0c;类似于广播&#xff0c;一点发出的通知消息&#xff0c; 其它…

Spring Boot 3.3 新特性介绍

1. 引言 Spring Boot 3.1.x 停止维护了&#xff0c;而 3.3.x 作为最新发布的版本&#xff0c;带来了许多新特性和改进。本篇文章将详细介绍这些新特性&#xff0c;并通过样例代码加以解释&#xff0c;帮助开发者更好地掌握和应用这些新功能。 Spring Boot 3.3现已正式发布&…

Android studio配置代码模版

一、背景&#xff1a; 在工作中&#xff0c;总是要写一些重复的代码&#xff0c;特别是项目有相关规范时&#xff0c;就会产生很多模版代码&#xff0c;每次要么复制一份&#xff0c;要么重新写一份新的&#xff0c;很麻烦&#xff0c;于是我就在想&#xff0c;能不能像创建一…

小程序开发入门:第一天的学习和实践指南

目录 一. 理解小程序的基本概念 1. 无需安装 2. 快速启动 3. 界面简洁 4. 独立性和封闭性 5. 数据安全 6. 框架结构 7. 生命周期 8. 全局配置 9. API支持 10. 发布和更新 二、选择合适的开发工具 1. 微信开发者工具 2. Visual Studio Code 3. Sublime Text 4. …

Tensor安装和测试

1: 打开git官方 https://github.com/NVIDIA/TensorRT 2: 下载得到&#xff1a;TensorRT-10.2.0.19.Linux.x86_64-gnu.cuda-11.8.tar.gz 3: 下载后配置环境变量&#xff0c;上面地址记得改成真实地址。 4: 如果想python使用tensorrt&#xff0c;那么 解压后目录&#xff0c…