news 2026/9/24 17:44:20

函数:写一次代码,用一万次

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
函数:写一次代码,用一万次

如果你连函数都写不出来, 那么你根本就没有进入到这门学问的门槛里面去。

函数, 它是编程里面的那个核心的抽象。你现在去写一个函数, 这就等于你去发明了一个新的动词。那其他的人, 包括那个未来的你自己在内, 也只需要去知道它是做什么事情的就行了。至于说它到底是怎么去做的这个问题, 是不需要去知道的。

今天我们将由浅入深, 从最基础的知识逐步过渡到更高级的内容, 把函数这个知识点讲透彻。

一、我们来了解一下关于函数的第一点一内容, 具体来说就是大家经常会问到的问题, 即究竟为什么要使用函数呢?

这段代码不需要用到函数。

# 计算三个圆的面积

r1, r2, r3 = 5, 10, 15

area1 = 3.14159 * r1 ** 2

area2 = 3.14159 * r2 ** 2

area3 = 3.14159 * r3 ** 2

# 如果π需要更高精度,你要改3个地方...

使用函数这一类事物的代码内容来展开表述。

def circle_area(radius, pi=3.14159):

"""计算圆的面积"""

return pi * radius ** 2

areas = [circle_area(r) for r in [5, 10, 15]]

# 改精度只需改一个地方

函数这种存在, 它的益处在哪里呢?

二、我们来定义和调用, 然后看一看2.1基本语法。

def greet(name):

"""向指定的人打招呼""" # 这是文档字符串(docstring)

return f"你好,{name}!"

# 调用

result = greet("小明")

print(result) # 你好,小明!

2.2是关于文档这一方面的字符串内容。

这个内容应当被视为函数的说明书, 它需要去清楚地说明函数究竟是做什么工作的, 其中的参数分别是什么情况, 以及最终的返回值又是什么东西。

def calculate_bmi(height, weight):

"""

计算BMI值并给出健康建议

Args:

height: 身高(米)

weight: 体重(公斤)

Returns:

dict: 包含BMI值和健康建议

Example:

>>> calculate_bmi(1.75, 70)

{'bmi': 22.86, 'advice': '正常'}

"""

bmi = weight / (height ** 2)

if bmi < 18.5:

advice = "偏瘦"

elif bmi < 24:

advice = "正常"

elif bmi < 28:

advice = "偏胖"

else:

advice = "肥胖"

return {"bmi": round(bmi, 2), "advice": advice}

好习惯:写 !它可以通过 help(函数名) 查看,配合 IDE 自动提示,是代码可读性的核心。

2.第三部分是关于语句这一内容的介绍。

# 返回一个值

def add(a, b):

return a + b

# 返回多个值(自动打包成元组)

def min_max(numbers):

return min(numbers), max(numbers)

result = min_max([3, 1, 4, 1, 5])

print(result) # (1, 5)

mn, mx = result # 解包

print(mn, mx) # 1 5

# 没有 return 返回 None

def print_hello():

print("Hello") # 没有 return

result = print_hello() # Hello

print(result) # None

三、关于3.1这个位置参数的详细解释如下。

def introduce(name, age, city):

print(f"我叫{name},今年{age}岁,住在{city}")

# 按位置传参

introduce("小明", 18, "北京")

3.2 关键字参数

# 按名称传参(顺序可以打乱)

introduce(city="上海", name="小红", age=20)

# 混合使用:位置参数必须在关键字参数前面

introduce("小刚", city="广州", age=22) #

# introduce(city="广州", "小刚", 22) # SyntaxError

3.3 默认参数

def connect(host="localhost", port=8080, timeout=30):

print(f"连接 {host}:{port},超时{timeout}秒")

connect() # 连接 localhost:8080,超时30秒

# 连接 example.com:8080,超时30秒

connect(port=9000) # 连接 localhost:9000,超时30秒

443, 10) # 全部自定义

️ 默认参数的陷阱:

# 大坑!

默认参数只会在定义时计算一次

def add_item(item, items=[]):

items.append(item)

return items

print(add_item("a")) # ['a']

print(add_item("b")) # ['a', 'b'] ← 不是预期的 ['b']!

# 正确做法:用 None + 在函数内部创建

def add_item(item, items=None):

if items is None:

items = []

items.append(item)

return items

3.第四个知识点是关于可变参数。

# *args:接收任意数量的位置参数(打包成元组)

def sum_all(*args):

print(f"参数:{args}")

return sum(args)

print(sum_all(1, 2, 3, 4, 5)) # 15

# **kwargs:接收任意数量的关键字参数(打包成字典)

def print_kwargs(**kwargs):

for key, value in kwargs.items():

print(f"{key} = {value}")

print_kwargs(name="小明", age=18, city="北京")

# name = 小明

# age = 18

# city = 北京

# 组合使用

