详解Python函数

函数

是一组指令的集合 、由多个任务组成。函数 可以 让 代码 进行 复用,函数中的任务 是 相对独立的。

函数的定义

语法 : 在 python 中,韩式 使用 def 关键字 进行定义 、 函数名 是一个标识符

def 函数名( [参数列表] ):

    函数体 (代码)
 def my_sum(n):
    sum = 0
    for x in range(n):
        sum += x
    print(sum)

return 关键字

return 关键字作用 : 可以将 函数执行的结果 返回给 调用者、如果函数没有明确的 return , 则默认 return 一个 None

return 可以 结束整个函数的执行、所以在 return 后 的代码 不会执行。

在 Python 语言中, 一个函数 允许返回 多个结果、 Python 会将所有的结果 存储到 元组中 返回给 调用者

def my_sum(n):
    """
    计算 0 ~ n 之间所有的整数 和 , 包含 0 不包含 n
    :param n:  终止值
    :return :  返回计算的所有数字和
    """
    sum = 0
    for x in range(n):
        sum += x
    return sum

函数对象 常见的属性

  • __doc__ : 获取 函数的 文档注释
  • __name__ : 获取 函数的名字、返回的字符串

函数的参数

  • 必传参数
def sum(a, b):
    """
    计算两个整数的和
    :param a:
    :param b:
    :return:
    """
    return a + b
    

sum()           # 报错、缺失2个参数
sum(1, )        # 报错, 缺失 1个参数
sum(1, 2)       # 正常 
sum(1, 2, 3)    # 报错、需要 2个参数、但传递了3个

a 和 b 参数 在调用时候 ,都必须传递,否则 会产生错误

  • 默认参数
    def sum(a, b=0):
        """
        计算两个整数的和
        :param a:
        :param b:
        :return:
        """
        return a + b
    
    
    sum(1, 2)      #  a = 1,  b = 2
    sum(b=1, a=2)  #  a = 2, b = 1
    sum(a=2)       #  a = 2,  b = 0
    sum(b=2)       # 报错, 因为 a 是 必传参数
    

    默认参数 的定义位置 必须 写在 必传参数的 后面 、 调用函数的时候 默认是 按照 参数定义的位置 依次传递数据、但也可以 通过 关键参数 给指定位置的参数 传值。

  • 关键参数

    如果要定义 关键参数、只需要 使用 * 来分割 其他类型的参数即可。

    def sum(a, *, b):
        """
        计算两个整数的和
        :param a:
        :param b:
        :return:
        """
        return a - b
        
        
    sum(1)        # 报错 , b 是一个关键必传参数
    sum(1, 2)     # 报错 , b 是一个关键参数、必须在调用的时候 使用 关键字 进行调用。
    sum(1, b=2)   # 正确写法 
    sum(b=2, a=1) # 正确写法 
    sum(b=2, 1)   # 报错、 第一个参数 使用关键参数传递、那么 后面的参数 都必须使用 关键参数 传递。
    
  • 不定项参数

    参数的个数 不确定。 在调用的时候, args 不能使用 关键参数 传递

    *args 负责 接收 多个参数 。 args 是 元组 , 在调用的时候,如果传入的是 字符串、 列表、元组、集合 ,可以使用 * 进行解包 。

    **kwargs 负责接收 多个关键参数。kwargs 是 字典 , 在调用的时候,如果 传入的是 字典, 可以 通过 ** 进行解包。

    def my_max(a, *args,  **kwargs):
        max = a
        for x in args:
            if max < x:
                max = x
        return max
    
    x = my_max(1, 2, 3, 4, 5, 7, 11, 12, 34, 1 ,  x=1, y=2, z=3)
    print(x)
    

参数的传递

形参: 在 定义函数的时候, 参数列表中 定义的参数 被称为 形参
实参: 在 调用 函数的时候, 传入的数据

  • 不可变类型 参数传递 (值传递): 在调用函数的时候,将 数据的值 传递给 函数
    def exchange(a, b):
        a, b = b, a
    
    
    a = 3
    b = 4
    exchange(a, b)
    print(a, b)     //  3,  4
    
  • 可变类型 参数传递 (引用传递) : 在 调用函数的时候 ,将 数据的 地址 传递给 函数
    def exchange(ls):
        ls[0], ls[1] = ls[1], ls[0]
    
    
    ls = [3, 4]
    exchange(ls)
    print(ls)         // [4, 3]
    

匿名函数

是一个特殊的函数 、 特殊在 函数没有 名字 。

匿名使用 使用 关键字 lambda 来定义

语法: lambda 参数列表: 函数体代码

参数 列表 可以有 0 ~ n 个, 多个参数使用 逗号 分割

函数体代码 有且只能有一条语句、如果需要返回结果 、可以省略 return

# 普通函数
def test(x):
    return x + 3 
# 匿名函数
lambda  x:  x + 3 

递归

一个函数 在 函数体中 自己 调用 自己 、那么 这个现象 就称为 递归调用。

