Python 函数定义和使用
# 函数的概念 # 概念 # 写了一段代码实现了某个小功能; 然后把这些代码集中到一块, 起一个名字; 下一次就可以根据这个名字再次使用这个代码块, 这就是函数 # 作用 # 方便代码的重用 # 分解任务, 简化程序逻辑 # 使代码更加模块化 # 函数分类 # 内建函数 # 三方函数 # 自定义函数 # 函数的基本使用 # 简单定义 # def 函数名(): # 函数体 # 函数的调用 # 函数名() def myFun(): print(1) #函数调用 myFun() # 函数的参数 # 单个参数 # 场景 # 需要动态的调整函数体中某一个处理信息 # 则可以, 以参数的形式接收到相关数据 # 定义 # def 函数名(参数名称): # 函数体 # 函数体中, 可以直接以变量的方式使用该参数 # 函数的调用 # 函数名(参数值) # 形参和实参的概念 # 上述函数定义中, "参数名称"即为形参; # 在调用函数的时候, 传递的真实数据, 即为实参 # 多个参数 # 场景 # 需要动态的调整函数体中多个处理信息时 # 则可以以 逗号 做分割, 接收多个参数 # 定义 # def 函数名(参数名称1, 参数名称2): # 函数体 # 函数体中, 可以直接以变量的方式使用所有参数 # 调用 # 方式1 # 函数名(参数1, 参数2, 参数3...) # 形参和实参一一对应 # 方式2 # 函数名(参数名称1=参数1, 参数名称n = 参数n...) # 可以指明形参名称 # 称为"关键字参数" # 不需要严格按照顺序 # 不定长参数 # 场景 # 如果函数体中, 需要处理的数据, 不确定长度 # 则可以以不定长参数的方式接收数据 # 方式1 # 定义 # def 函数名(*args): # 元组 # 函数体 # 函数体中, 可以直接以元组变量的方式使用该参数 # 使用 # 函数名(参数1, 参数2, 参数3...) def myFunA(*args): print(args,type(args)) for v in args: print(v) myFunA(1, 2, 3) #(1, 2, 3) <class 'tuple'> # 方式2 # 定义 # def 函数名(**dic): # 字典 # 函数体 # 函数体中, 可以直接以字典变量的方式使用该参数 # 使用 # 函数名(参数名称1=参数1, 参数名称2=参数2...) def myFunB(**kwargs): print(kwargs, type(kwargs)) myFunB(name = "abc", age = 18) # {'name': 'abc', 'age': 18} <class 'dict'> # 参数拆包 # 装包 # 把传递的参数, 包装成一个集合, 称之为"装包" # 拆包 # 把集合参数, 再次分解成单独的个体, 称之为"拆包" def mySum(a, b): print(a + b) def myFunC(*args): print(args) #拆包 mySum(*args) myFunC(1, 2) #(1, 2) 3 # 缺省参数 # 场景 # 当我们使用一个函数的时候, 如果大多数情况下, 使用的某个数据是一个固定值, 或者属于主功能之外的小功能实现; # 则可以使用默认值 # 这种参数, 称为"缺省参数" # 定义 # def 函数名(变量名1=默认值1, 变量名2=默认值2): # 函数体 # 函数体中, 即使外界没有传递指定变量, 也可以使用, 只不过值是给定的默认值 # 使用 # 函数名(变量1, 变量2) # 此处如果是缺省参数, 则可以不填写 # 参数注意 # 值传递和引用传递 # 值传递 # 是指传递过来的, 是一个数据的副本; # 修改副本, 对原件没有任何影响 # 引用传递 # 是指传递过来的, 是一个变量的地址 # 通过地址, 可以操作同一份原件 # 但是注意 # 在Python当中, 你没得选, 只有引用传递(地址传递) # 但是 # 如果数据类型是可变类型, 则可以改变原件 # 如果数据类型是不可变类型, 则不可以改变原件 def test(num): print(id(num)) print(num) num = 11 print(id(num)) # 和a地址不一样 a = 10 print(id(a)) test(a) # 打印的id 值一样 print(id(a)) print("------------------") # 值发生改变,是根据数据类型来说的 def test1(num): print(id(num)) num.append(666) a = [1, 2, 3] print(id(a)) test1(a) # 打印的id 值一样 print(a) #[1, 2, 3, 666] # 函数的返回值 # 场景 # 当我们通过某个函数, 处理好数据之后, 想要拿到处理的结果 # 语法 # def 函数(): # 函数体 # return 数据 # 注意事项 # 1. return 后续代码不会被执行 # 2. 只能返回一次 # 3. 如果想要返回多个数据, 可先把多个数据包装成一个集合, 整体返回 # 列表 # 元组 # 字典 # ... def myCall(a, b): he = a + b cha = a - b return (he, cha) # 返回元组,就可以返回多个值了 he, cha = myCall(11, 10) print(he, cha) #21 1 # 函数的使用描述 # 场景 # 当我们编写三方函数, 为了方便他人使用, 就需要描述清楚我们所写的函数功能以及使用方式等信息 # 定义格式 # 直接在函数体的最上面, 添加三个双引号对注释 # def 函数(): # ''' 这里写帮助信息 ''' # 查看函数使用文档 # help(函数) # 经验 # 一般函数的描述, 需要说明如下几个信息 # 函数的功能 # 参数 # 含义 # 类型 # 是否可以省略 # 默认值 # 返回值 # 含义 # 类型 def caculate(a, b = 1): """ 计算2个数的和与差 :param a: 参数1:数值类型,不可选,没有默认值 :param b: 参数2:数值类型,可选,默认值:1 :return:返回的是计算的结果:元组类型,(和,差) """ he = a + b cha = a - b return (he, cha) # 返回元组,就可以返回多个值了 help(caculate) # 偏函数 # 概念&场景 # 当我们写一个参数比较多的函数时, 如果有些参数, 大部分场景下都是某一个固定值, 那么为了简化使用, 就可以创建一个新函数, # 指定我们要使用的函数的某个参数, 为某个固定的值; 这个新函数就是"偏函数" # 语法 # 方式1 # 自己写一个新的 # 方式2 # 借助functools模块的partial函数 # import functools # newFunc = functools.partial(函数, 特定参数=偏爱值) # 场景 # int() #方式1 def myFun1(a, b, c, d): print(a + b + c + d) def myFun2(a, b, c, d = 1): myFun1(a, b, c, d) myFun2(1, 2, 3) #print 7 #方式2 import functools newFun = functools.partial(myFun1, d = 2) newFun(1, 2, 3) #print 8 #应用场景 numStr = "10010" res = int(numStr, base = 2) print(res) #print 18 int2 = functools.partial(int, base = 2) print(int2(numStr)) #print 18 # 高阶函数 # 概念 # 当一个函数A的参数, 接收的又是另一个函数时, 则把这个函数A成为是"高阶函数" # 例如 # sorted函数 # 案例 # 动态计算两个数据 l = [{"name": "a1", "age": 18}, {"name": "a2", "age": 20}, {"name": "a3", "age": 19}] def getKey(x): return x["age"] #按age的值来排序 result = sorted(l, key = getKey) print(result) # [{'name': 'a1', 'age': 18}, {'name': 'a3', 'age': 19}, {'name': 'a2', 'age': 20}] #案例 def caculate_test(num1,num2, newFun): return newFun(num1, num2) def sum(a, b): return a + b res = caculate_test(1, 2, sum) print(res) # 3 # 返回函数 # 概念 # 是指一个函数内部, 它返回的数据是另外一个函数; 把这样的操作成为"返回函数" # 案例 # 根据不同参数, 获取不同操作, 做不同计算 def getFunc(flag): def sum(a, b, c): return a + b + c def jian(a, b, c): return a - b - c if flag == "+": return sum elif flag == "-": return jian newFun = getFunc("+") res = newFun(1, 2, 3) print(res) # 6 # 匿名函数 # 概念 # 也称为 "lambda函数" # 顾名思义, 就是指没有名字的函数 # 语法 # lambda 参数1, 参数2.. : 表达式 # 限制 # 只能写一个表达式 # 不能直接return # 表达式的结果就是返回值 # 所以, 只适用于一些简单的操作处理 # 测试 # func = lambda x, y: x + y # func(1, 2) result = (lambda x, y : x + y)(1, 2) print(result, type(result)) #3 <class 'int'> newFunc = lambda x, y : x + y print(newFunc(4,5)) # 9 #应用场景 l = [{"name": "a1", "age": 18}, {"name": "a2", "age": 20}, {"name": "a3", "age": 19}] result = sorted(l, key = lambda x : x["age"]) print(result) # [{'name': 'a1', 'age': 18}, {'name': 'a3', 'age': 19}, {'name': 'a2', 'age': 20}] # 闭包 # 概念 # 在函数嵌套的前提下 # 内层函数引用了外层函数的变量(包括参数) # 外层函数, 又把 内层函数 当做返回值进行返回 # 这个内层函数+所引用的外层变量, 称为 "闭包" # 标准格式 # def test1(a): # b = 10 # 其他函数定义代码 # def test2(): # print(a) # print(b) # return test2 # 应用场景 # 外层函数, 根据不同的参数, 来生成不同作用功能的函数 # 案例 # 根据配置信息, 生成不同的分割线函数 # 注意事项 # 1. 闭包中, 如果要修改引用的外层变量 # 需要使用 nonlocal 变量 声明 # 否则当做是闭包内, 新定义的变量 # 2. 当闭包内, 引用了一个, 后期会发生变化的变量时, 一定要注意 # 函数, 是被调用时, 才去确定变量标识所对应的值 #应用场景 def line_config(content,length): print("-" * (length // 2) + content + "-" * (length // 2)) line_config("闭包",20) #----------闭包---------- line_config("闭包",20) #----------闭包---------- #上面的方式调用麻烦,现在改造一下,方便调用 def line_config(content,length): def line(): print("-" * (length // 2) + content + "-" * (length // 2)) return line line1 = line_config("闭包",20) line1() #----------闭包---------- line1() #----------闭包---------- line2 = line_config("闭包",40) line2() #--------------------闭包-------------------- # 装饰器 # 作用 # 在函数名以及函数体不改变的前提下, 给一个函数附加一些额外代码 # 语法 # @装饰器 # def 被装饰函数(): # code # 案例 # 发说说, 发图片 # 附加 # 身份验证 操作 # "开放封闭"原则 # 已经写好的代码, 尽可能不要修改 # 如果想要新增功能, 在原先代码基础上, 单独进行扩展 # 单一职责 # 注意 # 装饰器的执行时间, 是立即执行 # 进阶 # 装饰器叠加 # 从上到下装饰 # 从下到上执行 # 对有参函数进行装饰 # 无论什么场景, 保证函数调用参数个数一致 # 为了通用, 可以使用不定长参数, 结合 拆包操作进行处理 # 对有返回值的函数进行装饰 # 无论什么场景, 保证函数返回值一致 # 带有参数的装饰器 # 通过@装饰器(参数)的方式, 调用这个函数, 并传递参数; 并把返回值, 再次当做装饰器进行使用 # 先计算 @ 后面的内容, 把这个内容当做是装饰器 def checkLogin(func): print("*" * 10 ) # 只在使用@装饰器运行 def inner(): print("登陆认证....") func() print("-" * 10) # 函数调用后才运行 return inner @checkLogin # fss = checkLogin(fss) def fss(): print("发说说") @checkLogin # fpic = checkLogin(fpic) def fpic(): print("发图片") #下面为业务的逻辑部分,如果要想在程序中保持业务逻辑和代码不变,就需要上面的装饰符 indexItem = 1 if indexItem == 1 : fss() else: fpic() # 生成器 # 生成器 # 是一个特殊的迭代器(迭代器的抽象层级更高) # 所以, 拥有迭代器的特性 # 惰性计算数据, 节省内存 # 能够记录状态, 并通过next()函数, 访问下一个状态 # 具备可迭代特性 # 但是, 如果打造一个自己的迭代器, 比较复杂 # 需要实现很多方法 # 后续在"面向对象"编程中会进行讲解 # 所以, 就有一个更加优雅的方式 "生成器" # 创建方式 # 生成器表达式 # 把列表推导式的[] 修改成 () # (i for i in range(1, 10000000) if i % 2 == 0) # 生成器函数 # 函数中包含 yield语句 # 这个函数的执行结果就是 "生成器" # 产生数据的方式 # 生成器具备可迭代特性 # next()函数 # 等价于 # 生成器.__next__() # for in # send() 方法 # send方法有一个参数,指定的是上一次被挂起的yield语句的返回值 # 相比于.__next__() # 可以额外的给yield 语句 传值 # 注意第一次调用 # t.send(None) # 关闭生成器 # g.close() # 后续如果继续调用, 会抛出StopIteration异常提示 # 注意 # 如果碰到return # 会直接终止, 抛出StopIteration异常提示 # 生成器只会遍历一次 # L = [i for i in range(1,100) if i % 2 == 0] #直接生成列表,range生成比较多的时候,浪费内存 L = (i for i in range(1, 100) if i % 2 == 0) print(L) #<generator object <genexpr> at 0x03A7AC60> print(next(L)) # 2 print(next(L)) # 4 print(L.__next__()) # 6 for i in L: print(i) # 从 8 开始,因为上面已经移动了指针 def test(): print("xxx") yield 1 print("a") yield 2 print("b") runing = test() # 不会打印xxx for i in runing: print(i) # 递归函数 # 体现 # 函数A内部, 继续调用函数A # 概念 # 传递 # 回归 # 注意事项 # 有传递, 一定要有回归 # 否则, 就是无限的循环调用 # 案例 # 求一个数值的阶乘 5!= 5 * 4 * 3 * 2 * 1 def jiecheng(n): if n == 1: return 1 return n * jiecheng(n - 1) print(jiecheng(5)) # 120 # 函数作用域 # 基本概念 # 变量的作用域 # 变量的作用范围 # 可操作范围 # Python是静态作用域,也就是说在Python中,变量的作用域源于它在代码中的位置; # 在不同的位置, 可能有不同的命名空间 # 命名空间 # 是作用域的体现形式 # 不同的具体的操作范围 # Python-LEGB # L-Local # 函数内的命名空间 # 作用范围: 当前整个函数体范围 # E-Enclosing function locals # 外部嵌套函数的命名空间 # 作用范围: 闭包函数 # G-Global # 全局命名空间 # 作用范围: 当前模块(文件) # B-Builtin # 内建模块命名空间 # 作用范围: 所有模块(文件) # 注意 # Python中没有块级作用域 # 块级作用域 # 代码块中, 比如 if while for 后的代码块 # LEGB规则 # 按照L -> E -> G -> B 的顺序进行查找 # 基于命名空间的常见变量类型 # 局部变量 # 在一个函数内部定义的变量; # 作用域为函数内部 # 查看局部变量 # locals() # 全局变量 # 在函数外部, 文件最外层定义的变量 # 作用域为整个文件内部 # 查看全局变量 # globals() # 注意点 # 访问原则 # 从内到外 # 结构规范 # 全局变量 # 函数定义 # 使用 # 修改 # 后续代码 # 全局变量和局部变量重名 # 获取 # 就近原则 # 修改 # global 全局变量 # 声明 # l -> e # unlocal # 命名 # 全局变量 # g_xxx