def api_request(url, *args, method="GET", **kwargs):

print(f"请求:{method} {url}")

print(f"额外参数:{args}")

print(f"查询参数:{kwargs}")

api_request("/api/data", "extra", method="POST", page=1, size=20)

3.5 关键字仅可用的参数这一内容, 总共包含了 3 个特性。

# * 后面的参数必须用关键字传递

def create_user(name, age, *, city, phone):

print(f"创建用户:{name}, {age}岁")

print(f"城市:{city}, 电话:{phone}")

create_user("小明", 18, city="北京", phone="13800138000") #

# create_user("小明", 18, "北京", "13800138000") # TypeError

这种设计强制调用者写清楚参数名, 避免 ("小明", 18"北京""") 这种"天书调用"。

四、作用域与命名空间4.1 LEGB 规则

查找变量的顺序是这样规定的, 先是局部变量, 然后是非局部变量, 接着是全局变量, 最后才会去内置变量里面找。

# 全局作用域

x = "global"

def outer():

# 外层函数作用域

x = "outer"

def inner():

# 局部作用域

x = "inner"

print(f"inner: {x}") # inner: inner

inner()

print(f"outer: {x}") # outer: outer

outer()

print(f"global: {x}") # global: global

4.2 和

count = 0

def increment():

global count # 告诉 Python:我要改全局的 count

count += 1

increment()

print(count) # 1

# nonlocal:修改外层函数的变量

def outer():

x = 10

def inner():

nonlocal x

x += 5

inner()

print(x) # 15

我们要尽量少用全局变量。函数最好写成独立完整的模样, 也就是让它自己包含自己需要的东西。把输入参数交进去, 然后得到输出的结果。要是用了全局变量的话, 代码就会变得让人不容易理解, 也不太容易去调试排查问题。

五、所谓匿名函数,就是那种没有名字的函数()。

那是一行函数, 是比较合适的, 适用于那种简单的操作内容。

# 普通函数

def square(x):

return x ** 2

# lambda 等效

square = lambda x: x ** 2

# lambda 的真正用途是作为参数传递

# 配合 sorted()

students = [

("小明", 85),

("小红", 92),

("小刚", 78),

]

# 按分数排序

sorted_students = sorted(students, key=lambda s: s[1], reverse=True)

print(sorted_students) # [('小红', 92), ('小明', 85), ('小刚', 78)]

# 配合 filter()

numbers = [1, 2, 3, 4, 5, 6]

even = list(filter(lambda x: x % 2 == 0, numbers))

print(even) # [2, 4, 6]

# 配合 map()

squared = list(map(lambda x: x ** 2, numbers))

print(squared) # [1, 4, 9, 16, 25, 36]

这一条经验之谈是这么来的: 它比较适合用来处理那些只有一到两行代码的简单逻辑判断。如果遇到的情况是逻辑非常复杂, 那么还是用普通的 功能命名的函数来写更为合适。

六、高阶函数

这个函数是一个第一等级的公民, 它能够让使用者将普通变量视为等同物来进行相互之间的传递操作。

# 函数作为参数

def apply_twice(func, arg):

return func(func(arg))

def add_one(x):

return x + 1

print(apply_twice(add_one, 5)) # 7

# 函数作为返回值(闭包)

def make_power(n):

def power(x):

return x ** n

return power

square = make_power(2)

cube = make_power(3)

print(square(5)) # 25

print(cube(5)) # 125

# 装饰器基础

def timer(func):

"""计算函数执行时间"""

import time

def wrapper(*args, **kwargs):

start = time.perf_counter()

result = func(*args, **kwargs)

elapsed = time.perf_counter() - start

print(f"{func.__name__} 耗时:{elapsed:.4f}秒")

return result

return wrapper

@timer

def slow_function():

import time

time.sleep(0.5)

return "完成"

slow_function() # 输出:slow_function 耗时:0.5003秒

七、实战:工具箱

# utils.py — 你的个人工具箱

import time

from functools import wraps

def retry(max_attempts=3, delay=1):

"""

重试装饰器:函数执行失败时自动重试

Args:

max_attempts: 最大重试次数

delay: 重试间隔(秒)

"""

def decorator(func):

@wraps(func)

def wrapper(*args, **kwargs):

last_exception = None

for attempt in range(1, max_attempts + 1):

try:

return func(*args, **kwargs)

except Exception as e:

last_exception = e

print(f"第{attempt}次尝试失败:{e}")

if attempt < max_attempts:

time.sleep(delay)

raise last_exception

return wrapper

return decorator

# 数据验证函数

def validate_email(email):

"""简单验证邮箱格式"""

import re

pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}#39;

return bool(re.match(pattern, email))

def validate_phone(phone):

"""验证中国大陆手机号"""

import re

# 1开头,第二位3-9,后面9位数字

pattern = r'^1[3-9]\d{9}#39;

