Python 函数(lambda 匿名函数、自定义函数、装饰器)基本使用指南

Python 函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段

lambda 匿名函数

对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁;

对于不需要多次复用的函数,使用lambda 表达式可以在用完之后立即释放,提高程序执行的性能。

格式:

  • 一般形式

    lambda arguments: expression
    
    # 示例:
    add_lambda = lambda a, b: a + b
    print(add_lambda(10, 20))		# 输出:30
    
    # 不将 lambda 函数分配给变量,直接使用
    result = (lambda x: x * 2)(3)
    print(result)  					# 输出 6
    
  • 含 if 判断的形式

    lambda arguments: value_if_true if condition else value_if_false
    
    # 示例:
    a = lambda x : "偶数" if x % 2 == 0 else "奇数"
    print(a(10))		# 输出:偶数
    

lambda 函数与 def 函数的区别:

  • lambda 可以立刻传递(无需变量) ,自动返回结果
  • lambda 在内部只能包含一行代码
  • lambda 是一个为编写简单的函数而设计的,而 def 用来处理更大的任务
  • lambda 可以定义一个匿名函数,而 def 定义的函数必须有一个名字

自定义函数

语法

  • 基本语法:

    def functionName(param1, param2, ..., paramN):
       # function_suite
       return [expression]
    
    • def :关键字,定义函数的声明

    • functionName :自定义函数名称

    • (param1, param2, … , paramN) :定义入参

      • Python中,根据实际参数的类型不同,函数参数的传递方式可分为按值传递和按引用传递

        • 按值传递:适用于字符串、数字、元组

          按值传递的参数值在函数代码中发生改变,函数外面的变量值并不会受到任何影响

        • 按引用传递:适用于列表,字典、集合

          按引用传递的参数值在函数中的值改变了,函数外部的值也改变

      • 可以为参数设定一个默认的值,调用函数时如果没有传参,则使用默认值,而如果传参了,则会覆盖默认值

        默认参数通常放置在参数列表的末尾,以便在函数调用时更灵活。如果将默认参数放在非默认参数之前,则在函数调用时必须使用参数名称传参。

        def my_sum(num1, num2=1):
            return num1 + num2
        
      • 参数名称前面可以加一个星号:表示它会将传递进来的(一个或多个)值转为元组类型

        def my_def(*str):
            print(str)
        
        my_def("a")			# 输出:('a',)
        my_def("a", "b")	# 输出:('a','b')
        
      • 参数名称前面可以加两个星号:表示该参数需要接收字典类型的数据,即传参时需要用等号赋值的形式

        def my_def(**str):
            print(str)
        
        my_def(a=1)			# 输出:{'a': 1}
        my_def(a=1, b=2)	# 输出:{'a': 1, 'b': 2}
        
      • 配置 * 作为函数的一个形参:Python 不会对该 * 做任何事情,但是它将知道之后的所有参数都应作为关键字参数(键值对,也被称为 kwargs)来调用。即使它们没有默认值。

    • function_suite :函数的逻辑处理代码块

    • return [expression] :结束函数,并选择性地返回一个或多个值给调用方

      函数返回值可以是各种数值,字符,类,函数等对象,返回多个值时会被打包为一个元组

      如果函数没有显式指定返回值,则默认返回None,表示空值。None是一个特殊的Python对象,表示缺失或无效的值。

      没有返回值时,return 可省略

  • 注:

    • 函数内容以冒号起始,并且缩进

    • 函数的第一行语句可以选择性地使用文档字符串(用于存放函数说明)

    • 主程序调用函数时,如果函数没有在之前定义,那就会报出错误

      但如果是在函数中去调用函数,那函数定义的先后就不受影响

    • 调用函数传参时,可以通过位置和参数名称两种方法传参

      def person_info(name, age):
          print(f"Name: {name}")
          print(f"Age: {age}")
      
      # 位置传参
      person_info(25, "Alice")
      # 参数名称传参
      person_info(age=25, name="Alice")
      
    • 返回多个值的解包:

      在函数调用时,可以使用多个变量来接收函数返回的多个值。这被称为解包,可以通过位置或变量名来解包返回的值。

      def calculate(x, y):
          sum = x + y
          difference = x - y
          return sum, difference
      
      result_sum, result_diff = calculate(8, 3)
      print(result_sum)  	# 输出: 11
      print(result_diff)  # 输出: 5
      
    • 由于 python 是不需要编译的,所以其函数 def 是可执行的一条语句,也就意味着直到函数被调用前,这个函数都不存在,直到函数被调用时,def 语句才会创建一个新的函数对象,并赋予名字


