Python函数专题(函数的参数,不定长参数,参数的解包,函数的作用域,命名空间,递归函数,高阶函数及装饰器语法糖写法等)

简介: Python函数专题(函数的参数,不定长参数,参数的解包,函数的作用域,命名空间,递归函数,高阶函数及装饰器语法糖写法等)

Part 1

1.函数简介

  • 函数也是一个对象
  • 函数用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次调用。
语法
def 函数名([形参1,形参2,形参3....]):
    代码块
  • 注意:
    函数名必须符合标识符的规范(可以包含字母、数字、下划线但是不能以数字开头)
    print是函数对象 print()是调用函数

2.函数的参数

2.1形参和实参

形参(形式参数) 定义形参就相当于在函数内部声明了变量,但是并不是赋值。
实参(实际参数) 指定了形参,那么在调用函数时必须传递实参,实参将会赋值给对应的形参,简单来说有几个形参就要有几个实参。

def fun(a, b):  # 这个参数叫做形参,即形式上的参数
    # a = 1
    # b = 2
    print(a + b)

fun(1, 2)  # 实参 实际参数, 当你调用的时候传递实际参数
fun(45663453, 42452412)

2.2函数的传递方式

定义形参时,可以为形参指定默认值。指定了默认值以后,如果用户传递了参数则默认值不会生效。如果用户没有传递,则默认值就会生效。

  • 位置参数:位置参数就是将对应位置的实参赋值给对应位置的形参。
  • 关键字参数: 关键字参数可以不按照形参定义的顺序去传递,而根据参数名进行传递。
  • 混合使用位置参数和关键字参数的时候必须将位置参数写到关键字参数前面去。
def fun(a, b=0, c=0):  # c=0 给形参指定默认值, 当没有传递实参的时候,\
使用默认值,当传递了参数,使用传递进来的参数
    print(a)
    print(b)
    print(c)

fun(1, 2)

输出结果:
在这里插入图片描述

  • 位置传参和关键字传参混合使用, 位置传参必须放到关键字传参的前面
def fun(a, c, b):
    print(a)
    print(b)
    print(c)

fun(1, 3, b=2)

3.不定长参数

  • 定义函数时,可以在形参前面加一个*,这样这个形参可以获取到所有的实参,它会将所有的实参保存到一个元组中。

  • 带*号的形参只能有一个,可以和其他参数配合使用。

  • *形参 只能接受位置参数,不能接受关键字参数

  • **形参 可以接收其他的关键字参数,它会将这些参数统一保存到字典当中。字典的key就是参数的名字,字典的value就是参数的值。

  • **形参只有一个,并且必须写在所有参数的后面。

  • 通常将*参数写为*args,将**参数写为**kwargs。这是一种公认的规范写法。

def fun2(*args, **kwargs):  # 不定长参数的正规写法
    pass

# 位置传参的不定长参数:只能存在一个参数有*号, 可以和其他的传参方式一起配合使用, 位置参数不能放到不定长参数的后面,不定长参数 *a 要放到关键字传参的前面

def fun(c, *a, d):  
    print(a)
    r = 0
    for i in a:
        r += i
    print(r)

fun(1, 3, 4, d=2)
  • 位置传参必须放到不定长参数 (*args或**kwargs) 的前面
  • 关键字传参要放到关键字传参的不定长参数(**kwargs)之前。
  • 万能参数:
def fun1(*g, **e):  # 这样的写法,称作万能参数) 
    print(e)
    print(g)
    # print(h)    
# 这里的h是打印不出来的。虽然传入了h=1,但是h被保存到字典内后,原h=1即被删除。

fun1(1, 2,  a=1, b=2, c=3, d=4, h=1)

结果如下:
在这里插入图片描述


4.函数的解包(也称拆包)

传递实参时,也可以在序列类型的参数前添加星号,这样它会自动的将序列中元素依次作为参数传递。
要求序列中的元素的个数必须和形参的个数一致。

def fun(a, b, c):
    print(a)
    print(b)
    print(c)

tuple1 = (1, 2, 3)
dict1 = {
    'a': 1, 'b': 2, 'c': 3}
