本篇技术博文摘要 🌟
本文系统梳理了Python函数的核心知识点,从基础概念到高级特性,构建了完整的函数编程知识体系。内容涵盖:变量作用域的局部与全局划分、函数注释的规范写法、参数传递中值与引用的区别、匿名函数的灵活应用,以及Python 3.8的新特性(如海象运算符)。进一步深入偏函数、嵌套函数与闭包的实现,详解装饰器的语法与进阶用法(参数传递、多装饰器顺序、返回值处理)。最后结合高阶函数(
map
、filter
、reduce
)实现数据批量操作,并通过综合实验——工资计算系统,串联知识点,实践员工信息管理与月薪计算逻辑。
引言 📘
- 在这个变幻莫测、快速发展的技术时代,与时俱进是每个IT工程师的必修课。
- 我是盛透侧视攻城狮,一名什么都会一丢丢的网络安全工程师,也是众多技术社区的活跃成员以及多家大厂官方认可人员,希望能够与各位在此共同成长。
上节回顾
目录
综合实验:设计一个工资计算系统,录入员工信息,计算员工的月薪
01-函数中变量的作用域
- 原理:Python变量作用域分为局部作用域(函数内部)和全局作用域(函数外部)。
- 作用:理解变量可见性和生命周期
a = 100
def test_01():
a = 10
b = 20
print(a)
print(b)
def test_02():
print(a)
def test_03():
global a
a += 100
print(a)
test_03()
# test_02()
# test_01()
# print(b)
'''
全局变量:声明在函数外部的变量称为全局变量
'''
- 函数内部可以使用全局变量,但是不能修改,如果一定要在函数内部修改全局变量,那么global
02-函数注释
- 原理:使用文档字符串(
"""..."""
)和类型注解(Python 3.5+)为函数添加说明。- 作用:提高代码可读性,IDE可识别提示
def add(a: int, b: int) -> int:
"""返回两个整数的和"""
return a + b
print(add.__doc__) # 输出:返回两个整数的和
03-值传递引用传递
- 原理:
- 值传递:在传递参数的时候,仅仅是将自己的值拷贝了一份,传递给了函数的参数,变量不会改变
- 引用传递:在传递参数时,传地址,函数形参获取的值也是同一块内存
- 作用:理解参数修改对原数据的影响
def test_01(x, y):
x += 10
y += 100
print(x)
print(y)
a = 10
b = 20
test_01(a, b)
print(a)
print(b)
def test_02(nums):
print(id(nums))
nums.append(10)
nums.append(100)
print(nums)
list1 = [1, 2, 3]
print(id(list1))
test_02(list1)
print(list1)
04-Py函数以及匿名函数
- 原理:
lambda [arg1,arg2……]: 代码块(函数体)
存在函数作为参数传递给函数,并且又不想让外界访问,而且参数函数足够简单,即可以定义为匿名函数(lambda表达式)
- 作用:简化代码,常用于高阶函数参数
def now():
print("1111111111")
f = now
a = f
# __name__,拿到函数的名字
print(f.__name__)
print(a.__name__)
def test_func(add):
result = add(1, 2)
print(result)
def compute(a,b):
return a+b
test_func(compute)
test_func(lambda a, b: a + b)
- function(){}
- 在python中,将一个函数作为参数传到另一个函数中去
- 函数的参数数据类型:只要是一个对象就可以
- 函数本身就是一个对象
05-python3.8新特性
- 原理:Python 3.8引入海象运算符(
:=
)、位置参数限定符(/
)等。- 作用:提升代码简洁性和灵活性
# 海象运算符
if (n := len([1,2,3])) > 2:
print(f"长度是{n}") # 输出:长度是3
# 位置参数
def func(a, b, /, c):
print(a, b, c)
func(1, 2, c=3) # 正确
func(a=1, b=2, c=3) # 报错:a,b必须位置参数
# 声明函数参数类型
# def 函数名(a,b)
def add(x: int, y: int) -> int:
return x + y
print(add(1, 2))
print(add("a", "b"))
06-偏函数
- 原理:使用
functools.partial
固定函数的部分参数,生成新函数。- 作用:简化参数传递。
from functools import partial
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
print(square(5)) # 输出25
# functools模块提供很多方法,其中有一个就是偏函数
# int(3.14)
# def int2(x, base=2):
# return int(x, base)
#
# print(int2("10010",10))
import functools
int2 = functools.partial(int, base=2)
print(int2("100101"))
07-函数的嵌套
- 原理:在函数内部定义另一个函数。
- 作用:封装逻辑,限制函数作用域。
def outer():
def inner():
print("内部函数")
inner()
outer() # 输出:内部函数
def fun1():
b = 20
def fun2(): #fun2=def(){}
print(b)
return fun2
a = fun1()
a()
a()
a()
- 综上所述:
- 1、函数可以作为返回值进行返回
- 2、函数可以作为参数进行传进
- 3、函数名本质上是一个变量名,指向函数的内存地址
08-闭包
- 原理:一个函数嵌套一个函数,内层函数用到外层函数的局部变量,内层函数就被称为闭包
- 作用:保留状态,实现数据隐藏。
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
c = counter()
print(c(), c()) # 输出1, 2
def outer():
a = 1
def inner():
#内层函数如果要改变外层函数局部变量的值,需要使用nonlocal关键字赋予权限
nonlocal a
a += 1
inner()
print(a)
return inner
outer()
# 可以让一个变量常驻在内存当中
# 可以避免全局变量被修改
09-装饰器
- 原理:装饰器本质上是一个闭包,作用是不改变原有函数的前提下,为函数添加新的功能,但是源代码不改变
雏形: def wrapper(目标函数): def inner(): 之前添加的功能 目标函数() 之后添加的功能 return inner
- 作用:无侵入式增强功能(如用户登录、日志、计时)
10-装饰器参数
- 原理:装饰器本身支持参数传递。
- 作用:动态配置装饰器行为。
def guanjia(fn):
# 定义一个名为guanjia的装饰器函数,它接受一个函数作为参数
print("这波传入的函数是", fn.__name__)
# 打印传入函数的名称,方便调试和理解装饰器的工作过程
def inner(*args, **kwargs):
# 定义一个内部函数inner,它使用了可变参数*args和**kwargs
# *args用于接收任意数量的位置参数,**kwargs用于接收任意数量的关键字参数
print("开挂")
# 在调用被装饰的函数之前,打印“开挂”信息
fn(*args, **kwargs)
# 调用被装饰的函数,并将接收到的参数传递给它
print("关闭外挂")
# 在调用被装饰的函数之后,打印“关闭外挂”信息
return inner
# 返回内部函数inner,这样当使用@guanjia语法时,实际上是将被装饰的函数替换为inner函数
@guanjia # play_wz = guanjia(play_wz)
# 使用@guanjia语法将guanjia装饰器应用到play_wz函数上
# 这相当于执行了play_wz = guanjia(play_wz),将play_wz函数替换为guanjia返回的inner函数
def play_wz(uname, password):
# 定义一个名为play_wz的函数,它接受两个参数:用户名和密码
print("来和妲己玩耍吧")
# 打印游戏相关的提示信息
print(f"用户名{uname},密码是{password}")
# 打印用户名和密码信息
@guanjia
# 使用@guanjia语法将guanjia装饰器应用到play_dnf函数上
def play_dnf(uname, pwd, g):
# 定义一个名为play_dnf的函数,它接受三个参数:用户名、密码和技能
print("你好啊,我是赛利亚,今天又是美好的一天")
# 打印游戏相关的提示信息
print(f"用户名{uname},密码是{pwd},技能是{g}")
# 打印用户名、密码和技能信息
play_wz("zhangsan", "123456")
# 调用被装饰后的play_wz函数,传入用户名和密码
# 实际执行的是inner函数,会先打印“开挂”,然后调用原始的play_wz函数,最后打印“关闭外挂”
play_dnf("lisi", "lisisi", "吹")
# 调用被装饰后的play_dnf函数,传入用户名、密码和技能
# 同样会先打印“开挂”,然后调用原始的play_dnf函数,最后打印“关闭外挂”
- 装饰器:
guanjia
是一个装饰器函数,它可以在不修改原函数代码的情况下,为原函数添加额外的功能(如打印 “开挂” 和 “关闭外挂” 信息)。- 可变参数:
inner
函数使用了*args
和**kwargs
可变参数,这样可以处理任意数量和类型的参数,使得装饰器可以应用到不同参数的函数上。- 函数调用:调用被装饰后的函数时,实际上是调用了
inner
函数,inner
函数会在调用原函数前后执行额外的操作。
11-一个函数被多个装饰器装饰
- 原理:装饰器按从下到上的顺序执行
# 定义装饰器函数 wrapper1,它接收一个函数作为参数
def wrapper1(fn):
# 定义内部函数 inner,用于包装被装饰的函数
def inner(*args, **kwargs):
# 在调用被装饰函数之前,打印特定信息
print("11111111111111111")
# 调用被装饰的函数,并传入参数
fn(*args, **kwargs)
# 在调用被装饰函数之后,打印特定信息
print("111111111111111111")
# 返回内部函数 inner
return inner
# 定义装饰器函数 wrapper2,它接收一个函数作为参数
def wrapper2(fn):
# 定义内部函数 inner,用于包装被装饰的函数
def inner(*args, **kwargs):
# 在调用被装饰函数之前,打印特定信息
print("2222222222222222")
# 调用被装饰的函数,并传入参数
fn(*args, **kwargs)
# 在调用被装饰函数之后,打印特定信息
print("222222222222222222")
# 返回内部函数 inner
return inner
# 使用 wrapper1 装饰 wrapper2 装饰后的 target 函数
# 首先执行 @wrapper2,将 target 函数替换为 wrapper2.inner
# 然后执行 @wrapper1,将 wrapper2.inner 作为参数传入 wrapper1,得到 wrapper1.inner
@wrapper1
@wrapper2
# 定义目标函数 target
def target():
# 打印函数内的信息
print("我是函数")
# 调用被装饰后的 target 函数,实际上调用的是 wrapper1.inner
target()
# 以下是使用 time 模块的代码
# 导入 time 模块,该模块提供了各种与时间相关的函数
import time
# 打印当前的时间戳,即从 1970 年 1 月 1 日午夜(UTC)开始到现在的秒数
print(time.time())
# 让程序暂停执行 2 秒
time.sleep(2)
12-装饰器函数返回值问题
- 装饰器
guanjia
:在调用被装饰的函数前后分别打印提示信息,并返回被装饰函数的返回值
# 定义一个名为guanjia的装饰器函数,它接收一个函数作为参数
def guanjia(fn):
# 定义内部函数inner,用于包裹被装饰的函数
def inner(*args, **kwargs):
# 在调用被装饰函数前打印提示信息
print("11111111111111111")
# 调用被装饰的函数,并将其返回值赋给变量ret
ret = fn(*args, **kwargs)
# 在调用被装饰函数后打印提示信息
print("111111111111111111")
# 返回被装饰函数的返回值
return ret
# 返回内部函数inner
return inner
# 定义一个名为rizhi的装饰器函数,它接收一个函数作为参数
def rizhi(fn):
# 定义内部函数inner,用于包裹被装饰的函数
def inner(*args, **kwargs):
# 在调用被装饰函数前打印日志信息
print("玩了一次游戏")
# 调用被装饰的函数
fn(*args, **kwargs)
# 返回内部函数inner
return inner
# 使用guanjia装饰器装饰play_wz函数
# 相当于执行了play_wz = guanjia(play_wz)
@guanjia
# 定义一个名为play_wz的函数,接收用户名和密码作为参数
def play_wz(uname, password):
# 打印用户名和密码信息
print(f"用户名是{uname},密码是{password}")
# 打印游戏相关提示信息
print("来和妲己玩耍吧")
# 返回游戏相关的字符串
return "妲己玩的很666"
# 调用被装饰后的play_wz函数,并将返回值赋给变量a
a = play_wz('gouxin', 123456)
# 打印变量a的值
print(a)
# 使用rizhi装饰器装饰yonghu函数
# 相当于执行了yonghu = rizhi(yonghu)
@rizhi
# 定义一个名为yonghu的函数,接收名字和密码作为参数
def yonghu(name, pwd):
# 打印名字和密码信息
print(f"名字为{name},密码是{pwd}")
# 打印相关提示信息
print("ljs帅到飞起")
# 返回一个字符串
return "蔡志恒我直接爱思了"
# 调用被装饰后的yonghu函数,并将返回值赋给变量b
b = yonghu('caizhiheng', 6666)
# 打印变量b的值
print(b)
13-高阶函数map.py
- 原理:从可迭代对象中获取第一个元素,作为函数的参数,传入函数中,将函数执行后返回结果作为生成对象中的第一个元素,最终返回一个可迭代对象
- 语法:
map(将来可以调用的,可迭代数据)
# map()
a = map(lambda x: x * x, [1, 2, 3]) # [1,4,9]
print(list(a))
for i in a:
print(i)
# map(将来可以调用的,可迭代数据)
'''
'''
# b = map(lambda x,y:x+y,[1,2,3],[4,5,6])
# for i in b:
# print(i)
def f1(x, y):
return x, y
l1 = [0, 1, 2, 3, 4, 5, 6]
l2 = ['sun', 'm', 't', 'w', 't', 'f', 's']
l3 = map(f1, l1, l2)
print(list(l3))
- 第一个
map
示例:使用lambda
函数对列表[1, 2, 3]
中的元素进行平方操作,将map
返回的迭代器转换为列表打印后,迭代器变空,后续遍历无输出。- 注释掉的
map
示例:展示了map
处理两个可迭代对象的情况,使用lambda
函数对两个列表对应位置元素相加。- 自定义函数
f1
与map
:定义函数f1
接受两个参数并返回元组,使用map
将f1
应用到l1
和l2
对应元素上,最后将结果迭代器转换为列表打印。
14-filter函数
- 原理:filter(func, iterable) 过滤满足条件的元素,返回迭代器。
# filter() 过滤对指定的序列执行过滤
a = filter(lambda x: x % 2, [1, 2, 3, 4])
print(list(a))
# 只要成立(为真:0为假,其余数字为真),就保留
15-reduce函数
- 原理:用上一次计算的结果作为下一次传入的x值,如果上一次没有计算结果,则将可迭代数据的强两个元素分别作为x,y传入,如果有额外数据,会作为第一次传入的x值
from functools import reduce
a = reduce(lambda x, y: x + y, [1, 2, 7, 4],5)
print(a)
综合实验:设计一个工资计算系统,录入员工信息,计算员工的月薪
# 设计一个工资计算系统,录入员工信息,计算员工的月薪
from abc import ABCMeta, abstractmethod
class Employee(metaclass=ABCMeta): # 抽象类不能实例化,但是子类可以继承
# 继承抽象类
def __init__(self, name):
self.name = name
# 将这个方法变成抽象方法,对象不能直接调用,但是子类可以重写这个方法
@abstractmethod
def give_salary(self):
pass
class Magnaer(Employee):
# 创建一个部门经理类,部门经理每月固定月薪666666666666666.00元
def give_salary(self):
return 666666666666666.00
class Programmer(Employee):
# 创建一个程序员类,添加一个工作时间属性为 程序员计时支付月薪,每小时2元
# super().__init__() 就是调用父类的init方法, 同样可以使用super()去调用父类的其他方法。
def __init__(self, name, work_hour=0):
super(Programmer, self).__init__(name)
self.work_hour = work_hour
def give_salary(self):
return self.work_hour * 2
class SalesMan(Employee):
# 创建一个销售员类,添加一个销售员按照1元底薪加上销售额5%的提成支付月薪的销售属性
def __init__(self, name, sales=0):
super(SalesMan, self).__init__(name)
self.sales = sales
def give_salary(self):
return self.sales * 0.05 + 1
if __name__ == '__main__':
Salary = [Magnaer('任正非'), Programmer('马化腾'), Programmer('雷军'), Programmer('埃隆·马斯克'), SalesMan('马云'),
SalesMan('蒂姆库克')]
for i in Salary:
if isinstance(i, Programmer):
i.work_hour = float(input(f'请输入{i.name}工作时长:'))
elif isinstance(i, SalesMan):
i.sales = float(input(f'请输入{i.name}的销售额:'))
print(f'{i.name}的本月月薪为¥{i.give_salary()} ')
欢迎各位彦祖与热巴畅游本人专栏与技术博客
你的三连是我最大的动力
点击➡️指向的专栏名即可闪现
➡️ 永恒之心蓝队联纵合横防御
➡️逆向软件破解工程
➡️红帽高级工程师
➡️红帽系统管理员