函数嵌套

  • python 函数还有一大特性就是支持函数的嵌套。所谓的函数嵌套,就是函数里面又有函数

    def f1():
        print('hello')
        def f2():
            print('python')
        f2()
    f1()
    
  • 函数嵌套可以保证内部函数的隐私。

    内部函数只能被外部函数所调用和访问,不会暴露在全局作用域,因此,如果函数内部有一些隐私数据,不想暴露在外,那就可以使用函数的嵌套,将其封装在内部函数中,只通过外部函数来访问。其另一个作用就是可以简化代码。


函数变量作用域

  • python 函数中变量的作用域和其他语言类似,分为全局变量和局部变量

    • 全局变量:函数外定义的变量,程序任意位置都可以访问
    • 局部变量:函数内定义的变量
  • 在 python 中不能直接在函数内部随意改变全局变量的值(直接修改会报错)

    如果需要修改在函数中修改全局变量的值,需要先在函数内使用 global 关键字声明该变量是全局变量

    a = 10
    def myPrint():
        global a
        a = a + 1
    
    myPrint()
    print(a)	# 输出:11
    
  • 如果函数内与函数外定义了同名变量,则函数内部就是函数内部定义的值,函数外则是函数外定义的值

  • 映射至函数嵌套。如果内部函数要修改外部函数的值,需要先在内部函数内使用 nonlocal 关键字声明一下

    def myPrint():
        a = 10
        def myPrintIn():
            nonlocal a
            a = a + 1
        myPrintIn()
        print(a)
    
    myPrint()	# 输出:11
    

闭包

  • 和嵌套函数类似,不同的是外部函数返回的是一个函数,而不是一个具体的值。

    当一个内部函数引用了外部函数的变量,并且在外部函数执行完毕之后仍然存在,这个内部函数就被认为是一个闭包。

  • 闭包的主要优点是:

    • 可以保护隐藏在外部函数作用域中的数据,使其不能被意外修改。
    • 可以为外部函数提供一种延续性,即使外部函数已经执行完毕,闭包仍然可以访问和使用外部函数的变量。
    • 可以允许在多个函数调用之间共享数据。
    • 可以使代码更加简洁。
  • 闭包一般与装饰器一起使用!

  • 示例:

    # 定义闭包函数
    def color_print(color):
        def print_with_color(text):
            print(f"\033[{color}m{text}\033[0m")
        return print_with_color		# 返回内部函数,形成闭包
    
    # 调用闭包函数并赋值变量(创建不同的颜色打印函数)
    red_print = color_print("31")  		# 红色
    green_print = color_print("32")  	# 绿色
    
    # 调用颜色打印函数
    red_print("Hello, World!")
    green_print("Hello, World!")
    

装饰器

  • 装饰器是 Python 中一种强大而有用的编程工具,它允许修改、扩展或包装函数或方法的行为,而无需修改其原始代码。

    装饰器通常用于代码重用、修改函数的行为、日志记录、权限检查、性能测量等方面。

  • 在 Python 中,装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。这个新函数通常会在调用被装饰的函数之前或之后执行一些额外的操作,实现将额外的功能添加到原始函数上。

    装饰器通过在被装饰的函数定义之前使用 @ 符号来应用。

  • 基本语法:

    from functools import wraps
    
    # 定义装饰器
    def my_decorator(func):
        # 定义装饰器的内部函数
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 执行原始函数之前的操作
            result = func(*args, **kwargs)	 # 执行原始函数并获取返回值
            # 执行原始函数之后的操作
            return result
        return wrapper
    
    • functools 模块中的 @wraps 装饰器:是一个用于创建装饰器的实用工具。它通常与自定义装饰器一起使用,以确保被装饰的函数保留原始函数的元数据(如函数名、文档字符串、参数列表等)。

      当创建一个装饰器时,通常会包装或修改原始函数,这可能会导致原始函数的元数据丢失。使用 @wraps 装饰器可以解决这个问题,它会将包装函数的元数据设置为与原始函数相同。

      @wraps 装饰器非必需,但推荐使用,比如使用自定义装饰器装饰使用 fastapi 框架定义的路由函数时,不加会报错(解析参数失败)!

    • 装饰器的内部函数 wrapper 的参数列表通常使用 *args**kwargs,以便能够接收任意数量和类型的位置参数和名称参数,并将它们传递给原始函数。

    • func(*args, **kwargs):调用原始函数并获取其返回值。

    • 在装饰器的返回语句中,通常是返回 wrapper 函数,注意:是返回 wrapper,不是返回 wrapper()

    • 在装饰器中传递参数:可以在装饰器函数外部包装一个额外的函数,该额外函数接受参数并返回装饰器函数。

      这样,就可以通过调用包装函数来为装饰器传递参数。

  • 示例:

    import datetime
    
    # 定义装饰器外部包装函数
    def timestamp_decorator(arg1, arg2):
        # 定义装饰器
        def decorator(func):
            # 定义装饰器的内部函数
            @wraps(func)
            def wrapper(*args, **kwargs):
                # 获取当前时间
                timestamp = datetime.datetime.now()
                # 格式化时间戳
                formatted_timestamp = timestamp.strftime("%Y-%m-%d %H:%M:%S")
                # 打印时间戳并调用原函数
                print("[{}]".format(formatted_timestamp), end=" ")
                return func(*args, **kwargs)
            return wrapper
        return decorator
    
    # 应用装饰器
    @timestamp_decorator(arg1='', arg2='')
    def print_with_timestamp(str):
        print(str)
    
    # 调用函数
    print_with_timestamp("Hello, World!")	# 输出:[2023-07-16 02:04:59] Hello, World!
    