# 参数的拆包
# fun(*tuple1)
fun(**dict1)

结果如下
在这里插入图片描述


Part 2

1. 函数的返回值

  • 返回值就是函数执行以后返回的结果
  • 通过return来指定函数的返回值
  • return后面可以跟任意对象,返回值甚至可以是一个函数

2. 文档字符串

  • help() 是Python中内置函数,通过help()函数可以查询Python中函数的用法。
  • 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是对函数的说明。
    例如:
'''
这个函数的功能是计算阶乘。
'''

3. 函数的作用域

  • 作用域(scope)
    作用域指的是变量生效的区域
    在Python中一共有两种作用域
  • 全局作用域
    • 全局作用域在程序执行时创建,在程序执行结束时销毁
    • 所有函数以外的区域都是全局作用域
    • 在全局作用域中定义的变量,都是全局变量,全局变量可以在程序的任意位置进行访问
    • 函数作用域
    • 函数作用域在函数调用时创建,在调用结束时销毁。
    • 函数每调用一次就会产生一个新的函数作用域。
    • 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问。
    global关键字,用来在局部作用域内声明全局变量。global关键字必须写在声明的局部变量前,也就是说,在该局部作用域内,global前方不能出现其要声明的全局变量,否则会报错。
a = 1


def fun():
    # 使局部作用域变为全局作用域的办法
    # a = 10   # a=10写在这里的话,会报错。
    global a  # 声明此处的变量a是全局变量
    a = 10  # a的重新赋值
    print(a)
    
    def fun1():
        print(a)
    fun1()


fun()   # 输出两个10


print(a)   # 输出结果也为10

4. 命名空间

  • 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
  • locals()用来获取当前作用域的命名空间
  • 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
  • 返回值是一个字典
  • globals() 可用来获取全局作用域的命名空间。
    需要注意的是,此全局变量不是指所有定义过的变量,而是指全局作用域的变量,不含所有局部变量。初学者容易在这里顾名思义而走入误区。
  • 将locals()写在全局作用域的位置,也可以获取到全局作用域的命名空间。
a = 1
b = 2
c = 3
d = 4


def fun():
    a = 5
    b = 6
    e = locals()  # 获取局部作用域的命名空间 
    f = globals()  # 获取全局作用域的命名空间 

    print(e)  # 得到的结果为{'b': 6, 'a': 5}
    print(f)   # 得到的结果中'a': 1, 'b': 2, 'c': 3, 'd': 4, 'fun': <function fun at 0x000001FE90B61EA0>}等一些本地自带的变量......
    pass

fun()

s = locals()  # 获取当前作用域的所有变量,并且以一个字典的形式返回出来
print(s)   # 结果同上边的 f

5. 递归函数

  • 递归是解决问题的一种方式,它的整体思想,是将一个大问题分解为一个个的小问题,直到问题无法分解时,在去解决问题
  • 递归式函数有2个条件
    • 1. 基线条件 问题可以被分解为最小问题,当满足基线条件时,递归就不执行了
    • 2. 递归条件 可以将问题继续分解的条件
    举例,普通方法我们这样定义求阶乘函数:
def fun(n):
     r = 1
     for i in range(1, n + 1):
         r *= i
     return r

用递归的思想,则这样写:

def fun(n):  # fun(n) 是为了求取n的阶乘
    if n == 1:
        return 1
    return n * fun(n - 1)

掌握递归的思想,在算法的写作中是一件大有裨益的事情。


Part 3

1. 高阶函数

  • 接收函数作为参数,或者将函数作为返回值返回的函数就是高阶函数。
# 如,写一个能够筛选出序列中偶数的高阶函数。
def fun1(fn):  # fn是函数的参数(形参) 形参等于实参  fn = fun2  a
    list1 = []
    for i in range(101):
        if fn(i):  # fn(i) = fun2(i)
            list1.append(i)
    return list1


def fun2(i):
    if i % 2 == 0:
        return True

print(fun1(fun2))