return bool(re.match(pattern, phone))

# 数据转换函数

def chunk_list(data, chunk_size):

"""将列表分割成固定大小的块"""

return [data[i:i + chunk_size] for i in range(0, len(data), chunk_size)]

def flatten(nested_list):

"""展平嵌套列表"""

result = []

for item in nested_list:

if isinstance(item, list):

result.extend(flatten(item)) # 递归展平

else:

result.append(item)

return result

# 使用示例

@retry(max_attempts=3, delay=0.5)

def unstable_api_call(url):

"""模拟不稳定的API调用"""

import random

if random.random() < 0.7: # 70%概率失败

raise ConnectionError("网络错误")

return f"数据来自 {url}"

# 测试

result = unstable_api_call("http://api.example.com/data")

print(f"结果:{result}")

# 其他工具测试

data = [1, 2, 3, 4, 5, 6, 7, 8]

print(chunk_list(data, 3)) # [[1, 2, 3], [4, 5, 6], [7, 8]]

nested = [1, [2, [3, 4], 5], 6]

print(flatten(nested)) # [1, 2, 3, 4, 5, 6]

# True

print(validate_phone("13800138000")) # True

总结

概念

要点

定义

定义一个名为函数名的代码块, 并让它在内部接收参数。

返回值

它可以将各种类型的结果给带回来, 要是没有返回的具体东西, 那么就让它去返回那个叫作 None 的状态。

位置参数

按顺序传参

关键字参数

使用 name=value 的传参方式, 其中的顺序是可以被打乱的。

默认参数

这个内容在定义的时候就需要进行指定, 在这里务必要注意到可变对象所带来的陷阱。

*args

接收任意数量位置参数

**

接收任意数量关键字参数

作用域

LEGB规则指的是搜索顺序, 具体来说是先从局部开始进行查找, 接着再找内置名称。

一行匿名函数

装饰器

函数增强器,@语法糖

下一篇预告是关于文件操作的内容, 具体将涉及文件的读取和写入环节, 这一部分我们是十分认真的。

你学会了函数, 这意味着你已经从被称为“脚本小子”的人, 进化成为了真正的程序员。

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

ben skills

代码开发规范 不要为了测试便利在主业务代码中加入测试专用的注入点、分支或参数&#xff1b;应优先在测试代码中通过 mock 依赖或替换模块实现测试&#xff0c;保持主业务代码的可读性和职责纯粹。 开发前端代码时默认不添加无障碍属性&#xff08;role、aria-* 等&#xff09…

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

春晚机器人火爆背后:AI大模型点燃黄金赛道,普通人也能抓住机遇!

春晚的机器人表演引发了人们对AI技术的关注。文章指出&#xff0c;AI和机器人已经进入普通人的生活&#xff0c;并推动了“机器人租赁”等新产业的发展。同时&#xff0c;AI行业的薪资水平也在不断提高&#xff0c;因为市场上对AI人才的需求远远大于供给。普通人可以通过学习实…

作者头像 李华
网站建设 2026/9/24 17:40:21

实操——修复OpenSSH远程代码执行漏洞

文章目录说明修复步骤1.安装telnet服务2.升级openssh2.1 **开始安装openssl****2.2 安装openssh**说明 首先openssh漏洞&#xff0c;需要升级openssh&#xff0c;所以在升级过程中我们是无法用ssh连接过去的&#xff1b;所以我们需要先安装telnet 来进行连接。 参考文章&…

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

【Cursor+Ollama+本地大模型】Windows

下载安装Ollama并下载本地模型 第一步&#xff1a;下载Ollama 官网下载&#xff1a;https://ollama.com/download/windows 第二步&#xff1a; 安装 1.双击 OllamaSetup.exe 2.点击 “Install” 3.等待安装完成&#xff0c;任务栏右下角出现 羊驼图标 即表示后台服务已启动 验证…

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

Linux下Llama.cpp部署Qwen3-30b

在 CentOS 7 上部署 vLLM Qwen3-30B-A3B-Instruct-2507-GGUF 是一个高难度但可行的任务。结合你 无GPU&#xff08;VMware虚拟机&#xff09; 的环境&#xff0c;这需要从源码编译&#xff0c;且需要处理Qwen3 MoE模型的特定兼容性问题。&#x1f3af; vLLM 版本选择推荐使用 …

作者头像 李华
网站建设 2026/9/24 17:36:52

海洋科学航次数据缺项在哪一关露头,那一刻还补不补得回去

航次一结束&#xff0c;记录散在几个人手里&#xff0c;某一处缺项常要等到成表才被发现。这一篇要说的只有一件事&#xff1a;缺的地方在哪一关先露头&#xff0c;那时还补不补得回去。真要动手&#xff0c;章节排布这块一项免费能力能把顺序理顺&#xff0c;图表与算式那边另…

作者头像 李华