0.29 函数:把逻辑打包复用
第〇篇【零基础预科】免费。当一段操作要反复用,就把它做成函数。
前两篇里,条件判断和循环让你能写"一段会分叉、会重复"的代码;函数则让你把这一段代码打包、起个名字、随时喊它。到这一篇,你手里就凑齐了写检测脚本最核心的三块积木:条件、循环、函数。
一、定义与调用
def greet(name): # 定义:参数 name
print("你好,", name)
greet("eason") # 调用:传入 "eason"
greet("tom") # 再调用,复用同一段逻辑def声明函数,括号里是参数(输入);- 定义时不执行,只有调用(名字 + 括号)才执行缩进块。
greet("eason") 和 greet("tom") 跑的是同一段代码,只是塞进去的名字不同。这就像一台榨汁机:结构固定,你丢苹果进去出苹果汁、丢橙子进去出橙汁。函数体是机器,参数是你丢进去的原料,def 只是把机器装好放在那儿,你不按开关(调用),它自己不会转。
你可能会问:我明明写了
def greet(...),为什么运行后什么都没打印? 因为def那一行只是"定义"——它告诉 Python"有这么个函数",并不执行。你必须再写一句greet("eason")真正去调用它,机器才转。新手最常犯的错就是"只定义、忘了调用",然后盯着屏幕纳闷"代码呢"。看到没反应,先检查函数名后面到底有没有加括号调用。
函数名最好起得"见名知意"。greet 一看就知道在打招呼,is_vulnerable 一看就知道在判断"是否有漏洞"——名字本身就是注释。别图省事起成 a、func1、test,过三天你自己都忘了它干嘛。这个习惯现在养,后面读自己半年前写的脚本时会谢自己。
二、返回值:return
def add(a, b):
return a + b # 把结果交回
result = add(3, 5) # result = 8
print(result)return把函数产出的结果返回,调用处可接收;- 没有 return 的函数返回
None; - 执行到 return 立即结束函数。
print 和 return 的区别,是这里最该搞清的一对。print 只是把结果显示在屏幕上给你看,看完就没了,别的代码拿不到;return 才是真把结果"交回"给调用处,你能拿它存进变量、继续往下算。比如 add(3,5) 如果只是在函数里 print(8),外面写 result = add(3,5) 会发现 result 是空的(None),因为它根本没把 8 交出来。想让结果能被复用,就得用 return。
None 这个词先混个眼熟:它表示"什么都没有/空"。一个函数没写 return,调用它就默默返回 None,不报错——但你一拿它去做运算,就会报错。知道有这个东西,第一次碰到 TypeError: unsupported operand... None 时不会完全懵。
三、为什么要用函数
- 复用:写一次,到处调用;
- 清晰:用函数名概括一段操作(如
check_vuln(url)),主流程像在读步骤; - 易维护:改逻辑只改函数一处;
- 易测试:给定输入、检查输出是否正确。
这四点值得各举一个真实感受。"复用"最直接:你写好了一段发请求、判特征的代码,后面十个目标都要用,复制十遍太蠢,封成函数调用十次就行。"清晰"体现在读代码上——主流程里你看到 is_vulnerable(url),根本不用关心它内部怎么实现,光看名字就知道"它在查这个网址有没有漏洞";如果不封函数,主流程里直接摊着几十行发请求、取响应、查字符串的细节,你读三步就被绕晕了。
"易维护"是事后才懂的痛:特征串要从 rmi:// 换成别的,散在十处就得改十次,还容易漏改一处;封在函数里,改一处全生效。"易测试"则让排错变简单:怀疑检测逻辑有问题,不用跑整个批量,单独喂它一个已知有漏洞的地址、一个正常地址,看返回对不对,立刻能定位是逻辑错了还是网络问题。这四条合起来就是一句话:函数让长代码变得像拼积木,而不是一团乱麻。
四、安全脚本里的函数
def is_vulnerable(url):
# response = requests.get(url, timeout=5)
# return "特征" in response.text
return False
targets = ["http://a.com", "http://b.com"]
for url in targets:
if is_vulnerable(url): # 对每个目标调用检测函数
print(url, "可能存在漏洞")这是非常典型的结构:把"检测单个目标"写成函数,再用循环对多个目标调用。
你体会一下这个拆分:0.28 的循环负责"一个个轮流来",函数负责"对单个目标怎么查"。两者各管一件事——循环管重复,函数管逻辑。这样写的好处立刻显现:哪天你要升级检测逻辑(比如多加一个特征判断),只改 is_vulnerable 这一个函数,循环那边一行都不用动;想单独测某个目标行不行,直接 is_vulnerable("http://test.local") 就能调,不用跑整个批量。这就是"分而治之"在代码里的样子。
五、常见问题
- 定义后忘记调用 → 没反应;
- 调用时参数个数对不上 →
TypeError; - 函数内想改外部变量需注意作用域(初学时多用参数传入、return 返回)。
第二条看看真实报错长什么样:def add(a, b) 需要两个参数,你只写 add(3),运行就报 TypeError: add() missing 1 required positional argument: 'b'——翻译过来就是"add 还缺一个叫 b 的参数没给"。报错本身会告诉你缺哪个,照着补就行。第三条先不深究,你只要养成习惯:要给函数什么,用参数传进去;函数干完想留什么,用 return 交出来,就基本不会踩作用域的坑。等你真需要在函数里改外部变量了,再回头查"作用域"也不迟。
六、本篇小结
- 函数用 def 定义、可接参数、用 return 返回结果。
- 定义不执行,调用(加括号)才执行。
- 函数带来复用、清晰、易维护和易测试。
- 安全脚本:检测单目标写成函数,循环批量调用。
到这里,条件、循环、函数三块积木你都齐了。别小看它们——后面所有检测脚本,都是这三样的排列组合。
下一篇 0.30 解释"解释型"和"编译型"语言的区别。