2. 闭包

  • 将函数作为返回值也是高阶函数我们也称为闭包。
  • 闭包的好处
    通过闭包可以创建一些只有当前函数能访问的变量。
    可以将一些私有数据藏到闭包中。
  • 行成闭包的条件
    函数嵌套。
    将内部函数作为返回值返回。
    内部函数必须要使用到外部函数的变量。
# 定义一个外部函数
def fun_out(num1):

    # 定义一个内部函数
    def fun_inner(num2):
        res = num1 + num2
        return res

    return fun_inner
 # 这样就满足了闭包的三个要求。

再看另一种写法(不是闭包):

# 这种写法在内部函数中重现定义了num1,导致在调用时原先传入的num1被销毁,内部函数没有使用到外部函数的变量,所以这不是闭包。
def fun_out(num1):

    # 定义一个内部函数
    def fun_inner(num2):
        num1 = 10
        res = num1 + num2
        # print(res)
        return res

    print(num1)      # num1依然为传入值1
    fun_inner(1)
    print(num1)     # num1依然为传入值1
    return fun_inner

f=fun_out(1)       # f的功能成了计算 10 + num2  的值。

要想解决,可以在中间加上一个nonlocal关键字
nonlocal num1告诉解释器,这里使用的num1不是本地的num1,是外部变量num1。

def fun_out(num1):

    # 定义一个内部函数
    def fun_inner(num2):
        nonlocal num1  # 告诉解释器,这里使用的不是本地的num1, 是外部变量的num1
        num1 = 10
        res = num1 + num2
        # print(res)
        return res

    print(num1)
    fun_inner(1)
    print(num1)
    return fun_inner

3. 装饰器的引入

  • 我们可以直接通过修改函数中的代码来完成需求,但是会产生以下一些问题:
    1.如果修改的函数多,修改起来会比较麻烦。
    2.不方便后期的维护。
    3.这样做会违反开闭原则(ocp)
    • 程序的设计,要求开发对程序的扩展,要关闭对程序的修改
    装饰器示例:
def fun(fn, *args):
    print('函数开始执行')
    r = fn(*args)
    print(r)
    print('函数执行结束')

4. 装饰器的使用

  • 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展。
  • 在开发中,我们都是通过装饰器来扩展函数的功能的。
  • 装饰器是一个特殊的闭包函数

这里写一个功能为 验证登录 的装饰器函数:

# 假如说这个装饰器是一个验证登录的装饰器
def old_fun(a):

    def new_fun(*args, **kwargs):  # 不定长参数, 不管你传多少参数,\
    或者你传不传参数,我这个不定长参数都可以保证代码不会出错
        print('函数开始执行')
        # print(a())
        a(*args, **kwargs)
        print('函数执行结束')

    return new_fun
  • 装饰器语法糖写法:
@old_fun     # 装饰器的语法糖写法:  @old_fun =   'r = old_fun(fun)
def fun():
    print('我是fun函数')
    # return '我是fun函数'

fun()

执行结果:
在这里插入图片描述
感谢大家的访问与支持,您的支持将是博主坚持更新的最大动力!更多精彩内容敬请关注!

