ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/9/24 17:44:23 拓冰建站 浏览量
函数:写一次代码,用一万次 如果你连函数都写不出来, 那么你根本就没有进入到这门学问的门槛里面去。函数, 它是编程里面的那个核心的抽象。你现在去写一个函数, 这就等于你去发明了一个新的动词。那其他的人, 包括那个未来的你自己在内, 也只需要去知道它是做什么事情的就行了。至于说它到底是怎么去做的这个问题, 是不需要去知道的。今天我们将由浅入深, 从最基础的知识逐步过渡到更高级的内容, 把函数这个知识点讲透彻。一、我们来了解一下关于函数的第一点一内容, 具体来说就是大家经常会问到的问题, 即究竟为什么要使用函数呢?这段代码不需要用到函数。# 计算三个圆的面积r1, r2, r3 5, 10, 15area1 3.14159 * r1 ** 2area2 3.14159 * r2 ** 2area3 3.14159 * r3 ** 2# 如果π需要更高精度你要改3个地方...使用函数这一类事物的代码内容来展开表述。def circle_area(radius, pi3.14159):计算圆的面积return pi * radius ** 2areas [circle_area(r) for r in [5, 10, 15]]# 改精度只需改一个地方函数这种存在, 它的益处在哪里呢?二、我们来定义和调用, 然后看一看2.1基本语法。def greet(name):向指定的人打招呼 # 这是文档字符串docstringreturn 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 返回 Nonedef print_hello():print(Hello) # 没有 returnresult print_hello() # Helloprint(result) # None三、关于3.1这个位置参数的详细解释如下。def introduce(name, age, city):print(f我叫{name}今年{age}岁住在{city})# 按位置传参introduce(小明, 18, 北京)3.2 关键字参数# 按名称传参顺序可以打乱introduce(city上海, name小红, age20)# 混合使用位置参数必须在关键字参数前面introduce(小刚, city广州, age22) ## introduce(city广州, 小刚, 22) # SyntaxError3.3 默认参数def connect(hostlocalhost, port8080, timeout30):print(f连接 {host}:{port}超时{timeout}秒)connect() # 连接 localhost:8080超时30秒# 连接 example.com:8080超时30秒connect(port9000) # 连接 localhost:9000超时30秒443, 10) # 全部自定义️ 默认参数的陷阱# 大坑默认参数只会在定义时计算一次def add_item(item, items[]):items.append(item)return itemsprint(add_item(a)) # [a]print(add_item(b)) # [a, b] ← 不是预期的 [b]!# 正确做法用 None 在函数内部创建def add_item(item, itemsNone):if items is None:items []items.append(item)return items3.第四个知识点是关于可变参数。# *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小明, age18, city北京)# name 小明# age 18# city 北京# 组合使用def api_request(url, *args, methodGET, **kwargs):print(f请求{method} {url})print(f额外参数{args})print(f查询参数{kwargs})api_request(/api/data, extra, methodPOST, page1, size20)3.5 关键字仅可用的参数这一内容, 总共包含了 3 个特性。# * 后面的参数必须用关键字传递def create_user(name, age, *, city, phone):print(f创建用户{name}, {age}岁)print(f城市{city}, 电话{phone})create_user(小明, 18, city北京, phone13800138000) ## create_user(小明, 18, 北京, 13800138000) # TypeError这种设计强制调用者写清楚参数名, 避免 (小明, 18北京) 这种天书调用。四、作用域与命名空间4.1 LEGB 规则查找变量的顺序是这样规定的, 先是局部变量, 然后是非局部变量, 接着是全局变量, 最后才会去内置变量里面找。# 全局作用域x globaldef outer():# 外层函数作用域x outerdef inner():# 局部作用域x innerprint(finner: {x}) # inner: innerinner()print(fouter: {x}) # outer: outerouter()print(fglobal: {x}) # global: global4.2 和count 0def increment():global count # 告诉 Python我要改全局的 countcount 1increment()print(count) # 1# nonlocal修改外层函数的变量def outer():x 10def inner():nonlocal xx 5inner()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, keylambda s: s[1], reverseTrue)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 1print(apply_twice(add_one, 5)) # 7# 函数作为返回值闭包def make_power(n):def power(x):return x ** nreturn powersquare make_power(2)cube make_power(3)print(square(5)) # 25print(cube(5)) # 125# 装饰器基础def timer(func):计算函数执行时间import timedef wrapper(*args, **kwargs):start time.perf_counter()result func(*args, **kwargs)elapsed time.perf_counter() - startprint(f{func.__name__} 耗时{elapsed:.4f}秒)return resultreturn wrappertimerdef slow_function():import timetime.sleep(0.5)return 完成slow_function() # 输出slow_function 耗时0.5003秒七、实战工具箱# utils.py — 你的个人工具箱import timefrom functools import wrapsdef retry(max_attempts3, delay1):重试装饰器函数执行失败时自动重试Args:max_attempts: 最大重试次数delay: 重试间隔秒def decorator(func):wraps(func)def wrapper(*args, **kwargs):last_exception Nonefor attempt in range(1, max_attempts 1):try:return func(*args, **kwargs)except Exception as e:last_exception eprint(f第{attempt}次尝试失败{e})if attempt max_attempts:time.sleep(delay)raise last_exceptionreturn wrapperreturn decorator# 数据验证函数def validate_email(email):简单验证邮箱格式import repattern 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_attempts3, delay0.5)def unstable_api_call(url):模拟不稳定的API调用import randomif 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]# Trueprint(validate_phone(13800138000)) # True总结概念要点定义定义一个名为函数名的代码块, 并让它在内部接收参数。返回值它可以将各种类型的结果给带回来, 要是没有返回的具体东西, 那么就让它去返回那个叫作 None 的状态。位置参数按顺序传参关键字参数使用 namevalue 的传参方式, 其中的顺序是可以被打乱的。默认参数这个内容在定义的时候就需要进行指定, 在这里务必要注意到可变对象所带来的陷阱。*args接收任意数量位置参数**接收任意数量关键字参数作用域LEGB规则指的是搜索顺序, 具体来说是先从局部开始进行查找, 接着再找内置名称。一行匿名函数装饰器函数增强器语法糖下一篇预告是关于文件操作的内容, 具体将涉及文件的读取和写入环节, 这一部分我们是十分认真的。你学会了函数, 这意味着你已经从被称为“脚本小子”的人, 进化成为了真正的程序员。