news 2026/9/9 8:38:14

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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()函数。

>>>importmath>>>math.sqrt(2)1.4142135623730951
importmathdefquadratic(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):ifx<=0:return-xelse:returnxdefpingfanggen(a,b,c):ifa<0:print('被除数小于0!')returnNoneelse:d=(b*b)-(4*a*c)e1=-b-(math.sqrt(d))e2=-b+(math.sqrt(d))x1=e1/(2*a)x2=e2/(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 math1from math1 import pingfanggenfrom math1 import *是三种函数的引用方式:

引用方式含义调用方法
import math1导入整个模块模块名.函数名调用
from math1 import pingfanggen只引入 math1 里面的 pingfanggen 函数直接用函数名调用
from math1 import *引入 math1 里面的所有函数直接用函数名调用

第一种方式例如:

importmath1 result=math1.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):ifx<=0:return-xelse:returnxdefpingfanggen(a,b,c):ifa<0:print('被除数小于0!')returnNoneelse:d=(b*b)-(4*a*c)e1=-b-(math.sqrt(d))e2=-b+(math.sqrt(d))x1=e1/(2*a)x2=e2/(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):ifx<=0:return-xelse:returnxdefpingfanggen(a,b,c):ifa<0:print('被除数小于0!')returnNoneelse:d=(b*b)-(4*a*c)e1=-b-(math.sqrt(d))e2=-b+(math.sqrt(d))x1=e1/(2*a)x2=e2/(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')elifx<=0:return-xelse:returnx

这里可以用isinstance判断,判断是不是我们不允许通过的类型。这里我们选择只能传入intfloat,如果用户传了个字符串进来,这里isinstance输出不是整数和浮点数,达到我们的判断条件,就会直接中断程序并主动报错

raise 和 print/return 的区别

这里使用了raise而不是print,区别就在于print(或者return),那么函数还是会继续进行下去,直到最后才会报错,而raise直接强制中断程序,把错误信号抛出去,程序正常流程被打断。

打个比方:

  • return是外卖小哥正常把餐送到你手上,交易完成;
  • raise是小哥发现餐盒洒了,直接按响警报(而不是硬着头皮把洒了的餐送给你)——警报一响,周围所有人都停下,要么有人来处理(try/except),要么整个店歇业(程序崩溃)。

一句话总结:return是"事办成了,交结果";raise是"事办砸了,喊救命"。办砸了不能假装办成,这就是为什么类型不对时用raise而不是return一个奇怪的值。

常见的异常类型

就算我们没写 raise,有些情况 Python 也会自动报错,例如平方根里面有 abc 三个参数,但是只传两个进去,就会自动报错。以下是一些常见的错误:

除了 TypeError,还有一大家子异常类型(Python 内置的):

异常名什么时候出现直观例子
TypeError类型不对'1' + 1
ValueError值不合法(类型对,值不对)int('abc')字符串转整数失败
NameError名字没定义/拼写错用了没定义的变量
IndexError下标越界L = [1,2,3]; L[10]
KeyError字典键不存在d = {}; d['xxx']
ZeroDivisionError除以零1 / 0
FileNotFoundError文件不存在open('不存在的.txt')
AttributeError调用不存在的属性/方法'abc'.append(1)字符串没有 append

其中 TypeError 和 ValueError 最容易混淆——记住"类型不对是 TypeError(比如拿刀去切水),值不对是 ValueError(比如拿刀切空气)"。

多个 if 还是单个 if

上面代码实际上就可以写成:

defjueduizhi(x):ifnotisinstance(x,(int,float)):raiseTypeError('bad operand type')ifx<=0:return-xelse:returnx

区别就在于是使用一个if还是多个if,正常情况下都是多个if,因为:

  1. 简单直观知道错误条件,便于维护;
  2. 写在一个 if 里面容易逻辑混淆。

因为raise会直接中断程序,不会进行下一步的代码解析了,所以可以放心的使用多个if


六、文件结构

Cpythoncode D:\yingyong\pyhton\pythoncode .venv main.py math1.py
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 8:37:40

ROSS Harness与人形机器人全自主模式:工业场景落地的关键闭环

这次我们看的是 ROSS Harness。按照项目标题与公开信息&#xff0c;它已经进入“世界人形机器人运动会”工业场景赛全国前三&#xff0c;核心打法是“全自主模式”。这个名字听起来偏比赛向&#xff0c;但它要解决的问题非常实际&#xff1a;人形机器人进工业场景&#xff0c;过…

作者头像 李华
网站建设 2026/9/9 8:38:04

快手测试岗笔试复盘:从真题拆解到备考路径

快手2019春招测试岗笔试&#xff0c;我刷完真题后总结的这套复盘思路又到一年春招季&#xff0c;后台不少准备投测试岗的同学私信我&#xff0c;问快手的笔试题到底考什么、难度如何、该怎么准备。我手头正好存了一套快手2019年春季校园招聘的测试A试卷&#xff0c;虽然年份早了…

作者头像 李华
网站建设 2026/9/9 8:36:49

[MySQL] SQL优化之性能分析

??键盘敲烂&#xff0c;年薪30万??目录一、索引优化1、索引是什么&#xff1a;2、索引的数据结构&#xff1a;3、索引种类&#xff1a;4、sql分析&#xff08;回表查询&#xff09;二、定位慢查询语句1、慢查询日志2、profile详情3、explain执行计划&#xff08;重点&#…

作者头像 李华
网站建设 2026/9/4 16:17:38

在线考试答题系统架构设计:一套底层支撑考试、刷题、竞赛与活动

简介&#xff1a;这是一套面向教育机构、培训平台及知识竞赛组织者的在线考试答题系统源码&#xff0c;适用于考试测评、日常刷题、活动竞答与题库建设等多场景&#xff0c;兼顾教师出题管理与考生作答体验。资源包共2000个文件&#xff0c;主体为10443个PHP后端逻辑文件&#…

作者头像 李华
网站建设 2026/9/6 2:29:40

卡池故障排查指南:从现象到根因的五层定位法

“残虹姐刚才外边人多&#xff0c;卡池的事拜托了&#xff01;”这句话如果放在一个鉴宝故事里&#xff0c;意思很清楚&#xff1a;人多眼杂&#xff0c;不适合谈真事&#xff0c;等私底下再细细看。如果把它放到技术日常里&#xff0c;它其实精准描述了很多线上问题处理的真实…

作者头像 李华
网站建设 2026/9/5 16:39:24

【MySQL】MySQL数据库安装以及报错处理技巧

前言&#xff1a; 本节内容讲述在Ubuntu环境下怎么进行MySQL的安装。 以及一些安装过程中遇到的报错如何处理的问题。> > ps:注意&#xff0c; 本篇文章不是图形化界面的MySQL安装教程哦。想要安装图形化界面的MySQL的友友们可以另寻资源了。目录更新软件包列表安装MySQL…

作者头像 李华