目录
相关文章
|
28天前
|
测试技术 Python
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
106 31
【03】做一个精美的打飞机小游戏,规划游戏项目目录-分门别类所有的资源-库-类-逻辑-打包为可玩的exe-练习python打包为可执行exe-优雅草卓伊凡-持续更新-分享源代码和游戏包供游玩-1.0.2版本
|
1月前
|
人工智能 Python
【02】做一个精美的打飞机小游戏,python开发小游戏-鹰击长空—优雅草央千澈-持续更新-分享源代码和游戏包供游玩-记录完整开发过程-用做好的素材来完善鹰击长空1.0.1版本
【02】做一个精美的打飞机小游戏,python开发小游戏-鹰击长空—优雅草央千澈-持续更新-分享源代码和游戏包供游玩-记录完整开发过程-用做好的素材来完善鹰击长空1.0.1版本
56 7
|
1月前
|
存储 人工智能 运维
【01】做一个精美的打飞机小游戏,浅尝阿里云通义灵码python小游戏开发AI编程-之飞机大战小游戏上手实践-优雅草央千澈-用ai开发小游戏尝试-分享源代码和游戏包
【01】做一个精美的打飞机小游戏,浅尝阿里云通义灵码python小游戏开发AI编程-之飞机大战小游戏上手实践-优雅草央千澈-用ai开发小游戏尝试-分享源代码和游戏包
199 48
【01】做一个精美的打飞机小游戏,浅尝阿里云通义灵码python小游戏开发AI编程-之飞机大战小游戏上手实践-优雅草央千澈-用ai开发小游戏尝试-分享源代码和游戏包
|
1月前
|
程序员 Linux Python
python中模板和包的使用
本文介绍了 Python 模块和包的基本概念及使用方法。模块是 Python 程序结构的核心,每个以 `.py` 结尾的源文件都是一个模块,包含可重用的代码。文章详细讲解了模块的导入方式(如 `import` 和 `from...import`),模块的搜索顺序,以及如何创建和发布自己的模块。此外,还介绍了包的概念,包是包含多个模块的特殊目录,并通过 `__init__.py` 文件定义对外提供的模块列表。最后,文章简述了如何使用 `pip` 工具管理第三方模块的安装与卸载。作者:大石头的笔记;来源:稀土掘金。
|
2月前
|
分布式计算 MaxCompute 对象存储
|
2月前
|
Python 容器
[oeasy]python048_用变量赋值_连等赋值_解包赋值_unpack_assignment _
本文介绍了Python中变量赋值的不同方式,包括使用字面量和另一个变量进行赋值。通过`id()`函数展示了变量在内存中的唯一地址,并探讨了变量、模块、函数及类类型的地址特性。文章还讲解了连等赋值和解包赋值的概念,以及如何查看已声明的变量。最后总结了所有对象(如变量、模块、函数、类)都有其类型且在内存中有唯一的引用地址,构成了Python系统的基石。
36 5
|
3月前
|
大数据 数据处理 开发者
Python中的迭代器和生成器:不仅仅是语法糖####
本文探讨了Python中迭代器和生成器的深层价值,它们不仅简化代码、提升性能,还促进了函数式编程风格。通过具体示例,揭示了这些工具在处理大数据、惰性求值及资源管理等方面的优势。 ####
|
3月前
|
持续交付 Python
如何在Python中自动解决模块和包的依赖冲突?
完全自动解决所有依赖冲突可能并不总是可行,特别是在复杂的项目中。有时候仍然需要人工干预和判断。自动解决的方法主要是提供辅助和便捷,但不能完全替代人工的分析和决策😉。
|
23天前
|
存储 缓存 Java
Python高性能编程:五种核心优化技术的原理与Python代码
Python在高性能应用场景中常因执行速度不及C、C++等编译型语言而受质疑,但通过合理利用标准库的优化特性,如`__slots__`机制、列表推导式、`@lru_cache`装饰器和生成器等,可以显著提升代码效率。本文详细介绍了这些实用的性能优化技术,帮助开发者在不牺牲代码质量的前提下提高程序性能。实验数据表明,这些优化方法能在内存使用和计算效率方面带来显著改进,适用于大规模数据处理、递归计算等场景。
58 5
Python高性能编程:五种核心优化技术的原理与Python代码
|
2月前
|
Python
[oeasy]python055_python编程_容易出现的问题_函数名的重新赋值_print_int
本文介绍了Python编程中容易出现的问题,特别是函数名、类名和模块名的重新赋值。通过具体示例展示了将内建函数(如`print`、`int`、`max`)或模块名(如`os`)重新赋值为其他类型后,会导致原有功能失效。例如,将`print`赋值为整数后,无法再用其输出内容;将`int`赋值为整数后,无法再进行类型转换。重新赋值后,这些名称失去了原有的功能,可能导致程序错误。总结指出,已有的函数名、类名和模块名不适合覆盖赋新值,否则会失去原有功能。如果需要使用类似的变量名,建议采用其他命名方式以避免冲突。
52 14

热门文章

最新文章

推荐镜像

更多