为自定义函数写数据类型注解

  • 语法示例

    from typing import List
    
    # 示例函数,接受一个整数列表作为输入,并返回一个整数值。
    def fun(questions: List[int]) -> int:
        # 函数体
        return 1
    
    • questions :传进去的参数

      :List[int] :表明传入的 questions 类型是整数列表

    • -> int :表明函数的返回值是 int 类型

    注意::List[int]-> int 都只起到提示作用,并不会改变传入值返回值的类型和值。

  • 优点:

    • 类型注解:

      通过在函数定义的参数和返回值上进行类型注解,明确指定了函数的输入和输出类型,这样其他开发人员在使用和阅读代码时更容易理解函数的预期行为和使用方式。

    • 错误预防:

      类型注解可以帮助开发人员在编写代码时捕获潜在的类型错误。如果无意中将其他类型的数据传递给函数,或者函数返回了错误的类型,类型检查器可以在开发阶段或静态分析阶段发现问题,避免了在运行时出现潜在错误。

    • 文档说明:

      类型注解可以作为代码文档的一部分,帮助其他开发人员了解函数的预期输入和输出。它可以增加代码的可读性,让其他人更容易理解函数的目的和返回值的含义。


常用装饰器

装饰器是 Python 中一种强大的编程工具,它允许在不修改函数本身的情况下,添加、修改或扩展函数的行为。

常用的 Python 装饰器:

  • @staticmethod 和 @classmethod : 用于将方法标记为静态方法和类方法。

  • @abstractmethod : 用于定义抽象基类和抽象方法,确保子类实现了这些方法

    from abc import abstractmethod
    class Person:
        @staticmethod
        def static_method():
            pass
    
        @classmethod
        def class_method(cls):
            pass
        
        @abstractmethod
        def abstract_method(cls):
            pass
    
  • @dataclass :用于自动为类生成特殊的方法和默认的特殊方法,以简化数据类的创建和管理。

    @dataclass 可以用来装饰包含属性的类,它会自动生成一些通常需要手动编写的方法,如 __init__()__repr__()__eq__() 等,从而减少了代码的重复和冗余。

    它引入自 Python 3.7,并在 Python 3.8 中得到了增强(如字段类型注解、默认值、默认值工厂函数等)

    from dataclasses import dataclass
    @dataclass
    class Point:
        x: int
        y: int
    
  • @property 和 @<attribute_name>.setter : 用于创建属性(getter)和设置属性(setter)方法,其中 <attribute_name> 应该替换为实际的属性名称。通常用于访问和修改私有实例属性

    注:私有实例属性名称以一个下划线 _ 开头,这是一种约定,用于指示这是一个受保护的属性,不应该直接访问。

    通过属性的 getter 和 setter 方法来访问和修改属性值是一种良好的习惯,因为可以在属性访问过程中添加额外的逻辑或验证。

    from abc import abstractmethod
    
    class Person:
        def __init__(self, name):
            self._name = name
    
        @property
        def name(self):
            return self._name
    
        @name.setter
        def name(self, value):
            self._name = value
    
    person = Person("b")
    person.name = "a"
    print(person.name)		# 输出:a
    
  • @wraps : 用于保留原始函数的元数据(例如文档字符串、函数名等),通常在自定义装饰器中使用。


常用内置函数