a) 编写 递归调用 必须 要非常清楚 函数的作用
b) 递归调用 必须 提供 收敛条件(结束递归的手段)

Python 中 递归默认有深度限制,如果要修改该限制,可以通过 sys.setrecursionlimit(x) 更改

递归的性能是非常差的,通常情况下不建议采用递归

def sum(n):
    """
    求 前 n 项 连续的 数字 和
    """
    if n == 1:
        return 1
    return n + sum(n - 1)
    
    
def fac(n):
    """获取斐波那契数列中 第 n 项的数字"""
    if n == 1 or n == 2:
        return 1
    return fac(n - 1) + fac(n - 2)

函数作用域

循环/判断 的块 不会产生新的作用域, 而函数 / 类 对应的块 会产生 新的作用域

  • 全局变量 : 在 模块中 定义的变量 是 全局变量。
  • 局部变量: 在 函数中 定义的变量 是 局部变量。局部变量的作用范围是 整个函数,一旦出了函数,变量则失效。
  • 非局部变量: 在函数嵌套中、站在内部函数的角度看 外部函数定义的变量 、被称为 非局部变量

作用域的查找顺序 是 局部 —> 非局部 —> 全局 ----> builtins (内建模块)

a = 10  # 全局变量  

def  test():
     b = 20   #  非局部变量
     
     def inner():
         c = 30   # 局部变量

    inner()

global 关键字

如果 在 函数中要使用 全局变量 、 可以使用 global 关键字 标记

a  = 10    

def  test():
    globa  a    #  标记 a 是一个 全局变量, 否则 a = a + 1 会 被 解析器 认为 a 是一个局部变量, 且没有定义的时候使用 a 而产生错误。
    a  =  a + 1
    print(a)


test() 

nonlocal 关键字

如果 在 内部函数中 要 使用 外部函数 中定义的 变量, 可以使用 nonlocal 关键字 标记

def  test():
    a = 10 
    def inner():
        nonlocal a   # 标记 a 是一个 非局部变量, 否则 a +=1  会被 解析器 认为 a 是一个局部变量, 且没有定义的时候使用 a 而产生错误。
        a += 1
        print(a)
        
    inner()

globals() 函数

用来 获取 全局 范围内所有的 变量 组成的 字典

a = 1
b = 2
print(globals())   #  {.... "a":1,  "b":2}

locals() 函数

用来获取 局部 范围内所有的 变量 组成的 字典

def  test():
    a = 1
    b = 2
    print(locals())   #  {"a": 1, "b": 2}

闭包 & 函数嵌套

闭包 是一种 特殊 的 函数 嵌套 。 外部函数 返回 内部函数 的 引用对象。

闭包 可以 延长 外部函数中定义的 变量的 作用范围 。在 Python 中 闭包 还可以作为 装饰器 来使用

闭包的缺点 : 滥用闭包 有可能会导致产生 内存溢出的风险 。

def outer():
    """
    外部函数 主要用来将 变量 a 保护起来,不让别的程序随意访问
    """
    a = 0
    def generator_id():
        """内部函数负责 生成唯一且连续的数字"""
        nonlocal a
        a += 1
        return a
    # 外部函数必须返回内部函数的引用对象, 如果添加了 括号进行调用,则不再构成闭包。
    return generator_id

# 调用 outter 返回内部函数的引用对象
x = outer()
# 因为 内部函数在内存在被赋值给了x, 所以 和 内部函数相关的数据都会被保留, 从而让 外部函数中的 a 作用范围延长,没有再 outer调用结束后销毁。
print(x())
print(x())
print(x())

装饰器 decrator

装饰器的本身是采用闭包技术 实现的。

装饰器 可以在 不改变 函数 / 类 的 前提下 , 对 功能 进行 增强

装饰 函数

  • 外层 函数 的参数 为 要装饰 的 目标 函数对象 、 外层 函数 返回 内层函数 的 引用对象 。
  • 内层 函数 的参数 为 要装饰 的 目标函数的 参数列表, 通常用 *args**kwargs 表示
  • 内层 函数 一般情况下 做 三件事
    • 处理 装饰 逻辑
    • 调用 目标函数 、并获取函数的结果
    • 返回 目标 函数的 执行结果
def logs(fn):
    def inner(*args, **kwargs):
        # 调用目标函数
        ret = fn(*args, **kwargs)
        # 装饰器中主要负责 记录 函数的执行日志
        print("正在执行" + fn.__name__ + "函数、参数是", args, kwargs, "返回值是", ret, sep="")
        return ret
    return inner

使用 装饰器

@logs
def  sum(a,  b):
    return a + b 

在 函数的上方使用 @ 符号 加 装饰函数的名字 、就可以让 函数 引入 装饰器 , 当调用函数的时候,会自动执行 装饰器中的 代码, 由 装饰器内部函数中的代码 完成 目标函数的调用, 从而实现函数的增强效果。

调用 带有装饰器的目标函数

x  =  sum(1, 3)   # sum 函数 已经被装饰器修饰、那么 sum函数 本质上已经发生改变,其实调用的是 装饰器中的 inner 函数
print(x)

