尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python 学习日志(8.27)写函数、跨文件引用

Python 学习日志(8.27)写函数、跨文件引用 文章目录Python 学习日志8.27一、为什么要自己写函数、跨文件引用二、在 math1 里面写函数三、主文件引用函数的三种方式四、测试代码与 __name__踩坑预警不加 __name__ 判断会怎样把 __main__ 改成别的会怎样五、函数传值的类型检查raise 和 print/return 的区别常见的异常类型多个 if 还是单个 if六、文件结构Python 学习日志8.27学习教程廖雪峰的 Python 教程 - 函数一、为什么要自己写函数、跨文件引用Ok啊朋友们今天来学习自己写函数和跨文件引用。上次讲了一些常用的内置函数接下来讲讲自己写函数和跨文件引用。函数全都写在一个文件里面第一个问题就是文件太大了而且很多时候函数不一定会全程用到并且也不利于维护所以分开代码逻辑十分重要。例如老师留下的作业请定义一个函数quadratic(a, b, c)接收 3 个参数返回一元二次方程ax² bx c 0的两个解。提示一元二次方程的求根公式为x (−b ± √(b² − 4ac)) / 2a计算平方根可以调用math.sqrt()函数。importmathmath.sqrt(2)1.4142135623730951importmathdefquadratic(a,b,c):pass# 测试:print(quadratic(2, 3, 1) ,quadratic(2,3,1))print(quadratic(1, 3, -4) ,quadratic(1,3,-4))ifquadratic(2,3,1)!(-0.5,-1.0):print(测试失败)elifquadratic(1,3,-4)!(1.0,-4.0):print(测试失败)else:print(测试成功)那么我们可以新建一个函数文件叫math1。二、在 math1 里面写函数然后我们在 math1 里面写函数importmathdefjueduizhi(x):ifx0:return-xelse:returnxdefpingfanggen(a,b,c):ifa0:print(被除数小于0)returnNoneelse:d(b*b)-(4*a*c)e1-b-(math.sqrt(d))e2-b(math.sqrt(d))x1e1/(2*a)x2e2/(2*a)returnx2,x1这里要提醒几个事情import math是为了引入 Python 自带的数学函数因为我们要开根号函数格式是def 函数名参数: 函数逻辑最后一定要return函数结果不然函数写了等于没有写两个文件要放在同一个文件夹Python 默认只在当前目录找模块文件模块名不能以数字开头比如2gongju.py会报错。可以这么理解Python 找模块就像你去楼下快递柜取件快递柜只在你家楼下你得把快递文件放在同一个小区文件夹里才取得到。三、主文件引用函数的三种方式接下来是主代码文件里面写的代码importmath1frommath1importpingfanggen# from math1 import *# 测试:print(pingfanggen(2, 3, 1) ,pingfanggen(2,3,1))print(pingfanggen(1, 3, -4) ,pingfanggen(1,3,-4))ifpingfanggen(2,3,1)!(-0.5,-1.0):print(测试失败)elifpingfanggen(1,3,-4)!(1.0,-4.0):print(测试失败)else:print(测试成功)其中import math1、from math1 import pingfanggen、from math1 import *是三种函数的引用方式引用方式含义调用方法import math1导入整个模块用模块名.函数名调用from math1 import pingfanggen只引入 math1 里面的 pingfanggen 函数直接用函数名调用from math1 import *引入 math1 里面的所有函数直接用函数名调用第一种方式例如importmath1 resultmath1.pingfanggen(1,-3,2)print(result)日常用第二种就好简洁高效。那么上面的函数结果是pingfanggen(2, 3, 1) (-0.5, -1.0) pingfanggen(1, 3, -4) (1.0, -4.0) 测试成功四、测试代码与name踩坑预警接下来是一个课外知识。测试代码是必不可少的一般情况下我们会删掉或者注释但是如果文件里面写了测试代码因为import的时候被导入文件里的代码会全部执行一遍。⚠️ 踩坑预警如果我们的函数文件里有测试代码main.py 一导入也会跑比如上面的平方根函数在引入主函数之前想先试试函数效果怎么样那么我们可以直接在 math1 里面写print(pingfanggen(1,-3,2))这当然是允许的但是有一个问题会占用计算资源而且也会输出出来因为 import 的时候他也会被 Python 执行一遍。小的项目没有什么问题但是大型项目里面大家都写测试代码那么输出就乱七八糟的但是我们又不想注释或者删掉怎么办我们可以在 math1 文件里面这样写if__name____main__:# 只有直接运行 pingfanggen.py 时才执行这里print(pingfanggen(1,-3,2))这样的话如果 math1 文件是被引用后执行的那么就不会运行print(pingfanggen(1, -3, 2))。详细讲讲就是这个意思一个 .py 文件有两种命运命运什么时候发生__name__的值直接运行你点运行按钮或python pingfanggen.py__main__被导入别的文件import pingfanggenpingfanggen模块名__name__是 Python 偷偷塞在每个文件里的隐藏变量它自己会知道我现在是被直接运行还是被别家 import 了。所以if __name__ __main__:翻译成人话就是“如果我是被直接运行的才执行下面这段测试代码如果我是被别的文件借走的就别捣乱。”不加name判断会怎样例如如果不加这个特定语句我们的 math1 里面写importmathdefjueduizhi(x):ifx0:return-xelse:returnxdefpingfanggen(a,b,c):ifa0:print(被除数小于0)returnNoneelse:d(b*b)-(4*a*c)e1-b-(math.sqrt(d))e2-b(math.sqrt(d))x1e1/(2*a)x2e2/(2*a)returnx2,x1print(pingfanggen(1,-3,2))那么回到主文件运行就会输出(2.0, 1.0) pingfanggen(2, 3, 1) (-0.5, -1.0) pingfanggen(1, 3, -4) (1.0, -4.0) 测试成功但是如果我们加上__name__判断变成importmathdefjueduizhi(x):ifx0:return-xelse:returnxdefpingfanggen(a,b,c):ifa0:print(被除数小于0)returnNoneelse:d(b*b)-(4*a*c)e1-b-(math.sqrt(d))e2-b(math.sqrt(d))x1e1/(2*a)x2e2/(2*a)returnx2,x1# print(pingfanggen(1, -3, 2))if__name____main__:# 只有直接运行 gongju.py 时才执行这里print(pingfanggen(1,-3,2))那么回到主文件运行就会输出pingfanggen(2, 3, 1) (-0.5, -1.0) pingfanggen(1, 3, -4) (1.0, -4.0) 测试成功这样我们的测试代码就不会被运行出来了。把 ‘main’ 改成别的会怎样肯定有小伙伴问那如果我把 ‘main’ 改成别的会怎样例如if__name__math1:# 意思变成当我被 math1这个名字导入时当然不会怎么样。__name__是 Python系统自动决定的隐藏变量不是你去填它而是它自己根据运行方式变身。运行方式__name__的值直接运行这个文件__main__被别的文件 import模块名就是文件名不带 .py例如print(我的 __name__ 是,__name__)然后直接运行math1.py→ 输出我的 __name__ 是 __main__在另一个文件里import math1→ 输出我的 __name__ 是 math1同一个变量两种命运Python 自己切换我们什么都不用做。所以那对单引号的意义是if__name____main__:# 问我现在是被直接运行吗就像门卫问你“你是本楼住户吗”——答案只能是是__main__或不是其他。你不能给门卫安排一个新答案只能等他回答。所以之前的if __name__ math1:语法上没错但实际没人这么写——因为我们没法控制别人用什么名字导入文件这个判断基本不会生效。约定俗成这个位置就是写__main__背下来就行不用想别的。反正就是总结成一句话__main__是系统发的本楼住户门禁卡你只管拿它来对比不用管它从哪来。顺带一提__name__这种前后双下划线的变量叫魔法变量Python 里还有很多比如__file__表示当前文件路径、__doc__表示文档说明以后遇到了知道带双下划线的都是系统级的不用自己定义就行。五、函数传值的类型检查然后是函数传值的检查。大家一定听过程序员做了一个酒吧然后有个顾客进来点了份炒饭后程序爆炸的笑话但实际上就是我们真的不知道其他人会传什么东西进入函数所以我们可以主动控制函数里面到底什么东西可以过去。例如defjueduizhi(x):ifnotisinstance(x,(int,float)):raiseTypeError(bad operand type)elifx0:return-xelse:returnx这里可以用isinstance判断判断是不是我们不允许通过的类型。这里我们选择只能传入int和float如果用户传了个字符串进来这里isinstance输出不是整数和浮点数达到我们的判断条件就会直接中断程序并主动报错。raise 和 print/return 的区别这里使用了raise而不是print区别就在于print或者return那么函数还是会继续进行下去直到最后才会报错而raise会直接强制中断程序把错误信号抛出去程序正常流程被打断。打个比方return是外卖小哥正常把餐送到你手上交易完成raise是小哥发现餐盒洒了直接按响警报而不是硬着头皮把洒了的餐送给你——警报一响周围所有人都停下要么有人来处理try/except要么整个店歇业程序崩溃。一句话总结return是事办成了交结果raise是事办砸了喊救命。办砸了不能假装办成这就是为什么类型不对时用raise而不是return一个奇怪的值。常见的异常类型就算我们没写 raise有些情况 Python 也会自动报错例如平方根里面有 abc 三个参数但是只传两个进去就会自动报错。以下是一些常见的错误除了 TypeError还有一大家子异常类型Python 内置的异常名什么时候出现直观例子TypeError类型不对1 1ValueError值不合法类型对值不对int(abc)字符串转整数失败NameError名字没定义/拼写错用了没定义的变量IndexError下标越界L [1,2,3]; L[10]KeyError字典键不存在d {}; d[xxx]ZeroDivisionError除以零1 / 0FileNotFoundError文件不存在open(不存在的.txt)AttributeError调用不存在的属性/方法abc.append(1)字符串没有 append其中 TypeError 和 ValueError 最容易混淆——记住类型不对是 TypeError比如拿刀去切水值不对是 ValueError比如拿刀切空气。多个 if 还是单个 if上面代码实际上就可以写成defjueduizhi(x):ifnotisinstance(x,(int,float)):raiseTypeError(bad operand type)ifx0:return-xelse:returnx区别就在于是使用一个if还是多个if正常情况下都是多个if因为简单直观知道错误条件便于维护写在一个 if 里面容易逻辑混淆。因为raise会直接中断程序不会进行下一步的代码解析了所以可以放心的使用多个if。六、文件结构Cpythoncode D:\yingyong\pyhton\pythoncode .venv main.py math1.py
返回列表