open():打开并操作文件

  • open() 函数是 Python 内置的文件操作函数,用于打开文件以进行读取、写入或追加操作。

    通常和 with 语句一起使用,在文件操作完成后,文件会被自动关闭,不需要显式调用 file.close()。这是一种良好的习惯,可以避免资源泄漏问题。

  • 语法如下:

    open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
    
    • file :要打开的文件的路径

    • mode :打开文件的操作模式

      选项有:

      • ‘r’ :只读模式(打开文件以进行读取),默认。如果文件不存在,则引发 FileNotFoundError

      • ‘w’ :写入模式(打开文件以进行写入)。如果文件存在,则会覆盖已有内容;如果文件不存在,则创建文件

      • ‘a’ :追加模式(打开文件以进行写入)。如果文件存在,将在文件末尾追加数据;如果不存在,则创建文件

      • ‘x’ :独占创建模式(打开文件以供写入)。如果文件已存在,会引发 FileExistsError。用于确保在创建文件时不会覆盖现有文件。

      • ‘t’:文本模式,默认。与上述模式组合(例如 ‘rt’、‘wt’),用于处理文本文件

      • ‘b’ :二进制模式。与上述模式组合(例如 ‘rb’、‘wb’),用于处理二进制文件,如图像、音频或视频文件

      • **‘+’ **:读写模式与上述模式组合,用于同时读取和写入文件

        • ‘r+’:读写模式,打开文件以供读取和写入。如果文件不存在,会引发 FileNotFoundError
        • ‘w+’:写入和读取模式,打开文件以供读取和写入。如果文件不存在,会创建一个新文件,如果文件存在,会覆盖已有内容
        • ‘a+’:追加和读取模式,打开文件以供读取和写入。如果文件不存在,会创建一个新文件。如果文件存在,写入的内容会追加到文件末尾,而不会清空文件。
      • ‘U’ :通用换行模式,打开文件以供读取。自动识别不同平台的换行符(\n\r\r\n

        Python 3 不建议使用

      注意,不同模式的组合可能会导致不同的文件操作行为,因此在选择模式时要特别小心,以确保符合预期

    • buffering :控制文件的缓冲方式,通常可以忽略,-1 表示使用系统默认的缓冲大小

    • encoding :用于指定文本文件的编码方式,通常在文本模式下使用。例如:encoding='utf-8'

    • errors :指定编码错误的处理方式

    • newline :控制换行符的处理方式,在文本模式下使用

    • closefd :指定是否在关闭文件时同时关闭文件描述符,默认为 True。在某些情况下,可能需要将其设置为 False

    • opener :自定义文件打开器,通常不需要使用

  • open() 返回的文件对象是一个 Python 内置的文件流,它提供了多种方法来进行文件操作,包括读取、写入、定位文件指针等。

    以下是一些常用的文件对象方法:

    • read(size=-1) : 从文件中读取指定数量的字符或字节。如果未指定大小或大小为负数,则会读取整个文件

    • readline(size=-1) : 从文件中读取一行内容。如果指定 size,则读取指定数量的字符或字节

    • readlines(hint=-1) : 从文件中读取多行内容,并将其以列表形式返回。

      每一行都作为列表中的一个元素,保留了行尾的换行符(\n),换行符可以使用 strip() 函数进行处理。

      注意,它会将整个文件内容加载到内存中,所以对于非常大的文件可能会导致内存占用问题。如果处理大型文件,可能需要逐行读取文件而不是一次性读取所有行。

      hint 参数:

      • hint 参数应该作为位置参数传递,使用关键字参数传递会报错!

      • 指定 hint=0 或 hint=-1:用于读取所有行(直到结束符 EOF)并返回列表

      • 指定 hint 的值大于0:

        • 对于文本数据:字符数量以 UNICODE 编码字符集的字符为单位计算。

          支持多字节的字符,如中文一个汉字表示1字符,空格算一个字符。

          对于文本文件,换行符是否计入字符,有如下两种情况:

          • 指定字符数中间包含 \n,则 \n 算做字符
          • 如果 \n 在指定内容末尾,则不计入字符,算作换行符
        • 在二进制模式下,字符数量是以 ASCII 码对应的单字节为单位来计算的,不剔除换行符,字符 ‘\n’ 占用一个字节长度;

        • 如果 hint 的值 大于前 n 行的总字数但小于前 n+1 行的总字数,则执行函数会读取文件的前 n+1 行

    • write(string) :将字符串写入文件。这个方法在文件以写入模式 ‘w’、‘a’、‘x’ 或 ‘w+’ 打开时可用

    • writelines(lines) : 将字符串列表写入文件,通常用于写入多行文本

    • seek(offset, whence=0) : 将文件指针移动到指定位置

      offset 是相对于 whence 参数的偏移量

      • whence 值为 0(默认),表示从文件开头计算偏移量
      • whence 值为 1,表示从当前位置计算偏移量
      • whence 值为 2,表示从文件末尾计算偏移量
    • tell() :返回当前文件指针的位置,以字节为单位

    • flush() : 将缓冲区的数据刷新到文件。通常,文件对象会自动缓冲数据,但在某些情况下,可能需要手动刷新

  • 示例:

    with open('example.txt', 'w+') as file:
        file.write('Hello, World!') 	# 写入文本
        file.seek(0)  					# 将文件指针移回文件开头
        content = file.read() 			# 读取文件内容
        
        file.writelines(['Line 1\n', 'Line 2\n', 'Line 3\n'])  	# 写入多行文本
        file.seek(0)
        cleaned_lines = [line.strip() for line in lines]  		# 去除每行末尾的换行符
    

eval():执行字符串命令

  • 将字符串当成有效表达式来求值并返回计算结果。

  • eval 函数的作用是去掉字符串最外面的一对引号,然后把去掉最外侧引号的字符串通过 python 语句的方式进行操作

  • 变量是为了获取去掉引号后的值,以便进行操作

  • 可同 input() 函数一起使用,获得用户输入的数值,使用方式为 变量=eval(input())

  • 示例:

    eval('print("aaaa")')		# 输出:aaaa
    

round() :截取小数点后几位

  • round() 是 Python 内置函数,用于将一个浮点数四舍五入为指定的小数位数。

  • 语法:

    round(number, ndigits)
    
    • number :是要四舍五入的浮点数。
    • ndigits :是要保留的小数位数,可以省略,默认为 0
  • 注意:round() 函数在处理浮点数时有一些潜在的问题,可能会导致结果与预期不符。如果对浮点数没有精度要求时,可以使用 round 函数。如果对浮点数的精度要求较高,推荐使用 decimal 模块。

  • 以下是一些常见的“坑”:

    • 精度丢失问题: 浮点数在计算机内部以二进制形式表示,某些十进制分数无法准确表示为二进制分数。这可能导致四舍五入时的精度丢失。
    • 奇偶规则: 当一个数刚好在两个可能的结果中间时,round() 函数采用奇偶规则(round half to even),也称为银行家舍入规则。这意味着它会选择最接近的偶数。
    • 大数问题: 对于非常大的浮点数,四舍五入可能会导致不准确的结果,因为浮点数的精度有限。
  • 示例:

    print(round(2.875,2))	# 输出:2.88
    # 精度丢失问题
    print(round(1.225, 2))	# 输出: 1.22,而不是 1.2。因为十进制的1.225无法准确表示为二进制分数
    # 奇偶规则
    print(round(0.5))  		# 输出: 0
    # 大数问题
    print(round(1e16 + 0.5))  # 输出: 10000000000000000,而不是 10000000000000001
    

vars() 函数

  • 用于返回对象的 __dict__ 属性。

    __dict__ 是一个字典,包含了对象的命名空间(即对象的属性和值)。

  • 将一个类实例传递给 vars() 函数时,它会返回该实例的属性和值的字典。

    将一个类传递给 vars() 函数,它会返回该类的命名空间(类属性和方法)。

  • 示例:

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    person = Person("Alice", 30)
    
    # 返回实例的属性和值的字典
    person_dict = vars(person)
    print(person_dict)  # 输出:{'name': 'Alice', 'age': 30}
    
    # 返回类的命名空间(属性和方法)
    person_class_dict = vars(Person)
    print(person_class_dict) 
    # 输出:{'__module__': '__main__', '__init__': <function Person.__init__ at 0x...>, ...}
    

其他的简单函数

  • type() :用于获取对象的数据类型

  • range() :返回一个指定范围内的数字序列

    基本语法:

    range(start, stop, step)
    
  • len():用于获取序列(字符串、列表、元组等)的长度或项目数量

    fruits = ['apple', 'banana', 'orange', 'grape']
    print(len(fruits))		# 输出:4
    
  • sum() :计算序列(例如列表)中的元素总和

  • max()min() :用于找到序列中的最大值和最小值

  • abs():返回一个数的绝对值

  • sorted():对可迭代对象进行排序

  • enumerate():用于在迭代中获取元素的索引和值

  • zip():将多个可迭代对象的元素配对成元组

  • map():对可迭代对象的每个元素应用函数

  • filter():过滤可迭代对象中的元素

  • all()any():分别用于检查可迭代对象中的所有元素是否为 True 或是否至少有一个元素为 True

  • str.join():将字符串列表或其他可迭代对象的元素连接成一个字符串

  • chr()ord():分别用于将整数转换为字符和将字符转换为整数

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年11月2日
下一篇 2023年11月2日

相关推荐