
在书写至这一章节的时候, 你已然能够使得程序去进行数据的保存操作, 对列表以及字典予以处理, 接收来自用户的输入信息, 依据条件作出相应的判断, 借由循环来反复地执行任务了。但若是这些逻辑, 每一次都会直接铺陈于一个文件当中, 那么代码便会急速变长, 进而变乱, 并且诸多步骤还会持续不断地重复出现。比如问候用户这件事你可能会写很多次根据输入生成提示语这件事也可能会写很多次要是同一段代码, 在不一样的地方反复地出现, 那就表明, 它已然值得, 被单独收纳起来了。函数正是为了解决这个问题。它所拥有的价值, 并非在于多学习一个语法方面的要点, 而是在于能够使得程序开始拥有更进一步清晰明了堪称结构上的条理。从这儿起始, 你将会逐步碰到, 那种把重复的逻辑聚集在一起, 而后依照需求不断进行调用的写法。8.1 定义函数函数可以先理解成一段“起了名字的可重复步骤”。先是你把这一段步骤给定义好, 之后在后面程序需要用到它的时候, 就能够直接去调用, 而不用每一次都重新书写一遍。这和变量有点像。变量是给一份数据起名字方便以后继续使用函数则是给一段操作流程起名字方便以后继续执行。示例代码def greet_user(): print(你好欢迎学习 Python) greet_user()逐行解释第 1 行使用 def 开始定义函数函数名叫 。较靠后的那个圆括号, 当下呈现为空, 意味着该函数在当前阶段, 无需从外部把额外的数据传递进来。第2行, 是函数体, 这函数真正会完成的事情是, 输出一条欢迎语。函数在第4行是被调用的状态。唯有经历该调用这一行为之后之前已经定义好的那部分逻辑才会切实地去执行, , 此为一种先后顺序的规律。运行结果你好欢迎学习 Python新手提醒很多人第一次学函数时会以为“定义出来就自动运行”。其实不是。定义函数只是把逻辑收起来真正让它执行还要靠后面的调用。本节小结函数的第一层含义, 是将一段在未来有可能会被重复运用的操作, 进行单独的命名, 然后集中起来予以保存。8.2 向函数传递信息前边的那个 () 虽说能够起到效果, 然而它每一次仅仅只能给出同样的一条固定的欢迎话语。确实真正具备效用的函数, 一般来讲并非始终对固定内容予以处理, 而是能够依照传递进来的数据进行各异的输出。这也是函数开始真正变灵活的地方。你能够将函数视作一个进行加工的工具, 外部率先把原料交付给它, 它接着依据这份原料展开处理。示例代码def greet_user(username): print(f你好{username}) greet_user(小林)逐行解释第 1 行定义函数时在圆括号里写了一个名字 。它表示这个函数运行时需要外部先提供一份数据。第 2 行在函数体内部使用这个数据把它放进问候语里。第 4 行调用函数并把 小林 传进去。因此函数实际运行时 里装的就是 小林。运行结果你好小林新手提醒这里最重要的不是术语而是关系定义函数时写在括号里的是“等着接收的数据位置”调用函数时放进去的才是真正送进来的值。本节小结函数一旦能够接纳外部数据, 便不再单单是固定输出某一句话, 而是开始拥有通用性。8.2.1 形参和实参这一节的名称, 听起来带有那么一点儿术语的味道, 然而实际上呢, 能够运用极为直白的方式去加以理解。制订函数之际, 括弧以内的名称, 便如同什么似的那个, 就仅仅是一种“占位标记”, 等待着往后实际的数值进入。发起函数调用之际, 传进去的那个称做 小林 的部分, 才是于当次实际被用作的内容。所以你完全可以这样记1. 定义时先留位置。2. 调用时再把真实值送进去。示例代码def describe_pet(animal_type, pet_name): print(f\n我有一只{animal_type}。) print(f它的名字叫 {pet_name.title()}。) describe_pet(仓鼠, harry)逐行解释第 1 行定义函数时写了两个位置 和 。函数内部, 使用这两个值, 在第2行, 在第3行, 将其组织成自然语言在第5行, 当对函数进行调用之际, 将 仓鼠 这个内容按照顺序传递进去, 同时呢, 把 harry 也依照顺序传递进去。因而, 在函数运行之际, 于第一个位置所接到的是动物的类型, 而于第二个位置所接到的是宠物的名字。本节小结领会“先占位、后传值”这一表述之后, 函数的参数体系便不会显得难以理解了。8.3 位置实参当你对函数进行调用之际, 要是直接将多个值依照顺序传递进去, 那么函数会按照位置逐个且一一对应去接收它们的。这就是最常见的位置实参写法。它的好处是直观缺点是一旦顺序写反函数虽然可能还能运行但意思已经变了。示例代码def describe_pet(animal_type, pet_name): print(f\n我有一只{animal_type}。) print(f它的名字叫 {pet_name.title()}。) describe_pet(小狗, huanhuan)逐行解释做函数定义这一行为时候, 将第一个位置用来留给动物类型把第二个位置用于留给宠物名字。进行调用之际, “小狗”会率先进入首个位置, 之后, 空字符串会进入第二个位置。因为顺序匹配正确所以程序输出的含义也是正确的。新手提醒位置实参最忌惮的并非代码出现报错情况, 而是当顺序被写反之后, 会出现“代码依旧能够运行, 然而其表达的意思却是错误的”这种状况。所以调用函数时顺序一定要和定义时保持一致。本节小结位置实参的核心规则很简单函数定义里怎么排调用时就按这个顺序把值送进去。8.3.1 关键字实参要是你不打算单单凭借顺序去传递值, 那在进行调用的时候, 也能够把“谁对应谁”这种情况写得更为明晰。这样做的好处是可读性更强也不容易因为顺序搞错意思。示例代码def describe_pet(animal_type, pet_name): print(f\n我有一只{animal_type}。) print(f它的名字叫 {pet_name.title()}。) describe_pet(animal_type仓鼠, pet_nameharry)逐行解释函数定义部分不变。调用时没有只按位置放两个值而是明确写出 对应 仓鼠 对应 harry。像这样, 哪怕往后参数数量增多, 当别人看到这段调用的时候, 也能够更加容易地在一瞬间就弄清楚其中的意思。本节小结非是为使写法更具花哨之态的关键字参数的有益之处, 乃是为让参数之间的对应关联变得更明晰透彻。8.3.2 默认值有些函数参数在大多数情况下都取同一个值。如果每次调用都要重复写出来就会显得累赘。这时候可以给参数设置默认值。示例代码def describe_pet(pet_name, animal_type小狗): print(f\n我有一只{animal_type}。) print(f它的名字叫 {pet_name.title()}。) describe_pet(doudou) describe_pet(mimi, animal_type小猫)逐行解释在第1行进行函数定义之际, 将 的默认值设定成了 小狗。这表明, 要是在调用函数之际没有额外去提供动物的类型, 那么程序就会自动地选用 小狗。第 5 行只传了宠物名字因此函数会采用默认动物类型。第6行, 主动进行了 小猫 的指定 , 因而, 默认值会被新的值所替代。新手提醒默认值的作用是减少重复不是让逻辑变模糊。设有某个参数, 在大多数场景之中呈现为各异且有别情况的话, 就不太适宜针对其随意去设定一个默认值来作为规定了。本节小结默认值让函数在常见场景下更省事也让调用代码更简洁。8.4 返回值前面学的函数大多是“做完一件事后把结果打印出来”。但这还不够。更多情形下, 你并非仅仅期望瞧一下结果, 而是渴望将该结果交付给后续的代码续接着运用。这时就需要返回值。返回值的意义是函数在完成自身所负责的工作之后, 并非仅仅是将结果进行一下显示那么简单, 而是要把结果切实地交回到函数外部去。示例代码def get_formatted_name(first_name, last_name): full_name f{first_name} {last_name} return full_name.title() musician get_formatted_name(jimi, hendrix) print(musician)逐行解释第 1 行定义函数准备接收名和姓。第 2 行先把名和姓拼起来保存到变量 。第 3 行使用 把整理好的姓名结果交回函数外部。第 5 行调用函数并把返回结果保存到变量 。第 6 行再把这个返回值输出出来。运行结果Jimi Hendrix新手提醒要分清两种情况1. print() 只是把结果显示出来。2. 是把结果真正交回外部继续使用。后者才是让函数能参与更复杂程序结构的关键。本节小结函数的返回值, 使得函数从“会去执行一段动作”, 提升到了“能够生产出一个结果, 交给后面继续使用”。8.5 传递列表函数不只可以接收单个值也可以接收整个列表。这很重要因为很多批量处理任务本来就建立在列表基础上。例如, 对姓名列表展开遍历, 而后逐个进行问候, 针对订单列表予以处理, 接着逐个实施打印, 对文件名列表加以整理, 随后逐个更改格式。示例代码def greet_users(names): for name in names: msg f你好{name.title()} print(msg) usernames [hannah, ty, margot] greet_users(usernames)逐行解释第 1 行定义函数准备接收一个列表 names。第 2 行开始循环依次取出列表中的每个名字。第 3 行把当前名字组织成问候语。第 4 行把这条问候语输出出来。第 6 行创建一个用户名列表。第 7 行把整个列表传进函数让函数统一处理。本节小结将函数与列表连接起来后, 你便能够把“批量处理一组数据”的逻辑, 整个收纳进一个清晰的函数之中。8.5.1 在函数中修改列表这里有一个很重要的现象必须尽早知道要是在函数之中直接对传进来的列表予以修改, 那么在函数外部原本的那个列表通常而言也会随之发生变化。这既很有用也需要格外小心。有用的地方在于你可以让函数真正参与数据处理有需要小心之处, 倘若你未觉察到这一点, 那么便有可能于毫无察觉之时改了原始数据。本节小结把列表传给函数时要始终想清楚我到底是想让它“只读取”还是“顺带修改”。8.6 传递任意数量的实参有些函数并不知道以后会收到几个值。比如, 当一个用户去点披萨的时候, 其配料的数量, 有可能是两个, 也存在着是五个的这种情况。一个程序收集标签时标签数量也不固定。这时就需要让函数具备“接收任意多个值”的能力。示例代码def make_pizza(*toppings): print(toppings) make_pizza(蘑菇) make_pizza(蘑菇, 青椒, 额外芝士)逐行解释第 1 行定义函数时在参数名前面加了一个 *。它作出这样的表明: 此函数能够接纳任意数量的位置实参, 且将它们统一收纳聚合起来。第 2 行直接把收集到的内容打印出来。第 4 行只传一个值函数也能接住。第 5 行传三个值函数同样也能接住。本节小结这种写法的核心不是记住星号而是理解函数参数数量有时并不是固定的程序需要更灵活地接收输入。8.7 将函数存储在模块中写到这里你已经能感受到一个问题函数数量逐渐增多之后, 要是全部堆积在同一个文件当中, 那么管理起来就会越发混乱。所以函数再往后走一步就会进入“模块”的世界。模块可以简单理解成把一组相关函数单独放进一个文件里后面需要时再导入。这会让代码结构更清晰也更容易复用。本节小结函数所处理的是, 那“怎样将重复逻辑收纳起来”的事宜 , 模块所处理的是, 那“在收纳之后放置于何处会更妥当合适”的问题。8.8 本章总结这一章节之中, 最大程度的提升之处, 并非在于能够写出几个def, 而是在于程序开始拥有了一种相较于之前更为清晰些的组织方式。你现在已经接触了1. 如何定义函数。2. 如何把值传给函数。3. 位置实参、关键字实参和默认值。4. 如何通过返回值把结果交回外部。5. 如何把列表传给函数。6. 如何接收任意数量的参数。7. 函数和模块之间的关系。从这儿起始, 你所编写的程序, 会逐步地, 从“将所有事物径直书写在一起”, 转变至“把逻辑依照块来进行组织”。这恰恰是, 后续所面向的对象, 对于文件的处理, 还有相关的测试, 以及项目开发时, 真正切实需要的那个基础呀。