# 上述代码 本质上是 、 此时 sum函数上不允许使用 @logs
logs(sum)(1, 3) 

保留目标函数的原始信息

import functools

def logs(fn):
    
    @functools.wraps(fn)
    def inner(*args, **kwargs):
        # 调用目标函数
        ret = fn(*args, **kwargs)
        # 装饰器中主要负责 记录 函数的执行日志
        print("正在执行" + fn.__name__ + "函数、参数是", args, kwargs, "返回值是", ret, sep="")
        return ret
    return inner

装饰器的作用

  • 日志 记录
  • 性能分析
  • 监控
  • 权限检查
  • 事务管理 (数据库)
  • 处理返回值格式

带参数的装饰器

带参数的装饰器 需要 三层函数嵌套

第一层函数 的参数 代表 装饰器所需要的参数

第二层函数的参数 代表 装饰器 装饰的目标对象

第三层函数的参数代表 装饰器 装饰的目标对象的 参数列表

def  ticket(action):
    
    def decorator(func):
        
        def inner(*args, **kwargs):
            # 处理装饰器逻辑
            # 调用目标函数
            ret = func(*args, **kwargs)
            # 处理装饰器逻辑
            # 返回目标函数的结果
            return ret
        return inner 
        
    return decorator
    
    
@ticket(action="我是装饰器的参数")    
def test():
  print("我是目标函数")

生成器

  • 可迭代对象 : 字符串、 元组、列表、集合 、字典
  • 迭代器

    在 python 中 ,可以 使用 iter 函数将可迭代对象 转换成 迭代器。 可以使用 next 函数 来 每次 获取 迭代器中的 一个数据。

    当 将 迭代器中的所有数据 获取完成后, 再次 调用 next 会产生一个 StopIteration 错误。

    迭代器 是一次性的 、数据的获取顺序 是从 第一个 到 最后一个 、 且过程 不可逆。

  • 生成器

    生成器 是一种 特殊的 迭代器 、通过某种算法 可以将大量数据 存储到内存在 ,且不占用太多的内存空间。通过计算存储的数据。生成器 可以表示无限个数据

生成器的实现方式

  • 元组生成推导式
s = [12, 34, 65, 78, 23, 5]

# 将 列表中的数据 用 生成器来表示
data = (x for x in s)
  • 在 函数中 、使用 yield 关键字 返回数据
def get_number():
    i = 1
    while True:
        yield i
        i += 1


# 一个函数 如果 使用了 yield , 程序会将 函数理解为 一个生成器对象、不会执行函数中的任何代码
ls = get_number()

# 执行函数中的代码,当 执行到 yield i 时, 将 i 的值 返回, 并暂停函数的执行,等待下一次 调用 next函数。
next(ls)
# 继续从 上一次暂停的位置 i+=1 开始执行、当执行到 yield 时,再次返回 i 的值,并暂停函数的执行。
next(ls)

内置函数

  • abs(x) : 获取 x 数字的 绝对值
  • all(iterable) : 判断 可迭代 对象中的 所有元素 是否 都 是 True (包含 隐式转换) 、 如果 可迭代对象 是空 的, 也返回 True
  • any(iterable) : 判断 可迭代 对象中的 任意一个元素 是否 是 True (包含 隐式转换) 、如果 可迭代对象是 空的, 则 返回 False
  • bin / oct / hex / int : 进制转换函数
  • chr / ord : 字符 和 码点 的相互转换
  • divmod(x, y) : 计算 x// y 和 x % y 、返回一个 长度为 2 的元组
  • hash(x) : 计算 一个 不可变类型 对应的 hash 值
  • isinstance(obj , type_tuple) : 判断 一个 对象是否 是 指定的 类型, 类型 如果有多个,则 使用 元组 。
  • iter(iterable) : 将 一个 可迭代对象 转换成 迭代器
  • len(seqence) : 获取 序列的 长度
  • max/min(*args) : 获取传入数据的最大/小值
  • pow(x, y) : 计算 x 的 y 次幂
  • round(x, n=None) : 将 x 进行 四舍五入, 默认取整 、 如果需要 保留 小数,可以 传入一个 n , n >0 即可。
  • sum(iterable) : 将一个可迭代对象中的元素 累加求和
  • sorted() : 排序 、返回一个 排序后的列表
lst = [
    {"name": "李四", "age": 18},
    {"name": "张三", "age": 18},
    {"name": "王五", "age": 17},
    {"name": "赵六", "age": 31},
]

# 按照 年龄 升序排列 、 如果年龄相同, 按照 名字 升序排列
x = sorted(lst, key=lambda y: (y["age"], y["name"]))


# 对 列表 去重 、还要保留 原 数据的顺序
ls = [1, 32, 54, 1, 34, 65, 1, 32]
print(sorted(set(ls), key=lambda y: ls.index(y)))


# 随机打乱
ls = (12, 34, 65, 23, 6, 1, 45)
new_ls = sorted(ls, key=lambda x: random.random())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值