Python学习第四天

  1. 元组是什么

    一个 Python 元组(tuple) 是一个有序的、不可变的集合。这意味着元组中的元素有固定的顺序,一旦创建就不能被修改(添加、删除或改变)。


    以下是元组的一些主要特点:

    • 有序性 (Ordered): 元组中的元素是有序的,这意味着它们有固定的位置,并且你可以通过索引来访问它们。

    • 不可变性 (Immutable): 这是元组最重要的特性。一旦元组被创建,你不能改变它的内容。如果你需要一个可变的数据结构,你可以使用列表(list)。

    • 允许重复元素 (Allows Duplicate Elements): 元组可以包含重复的元素。

    • 可以包含不同类型的数据 (Can Contain Different Data Types): 元组可以包含不同数据类型的元素,比如整数、浮点数、字符串甚至其他元组或列表。


    如何创建元组

    元组通常使用圆括号 ()​ 来创建,元素之间用逗号 ,​ 分隔。

    Python

    # 创建一个空元组
    empty_tuple = ()
    print(empty_tuple) # 输出: ()
    
    # 创建一个包含整数的元组
    numbers = (1, 2, 3, 4, 5)
    print(numbers) # 输出: (1, 2, 3, 4, 5)
    
    # 创建一个包含不同数据类型的元组
    mixed_tuple = (1, "hello", 3.14, True)
    print(mixed_tuple) # 输出: (1, 'hello', 3.14, True)
    
    # 创建一个只包含一个元素的元组,注意逗号是必需的
    single_element_tuple = (1,)
    print(single_element_tuple) # 输出: (1,)
    
    # 如果没有逗号,Python 会将其视为一个普通的表达式
    not_a_tuple = (1)
    print(type(not_a_tuple)) # 输出: <class 'int'>
    

    元组的常见操作

    尽管元组是不可变的,但你仍然可以执行一些操作:

    • 访问元素 (Accessing Elements): 使用索引来访问元组中的元素(索引从 0 开始)。

    • 切片 (Slicing): 获取元组的一部分。

    • 连接 (Concatenation): 使用 +​ 运算符连接两个或多个元组来创建一个新元组。

    • 重复 (Repetition): 使用 *​ 运算符重复元组中的元素。

    • 检查元素是否存在 (Checking for Element Existence): 使用 in​ 关键字。

    • 获取长度 (Getting Length): 使用 len()​ 函数。


    元组的应用场景

    元组因为其不可变性,在很多场景下非常有用:

    • 作为字典的键 (As Dictionary Keys): 由于元组是不可变的,它们可以作为字典的键,而列表则不能。

    • 返回多个值 (Returning Multiple Values): 函数可以方便地返回一个元组来表示多个返回值。

    • 数据完整性 (Data Integrity): 当你需要确保数据在创建后不会被意外修改时,元组是理想的选择。

    • 固定集合 (Fixed Collections): 例如,表示坐标(x, y)、RGB 颜色值 (R, G, B) 等,这些值通常是固定不变的。

    理解元组的不可变性是关键。如果你需要一个可以修改的序列,那么列表(list)会是更合适的选择。

  2. 什么是函数

    一个函数(Function) 是组织好的、可重复使用的、用于执行单一或相关联操作的代码块。你可以把它想象成一个小的、独立工作的机器,它接收一些输入(可选),执行特定的任务,然后可能返回一个结果。


    函数的核心概念

    以下是函数的几个核心特点:

    • 封装性 (Encapsulation): 函数将一系列操作封装在一起,形成一个独立的单元。这样做的好处是你可以专注于这个单元的功能,而不需要关心其内部的实现细节。

    • 可重用性 (Reusability): 定义一次函数后,你可以在程序的不同地方多次调用它,而无需重复编写相同的代码。这大大减少了代码量,提高了开发效率。

    • 模块化 (Modularity): 函数将程序分解成更小、更易于管理和理解的模块。当程序变得复杂时,这种模块化设计能帮助你更好地组织和维护代码。

    • 抽象性 (Abstraction): 函数提供了一种抽象层次。你只需要知道函数是做什么的(它的功能),而不需要知道它是如何做到的(它的具体实现)。


    函数的基本结构

    大多数编程语言中,函数的结构都包含以下几个部分:

    1. 名称 (Name): 用于标识函数,方便你调用它。

    2. 参数 (Parameters) / 输入 (Inputs): 函数可以接受零个或多个参数。这些参数是函数执行任务所需的数据。

    3. 函数体 (Function Body): 这是函数内部的代码块,包含了函数执行特定任务的所有指令。

    4. 返回值 (Return Value) / 输出 (Output): 函数执行完毕后,可以返回一个结果。返回值是可选的,有些函数可能只执行操作而不返回任何值。


    为什么使用函数?

    • 避免代码重复: 这是最主要的原因。如果一段代码需要多次使用,将其封装成函数可以避免重复编写,降低出错的可能性。

    • 提高代码可读性: 将复杂任务分解为小的、有明确功能的函数,使代码更容易理解和阅读。

    • 便于维护: 当你需要修改某个功能时,只需要修改对应的函数,而不会影响到程序的其他部分。

    • 更容易测试: 独立的功能单元更容易进行单元测试,确保其正确性。


    一个 Python 函数的例子

    Python

    # 定义一个名为 'greet' 的函数,它接受一个参数 'name'
    def greet(name):
        """
        这个函数用于向指定的名字问好。
        """
        message = f"你好,{name}!欢迎来到 Python 世界!"
        print(message) # 执行打印操作
    
    # 调用函数
    greet("Alice") # 输出: 你好,Alice!欢迎来到 Python 世界!
    greet("Bob")   # 输出: 你好,Bob!欢迎来到 Python 世界!
    
    # 另一个例子:计算两个数的和并返回结果
    def add_numbers(num1, num2):
        """
        这个函数计算两个数字的和并返回结果。
        """
        sum_result = num1 + num2
        return sum_result # 返回计算结果
    
    # 调用函数并接收返回值
    result = add_numbers(5, 3)
    print(f"5 + 3 = {result}") # 输出: 5 + 3 = 8
    

    在这个例子中:

    • ​greet​ 和 add_numbers​ 是函数名。

    • ​name​、num1​ 和 num2​ 是参数。

    • ​print(message)​ 和 sum_result = num1 + num2​ 是函数体内的操作。

    • ​add_numbers​ 函数使用 return sum_result​ 返回了一个值。

    函数是编程中非常基础且重要的概念,掌握它对于编写高效、可维护的代码至关重要。

  3. 什么是参数

    在编程中,参数(Parameters) 是函数或方法定义中用于接收输入值的占位符。它们是函数在执行其任务时所需要的数据。你可以把参数想象成函数在开始工作前,你需要给它提供的一些信息或工具。


    参数和实参(Arguments)的区别

    这是一个经常让人混淆的地方,但理解它们之间的区别很重要:

    • 参数(Parameters):

      • 在函数定义中声明。

      • 它们是变量名,表示函数期望接收的数据类型和数量。

      • 它们是函数内部使用的局部变量。

      • 例子:

    • 实参(Arguments):

      • 在函数调用时传递给函数的值。

      • 它们是实际的数据,会在函数执行时赋值给对应的参数。

      • 例子:

    简而言之:参数是定义时用的变量名,实参是调用时给的实际值。


    为什么需要参数?

    参数的引入主要有以下几个原因:

    1. 灵活性和通用性: 函数可以在不同的情况下处理不同的数据,而不需要为每种情况都编写一个新函数。例如,一个 add​ 函数可以计算任意两个数的和,而不是只能计算 2 和 3 的和。

    2. 数据传递: 参数是向函数传递数据的主要方式。函数通过参数接收外部数据,从而执行特定的操作。

    3. 解耦: 参数帮助解耦函数和它所操作的数据。函数只关心它接收到的参数,而不关心这些数据从何而来。

    4. 提高可重用性: 相同的函数逻辑可以通过不同的参数实现多种用途,大大提高了代码的可重用性。


    参数的类型(Python 中常见的参数类型)

    在 Python 中,参数可以有几种不同的类型,这允许函数在接收输入时具有更大的灵活性:

    1. 位置参数(Positional Arguments)


      这是最常见的参数类型。它们按照在函数定义中出现的顺序,与函数调用时传递的实参一一对应。

      Python

      def describe_pet(animal_type, pet_name):
          print(f"我有一只 {animal_type},它叫 {pet_name}。")
      
      describe_pet("狗", "旺财") # "狗" 赋值给 animal_type,"旺财" 赋值给 pet_name
      
    2. 关键字参数(Keyword Arguments)


      你可以在函数调用时使用 参数名=值​ 的形式来传递实参。这样做的优点是:

      • 可以不按顺序传递: 你可以随意调换参数的顺序。

      • 提高可读性: 清晰地指明了哪个值对应哪个参数。

      Python

      def describe_pet(animal_type, pet_name):
          print(f"我有一只 {animal_type},它叫 {pet_name}。")
      
      describe_pet(pet_name="旺财", animal_type="狗") # 顺序调换了,但依然正确
      
    3. 默认参数(Default Arguments)


      你可以在函数定义时为参数指定一个默认值。如果调用函数时没有为该参数提供实参,就会使用这个默认值。

      Python

      def greet(name, message="你好"): # 'message' 有默认值
          print(f"{message},{name}!")
      
      greet("张三")         # 输出: 你好,张三! (使用默认消息)
      greet("李四", "下午好") # 输出: 下午好,李四! (使用新消息)
      
    4. 可变位置参数(Arbitrary Positional Arguments) - *args​


      如果你不确定函数会接收多少个位置参数,可以使用 *args​。它会将所有额外的、未被明确定义的位置实参收集到一个元组中。

      Python

      def sum_all(*numbers): # 'numbers' 会是一个元组
          total = 0
          for num in numbers:
              total += num
          return total
      
      print(sum_all(1, 2, 3))       # 输出: 6
      print(sum_all(10, 20, 30, 40)) # 输出: 100
      
    5. 可变关键字参数(Arbitrary Keyword Arguments) - **kwargs​


      如果你不确定函数会接收多少个关键字参数,可以使用 **kwargs​。它会将所有额外的、未被明确定义的关键字实参收集到一个字典中。

      Python

      def print_info(**details): # 'details' 会是一个字典
          for key, value in details.items():
              print(f"{key}: {value}")
      
      print_info(name="小明", age=30, city="北京")
      # 输出:
      # name: 小明
      # age: 30
      # city: 北京
      

    理解参数如何工作是编写灵活、可重用和易于维护代码的关键。你有没有考虑过在自己的代码中如何利用这些参数类型来提高函数的通用性呢?

  4. 什么是形参,什么是实参

    好的,我们来详细解释一下编程中形参和实参的区别。这是一个非常核心且容易混淆的概念,但理解了它,你就能更好地掌握函数的运作方式。


    形参(Parameters)

    形参是在函数定义中声明的变量。它们是函数的“蓝图”或“规格”,告诉函数在被调用时需要接收什么类型和数量的数据。形参是占位符,它们在函数被定义时就已经存在,但还没有具体的数值。

    你可以把形参想象成一个表格的列标题。当你设计表格时,你会先写上“姓名”、“年龄”、“城市”等标题,但这些标题本身并没有具体的数据。

    特点:

    • 定义时使用: 出现在 def​ 关键字后面,函数名称旁的括号里。

    • 占位符: 它们是变量名,用于在函数体内部引用即将传入的数据。

    • 局部作用域: 形参在函数内部是局部变量,只在函数执行期间有效。

    Python 示例:

    Python

    def greet(name, age): # 'name' 和 'age' 是形参
        print(f"你好,{name}!你今年 {age} 岁。")
    

    在这个例子中,name​ 和 age​ 就是形参。它们表示 greet​ 函数期望接收一个名字和一个年龄。


    实参(Arguments)

    实参是在函数调用时传递给函数的实际值。它们是具体的数据,用于填充函数定义中的形参占位符。当函数被调用时,这些实参会被赋值给对应的形参,然后函数体内的代码才能使用这些具体的值进行操作。

    接着上面的表格例子,实参就是你向表格中填写的具体数据行:比如“张三”、“30”、“北京”。

    特点:

    • 调用时使用: 出现在你调用函数时,函数名称旁的括号里。

    • 实际值: 它们是具体的数据(字面量、变量、表达式的结果等),会被赋值给对应的形参。

    • 传递给函数: 实参是向函数内部传递信息的方式。

    Python 示例:

    Python

    greet("小明", 25) # "小明" 和 25 是实参
    

    在这里,"小明"​ 和 25​ 就是实参。当调用 greet("小明", 25)​ 时,"小明"​ 这个字符串会被赋值给形参 name​,而整数 25​ 会被赋值给形参 age​。


    总结区别

    特点

    形参(Parameters)

    实参(Arguments)

    定义位置

    函数定义时

    函数调用时

    本质

    占位符、变量名

    实际传递的值

    作用

    定义函数所需的数据类型和数量

    为函数提供具体的数据

    生存期

    函数被定义时存在,运行时赋值

    函数被调用时传递,运行时被处理

    示例

    ​def func(a, b):​中的a, b​

    ​func(10, 20)​中的10, 20​


  5. 函数如何返回值

    函数通过使用 return​ 语句来返回值。return​ 语句会立即终止函数的执行,并将 return​ 后面的值(或表达式的结果)传递回调用它的地方。


    return​ 语句的核心作用

    1. 返回结果: 这是 return​ 最主要的功能。函数执行完其任务后,通常会产生一个结果。return​ 语句就是用来把这个结果送出去的。

    2. 终止函数: 一旦执行到 return​ 语句,函数会立即停止运行,即便后面还有其他代码,也不会被执行。

    3. 将控制权交还给调用者: return​ 不仅传递值,还将程序的执行流程从函数内部转回到调用该函数的代码行。


    如何使用 return​

    我们来看一些 Python 例子:

    1. 返回一个值

    这是最常见的情况。函数执行计算或处理,然后返回一个明确的结果。

    Python

    def add(a, b):
        """计算两个数的和并返回结果。"""
        result = a + b
        return result # 返回变量 result 的值
    
    # 调用函数并接收返回值
    sum_result = add(5, 3)
    print(f"两数之和是: {sum_result}") # 输出: 两数之和是: 8
    

    在这个例子中,add​ 函数计算 a + b​,然后通过 return result​ 把这个和返回给调用者。sum_result​ 变量接收了这个返回的值。

    2. 返回多个值(作为元组)

    虽然 return​ 语句本身只能返回一个“对象”,但你可以将多个值封装在一个元组(tuple)中返回。在 Python 中,这非常常见,并且会默认将多个逗号分隔的值自动打包成一个元组。

    Python

    def get_user_info():
        """返回用户的姓名和年龄。"""
        name = "爱丽丝"
        age = 30
        return name, age # 实际返回的是一个元组 ('爱丽丝', 30)
    
    # 调用函数并解包返回值
    user_name, user_age = get_user_info() # 这里使用了元组解包
    print(f"姓名: {user_name}, 年龄: {user_age}") # 输出: 姓名: 爱丽丝, 年龄: 30
    
    # 也可以不解包,直接作为一个元组接收
    user_data = get_user_info()
    print(f"用户信息: {user_data}") # 输出: 用户信息: ('爱丽丝', 30)
    

    这里 return name, age​ 实际上等同于 return (name, age)​。

    3. 不返回任何值(隐式返回 None​)

    如果函数中没有 return​ 语句,或者 return​ 语句后面没有跟任何值,那么函数会隐式地返回特殊值 None​。None​ 在 Python 中表示“没有值”或“空”。

    Python

    def greet(name):
        """向指定名字问好,不返回任何值。"""
        print(f"你好,{name}!")
        # 这里没有 return 语句
    
    result = greet("鲍勃")
    print(f"greet 函数的返回值是: {result}") # 输出: greet 函数的返回值是: None
    

    即使你没有明确写 return None​,当函数执行完毕但没有遇到 return​ 语句时,Python 也会默认返回 None​。

    4. return​ 语句的位置和行为

    ​return​ 语句会立即终止函数的执行。这意味着 return​ 后面的代码不会被执行。

    Python

    def check_positive(number):
        """检查一个数是否为正数。"""
        if number > 0:
            return "是正数" # 如果条件满足,函数在这里就结束了
        print("这行代码在数字非正数时才会被打印。") # 只有在 number <= 0 时才执行
        return "不是正数或零"
    
    print(check_positive(10)) # 输出: 是正数
    print(check_positive(-5)) # 输出: 这行代码在数字非正数时才会被打印。
                              # 输出: 不是正数或零
    

    为什么要返回值?

    • 结果传递: 函数执行某个任务,并将结果传递给程序的其他部分使用。

    • 链式操作: 一个函数的返回值可以作为另一个函数的输入。

    • 状态指示: 返回布尔值 (True​/False​) 来指示某个操作是否成功。

    • 清晰的接口: return​ 语句明确定义了函数完成其工作后会提供什么输出。

    理解 return​ 语句是掌握函数编程的关键一步,因为它定义了函数与程序其余部分进行通信的主要方式。

  6. 将99乘法表制作为函数,要求有2个参数,两个参数分别控制乘法表的最大乘数和最大被乘数。

    好的,我们可以创建一个 Python 函数来生成九九乘法表,并用两个参数分别控制最大乘数和最大被乘数。


    创建乘法表函数

    这里是实现这个功能的函数:

    Python

    def print_multiplication_table(max_multiplicand, max_multiplier):
        """
        生成并打印乘法表。
    
        Args:
            max_multiplicand (int): 被乘数的最大值(控制行数)。
            max_multiplier (int): 乘数的最大值(控制列数)。
        """
        if not (isinstance(max_multiplicand, int) and isinstance(max_multiplier, int)):
            print("错误:最大被乘数和最大乘数必须是整数。")
            return
    
        if max_multiplicand <= 0 or max_multiplier <= 0:
            print("错误:最大被乘数和最大乘数必须是正整数。")
            return
    
        print(f"--- 乘法表 (被乘数到 {max_multiplicand}, 乘数到 {max_multiplier}) ---")
    
        for i in range(1, max_multiplicand + 1):  # 被乘数 (行)
            for j in range(1, max_multiplier + 1):  # 乘数 (列)
                # 使用 f-string 格式化输出,确保每个表达式占固定宽度,使表格对齐
                print(f"{j} * {i} = {i * j:<4}", end=" ")
            print() # 每行打印完后换行
        print("---------------------------------------")
    
    

    函数解析

    • ​print_multiplication_table(max_multiplicand, max_multiplier)​: 这是我们定义的函数。

      • ​max_multiplicand​: 这个参数控制被乘数的最大值,也就是乘法表的行数。

      • ​max_multiplier​: 这个参数控制乘数的最大值,也就是乘法表的列数。

    • 参数校验: 函数开始部分加入了简单的类型和值检查,确保传入的参数是正整数,这样可以避免不必要的错误。

    • 外层循环 for i in range(1, max_multiplicand + 1)​: 这个循环遍历被乘数,从 1 到 max_multiplicand​。i​ 代表当前正在处理的被乘数。

    • 内层循环 for j in range(1, max_multiplier + 1)​: 这个循环遍历乘数,从 1 到 max_multiplier​。j​ 代表当前正在处理的乘数。

    • ​print(f"{j} {i} = {i j:<4}", end=" ")​:

      • 我们使用 f-string 来格式化输出。

      • ​{j} {i} = {i j}​ 是乘法表达式。

      • ​{i * j:<4}​ 中的 :<4​ 是格式化说明符,表示将结果左对齐并占用至少 4 个字符的宽度。这有助于让表格的列保持对齐,即使乘积的位数不同。

      • ​end=" "​ 使得每个表达式打印后不换行,而是以一个空格结尾,这样同一行的表达式可以并排显示。

    • ​print()​: 内层循环结束后,这行代码会打印一个空行,实现换行效果,让下一行的乘法表达式从新的一行开始。


    如何使用函数

    你可以通过传入不同的参数来生成不同大小的乘法表:

    Python

    # 生成标准的九九乘法表 (被乘数到9,乘数到9)
    print_multiplication_table(9, 9)
    
    # ---
    
    # 生成一个较小的乘法表 (被乘数到5,乘数到3)
    print_multiplication_table(5, 3)
    
    # ---
    
    # 尝试无效输入
    print_multiplication_table(0, 5)
    print_multiplication_table(5, "abc")
    

    这个函数不仅能生成传统的九九乘法表,还能让你根据需要定制乘法表的范围,非常灵活!

  7. input是如何使用的?试着使用input输出参数传递给99乘法表的函数

    ​input()​ 函数在 Python 中用来从用户那里获取输入。当程序执行到 input()​ 函数时,它会暂停并等待用户在命令行或终端中输入一些文本,然后按回车键。用户输入的内容会作为字符串返回给程序。


    input()​ 的基本用法

    ​input()​ 函数可以接受一个可选的字符串参数,这个字符串会作为提示信息显示给用户,告诉他们应该输入什么。

    Python

    # 基本用法,没有提示信息
    user_input = input()
    print(f"你输入了: {user_input}")
    
    # 带提示信息
    name = input("请输入你的名字:")
    print(f"你好,{name}!")
    

    使用 input()​ 给九九乘法表函数传递参数

    现在,我们将结合 input()​ 函数和我们之前定义的九九乘法表函数 print_multiplication_table​。我们会让用户输入乘法表的最大被乘数和最大乘数。

    首先,确保你的 print_multiplication_table​ 函数定义在你的 Python 文件中:

    Python

    # 定义你的乘法表函数
    def print_multiplication_table(max_multiplicand, max_multiplier):
        """
        生成并打印乘法表。
    
        Args:
            max_multiplicand (int): 被乘数的最大值(控制行数)。
            max_multiplier (int): 乘数的最大值(控制列数)。
        """
        if not (isinstance(max_multiplicand, int) and isinstance(max_multiplier, int)):
            print("错误:最大被乘数和最大乘数必须是整数。")
            return
    
        if max_multiplicand <= 0 or max_multiplier <= 0:
            print("错误:最大被乘数和最大乘数必须是正整数。")
            return
    
        print(f"--- 乘法表 (被乘数到 {max_multiplicand}, 乘数到 {max_multiplier}) ---")
    
        for i in range(1, max_multiplicand + 1):  # 被乘数 (行)
            for j in range(1, max_multiplier + 1):  # 乘数 (列)
                print(f"{j} * {i} = {i * j:<4}", end=" ")
            print()
        print("---------------------------------------")
    
    

    获取用户输入并调用函数

    在函数定义之后,你可以像这样使用 input()​ 来获取用户输入:

    Python

    # --- 获取用户输入并调用函数 ---
    
    print("欢迎使用自定义乘法表生成器!")
    
    try:
        # 1. 获取最大被乘数
        # input() 返回的是字符串,需要用 int() 转换为整数
        num1_str = input("请输入最大被乘数(例如 9):")
        max_mplicand = int(num1_str) # 将用户输入的字符串转换为整数
    
        # 2. 获取最大乘数
        num2_str = input("请输入最大乘数(例如 9):")
        max_muliplier = int(num2_str) # 将用户输入的字符串转换为整数
    
        # 3. 将用户输入的值作为实参传递给函数
        print_multiplication_table(max_mplicand, max_muliplier)
    
    except ValueError:
        print("输入无效!请输入有效的整数。")
    except Exception as e:
        print(f"发生了一个错误: {e}")
    
    

    代码解释

    1. ​num1_str = input("请输入最大被乘数(例如 9):")​:

      • ​input()​ 函数会显示括号内的提示信息给用户。

      • 程序会在这里暂停,等待用户输入。

      • 用户输入的内容(例如 9​)会以字符串的形式存储在 num1_str​ 变量中。

    2. ​max_mplicand = int(num1_str)​:

      • 由于 input()​ 总是返回字符串,而我们的乘法表函数需要整数作为参数,所以我们必须使用 int()​ 函数将 num1_str​ 转换为整数。如果用户输入了非数字字符,int()​ 转换会失败并引发 ValueError​。

    3. ​try...except ValueError​:

      • 这是一个错误处理的结构。try​ 块中的代码会被尝试执行。

      • 如果 try​ 块中的 int()​ 转换失败(例如用户输入了 "abc"),程序不会崩溃,而是会跳到 except ValueError:​ 块中执行相应的错误提示信息,增强了程序的健壮性。

    4. ​print_multiplication_table(max_mplicand, max_muliplier)​:

      • 最后,我们将用户输入并转换后的整数值(现在它们是实参)传递给 print_multiplication_table​ 函数。函数会接收这些值并生成相应的乘法表。


    运行方式

    将上述所有代码(函数定义部分和获取用户输入部分)保存为一个 .py​ 文件,例如 interactive_table.py​。然后在命令行中运行:

    Bash

    python interactive_table.py
    

    程序会提示你输入数值,然后根据你的输入生成乘法表。试着输入不同的正整数,或者尝试输入非数字字符,看看它是如何工作的吧!

  8. 默认print会自动换行,如何让print不自动换行

    ​print()​ 函数默认在每次输出结束后会添加一个换行符,这就是为什么每次 print()​ 语句的输出都会另起一行。要改变这种行为,你可以使用 print()​ 函数的 end​ 参数。


    使用 end​ 参数

    ​end​ 参数允许你指定 print()​ 语句结束时要打印的字符。默认情况下,end​ 的值是 '\n'​(表示换行符)。如果你想让 print()​ 不换行,或者以其他字符结尾,只需将 end​ 参数设置为你想要的值。

    1. 不换行(用空字符串结尾)

    将 end​ 参数设置为一个空字符串 ''​,就可以让 print()​ 在输出结束后不添加任何字符,从而实现不换行的效果。

    Python

    print("Hello", end='')
    print("World")
    # 输出: HelloWorld
    

    2. 用其他字符结尾

    你也可以指定 end​ 为其他字符,比如一个空格,一个逗号,或者任何你想要的字符串。

    Python

    print("Apple", end=' ') # 以空格结尾
    print("Banana", end=', ') # 以逗号和空格结尾
    print("Cherry")
    # 输出: Apple Banana, Cherry
    

    3. 在循环中应用

    这在循环中非常有用,比如你想要在同一行打印一系列结果,而不是每个结果都占一行。

    Python

    for i in range(1, 6):
        print(i, end=' ') # 每次打印一个数字后,以空格结尾,不换行
    print("\n循环结束") # 循环结束后,手动换行
    # 输出: 1 2 3 4 5 
    #       循环结束
    

    总结

    要让 print()​ 不自动换行,你只需要在 print()​ 函数中添加 end=''​ 参数。如果你希望用其他字符(如空格)来分隔输出而不是换行,就可以将 end​ 设置为相应的字符,例如 end=' '​。

  9. 打印列表时,如何倒序打印,就是先输出字典最后一位,最后输出字典的第一位

    在 Python 中,当你想要倒序打印列表中的元素时,有几种常见且有效的方法。


    如何倒序打印列表

    1. 使用切片 [::-1]​

    这是最简洁和 Pythonic 的方法之一。通过使用步长为 -1​ 的切片,你可以创建一个原列表的逆序副本,而不会修改原列表。

    Python

    my_list = [10, 20, 30, 40, 50]
    reversed_list = my_list[::-1]
    print(reversed_list)
    # 输出: [50, 40, 30, 20, 10]
    

    如果你只是想倒序打印,不需要创建新的列表变量,可以直接在 print​ 中使用:

    Python

    my_list = ['apple', 'banana', 'cherry']
    print(my_list[::-1])
    # 输出: ['cherry', 'banana', 'apple']
    

    2. 使用 reversed()​ 函数

    ​reversed()​ 是一个内置函数,它返回一个迭代器,该迭代器以反向顺序生成列表中的元素。这在处理非常大的列表时效率很高,因为它不会一次性创建整个逆序列表的副本。你需要遍历这个迭代器或者将其转换为列表(如果你需要一个实际的逆序列表)。

    Python

    my_list = [1, 2, 3, 4, 5]
    
    # 遍历 reversed 对象
    print("使用 reversed() 遍历:")
    for item in reversed(my_list):
        print(item, end=' ')
    # 输出: 5 4 3 2 1
    
    # 将 reversed 对象转换为列表
    reversed_list_from_iterator = list(reversed(my_list))
    print(f"\n转换为列表: {reversed_list_from_iterator}")
    # 输出: 转换为列表: [5, 4, 3, 2, 1]
    

    3. 使用 list.reverse()​ 方法(修改原列表)

    这是一个原地修改列表的方法,意味着它会直接改变原始列表的顺序,而不是创建新的列表。如果你不需要保留原始列表的顺序,并且希望节省内存,这是个不错的选择。

    Python

    my_list = [100, 200, 300]
    print(f"原始列表: {my_list}") # 输出: 原始列表: [100, 200, 300]
    
    my_list.reverse() # 原地反转列表
    
    print(f"反转后的列表: {my_list}") # 输出: 反转后的列表: [300, 200, 100]
    

    请注意,list.reverse()​ 方法没有返回值(它返回 None​),它只是修改了列表本身。


    总结和选择

    • ​[::-1]​ 切片:最常用和简洁,创建新列表。适用于大多数情况,特别是你不想修改原列表时。

    • ​reversed()​ 函数:返回一个迭代器,内存效率高,尤其适用于大型列表。通常在需要遍历但不需要完整逆序列表时使用。

    • ​list.reverse()​ 方法:原地修改原列表,不创建新列表。适用于当你不需要保留原列表顺序,并希望节省内存时。

    选择哪种方法取决于你的具体需求:是需要一个逆序的副本,还是想直接修改原列表,以及对内存效率的要求。

  10. 如何列表进行去重


    如何对 Python 列表进行去重

    在 Python 中,对列表进行去重是常见的操作。由于列表可以包含重复元素,当你需要一个只包含唯一值的数据集时,就需要用到去重方法。这里有几种常用的方法,每种方法都有其适用场景和特点。


    1. 使用 set()​(最常用且推荐)

    ​set​ 是 Python 中的一种数据结构,它的主要特点是无序且不包含重复元素。利用这个特性,我们可以快速高效地对列表进行去重。

    优点:

    • 简洁高效: 代码量少,执行速度快,尤其适用于大型列表。

    • 内置支持: set​ 是 Python 的内置类型。

    缺点:

    • 会改变顺序: 由于 set​ 是无序的,转换后再转回列表时,元素的原始顺序可能会丢失。

    • 元素限制: 列表中的元素必须是可哈希的 (hashable),才能转换为 set​。这意味着,像列表或字典这样的可变类型不能直接作为 set​ 的元素。

    示例:

    Python

    my_list = [1, 2, 2, 3, 4, 4, 5, 'a', 'a', 'b']
    unique_elements = list(set(my_list))
    print(unique_elements)
    # 输出: [1, 2, 3, 4, 5, 'a', 'b'] (顺序可能不同)
    
    # 示例:顺序丢失
    my_list_ordered = [3, 1, 4, 1, 5, 9, 2, 6]
    unique_ordered = list(set(my_list_ordered))
    print(unique_ordered)
    # 输出: [1, 2, 3, 4, 5, 6, 9] (原始顺序 [3, 1, 4] 变成了 [1, 2, 3])
    

    2. 使用字典的 fromkeys()​ 方法(保持原始顺序,Python 3.7+ 推荐)

    从 Python 3.7 开始,字典保持插入顺序。利用字典 fromkeys()​ 方法的特性(它会为每个键创建一个 None​ 值,并且键是唯一的),可以实现去重并保留原始插入顺序。

    优点:

    • 保持顺序: 去重后的元素顺序与原列表一致。

    • 简洁高效: 同样非常简洁。

    缺点:

    • 元素限制: 同样要求列表中的元素必须是可哈希的。

    示例:

    Python

    my_list = [1, 2, 2, 3, 4, 4, 5, 'a', 'a', 'b']
    unique_elements_ordered = list(dict.fromkeys(my_list))
    print(unique_elements_ordered)
    # 输出: [1, 2, 3, 4, 5, 'a', 'b'] (保持了原始顺序)
    
    my_list_ordered = [3, 1, 4, 1, 5, 9, 2, 6]
    unique_ordered = list(dict.fromkeys(my_list_ordered))
    print(unique_ordered)
    # 输出: [3, 1, 4, 5, 9, 2, 6] (保留了原始顺序)
    

    3. 使用循环遍历(适用于不可哈希元素,或需要更细粒度控制)

    当你需要处理包含不可哈希元素(如嵌套列表、字典)的列表时,或者你需要对去重过程有更细致的控制时,可以使用循环遍历的方法。

    优点:

    • 通用性强: 可以处理包含不可哈希元素的列表。

    • 保持顺序: 可以轻松保持元素的原始顺序。

    • 控制灵活: 可以在去重过程中添加自定义逻辑。

    缺点:

    • 效率较低: 对于大型列表,效率不如 set()​ 或 dict.fromkeys()​。

    示例:

    Python

    # 适用于可哈希元素 (和 set() 效果类似,但效率低)
    my_list = [1, 2, 2, 3, 4, 4, 5]
    unique_elements = []
    for item in my_list:
        if item not in unique_elements:
            unique_elements.append(item)
    print(unique_elements)
    # 输出: [1, 2, 3, 4, 5]
    
    # 适用于包含不可哈希元素的列表 (例如列表中的列表)
    list_of_lists = [[1, 2], [3, 4], [1, 2], [5, 6], [3, 4]]
    unique_nested_lists = []
    for sublist in list_of_lists:
        if sublist not in unique_nested_lists:
            unique_nested_lists.append(sublist)
    print(unique_nested_lists)
    # 输出: [[1, 2], [3, 4], [5, 6]]
    

    总结与选择

    方法

    优点

    缺点

    适用场景

    ​set()​

    最快,代码简洁

    不保留顺序,要求可哈希

    最常见,当顺序不重要且元素可哈希时。

    ​dict.fromkeys()​

    保留顺序,简洁

    要求可哈希

    当需要去重并保持原始顺序,且元素可哈希时。

    循环遍历

    通用性强,处理不可哈希,保留顺序

    效率相对较低

    当列表中包含不可哈希元素,或需要自定义去重逻辑时。

    在大多数情况下,如果你不关心元素的顺序,使用 set()​ 是最推荐和最高效的方法。如果你需要保持原始顺序,并且元素都是可哈希的,那么 dict.fromkeys()​ 是一个极佳的选择。只有当列表包含不可哈希的元素时,才考虑使用循环遍历的方法。

    你通常会去重什么样的列表呢?

  11. 单个下划线是否可以作为变量名称,什么情况下会使用这种方式


    是的,单个下划线 _​ 可以作为变量名称。在 Python 中,这是一种特殊的用法,通常被称为“哑变量”或“临时变量”,表示你不关心这个变量的值。


    什么时候会使用单个下划线 _​ 作为变量名?

    使用 _​ 作为变量名,主要是为了代码的可读性和明确性,告诉阅读代码的人这个变量的值不会被用到,或者它有某种特殊的含义。

    以下是几种常见的使用场景:

    1. 忽略循环中的值

    当你遍历一个序列,但你只关心循环的次数,而不需要使用每次迭代的具体元素时,可以使用 _​。

    Python

    # 循环5次,但每次迭代的数字本身不重要
    for _ in range(5):
        print("Hello")
    
    # 输出:
    # Hello
    # Hello
    # Hello
    # Hello
    # Hello
    

    在这个例子中,range(5)​ 会生成 0, 1, 2, 3, 4,但我们对这些数字不感兴趣,所以用 _​ 来表示我们忽略它们。

    2. 忽略元组解包中的特定元素

    当从一个元组、列表或其他可迭代对象中解包多个值时,如果你只需要其中的一部分,可以用 _​ 来忽略你不关心的那些位置。

    Python

    data = ("Alice", 30, "New York", "Engineer")
    
    # 只关心名字和城市,忽略年龄和职业
    name, _, city, _ = data
    print(f"Name: {name}, City: {city}") # 输出: Name: Alice, City: New York
    
    # 也可以用来忽略函数返回的多个值中的一部分
    def get_coordinates():
        return 10, 20, 5 # x, y, z
    
    x, y, _ = get_coordinates() # 只获取 x 和 y,忽略 z
    print(f"X: {x}, Y: {y}") # 输出: X: 10, Y: 20
    

    3. 作为临时变量或占位符

    在一些简单的表达式或单行代码中,当确实需要一个变量来存储某个中间结果,但这个结果只用一次且不需要有语义化的名称时,_​ 也可以充当临时占位符。

    Python

    # 假设你只是想执行一个函数,它的返回值你暂时不需要
    # 比如一个打印日志的函数,它可能返回操作状态,但你不关心
    _ = print("Log message sent.")
    

    不过,对于这种简单的函数调用,通常不给变量赋值也是可以的,除非函数有非预期行为需要接收返回值。

    4. 交互式解释器中存储上一个表达式的结果

    在 Python 的交互式解释器(如 IDLE 或 Jupyter Notebook)中,单个下划线 _​ 会自动存储上一个表达式的计算结果。这在进行快速调试或临时计算时非常方便。

    Python

    >>> 2 + 3
    5
    >>> _
    5
    >>> "hello" + " world"
    'hello world'
    >>> _
    'hello world'
    

    5. 国际化/本地化中的函数名(惯例)

    在一些框架或库中,特别是在进行国际化(i18n)和本地化(l10n)时,单个下划线 _​ 也常被用作一个函数名,通常是用来标记需要翻译的字符串。这是一种约定俗成的惯例,而不是 Python 语言强制的。

    Python

    # 假设 _ 是一个用于翻译的函数
    # from some_translation_library import translate as _
    
    # msg = _("Hello, world!") # 标记这个字符串需要被翻译
    

    在这种情况下,_​ 作为一个函数名,和作为变量名的意义是不同的。


    总结

    虽然 Python 允许你用 ​ 作为任何常规变量名,但它的特殊意义(“我不在乎这个值”或“这是一个临时变量”)使得它在上述特定场景下使用,有助于提高代码的清晰度和可读性。滥用 ​ 可能会让代码难以理解,所以最好遵循其约定俗成的用法。

  12. 比如列表为['小明',18,'BeiJing'],如何快速为每个元素单独设置变量名


    当你的列表元素数量固定且你知道每个位置代表什么含义时,最快、最 Pythonic 的方法是使用序列解包(Sequence Unpacking)。


    使用序列解包为列表元素设置变量名

    序列解包允许你将列表(或其他可迭代对象,如元组、字符串等)中的元素直接赋值给对应数量的变量。它的核心思想是“形状匹配”:等号左边的变量数量必须和等号右边可迭代对象的元素数量相同。

    示例

    对于你的列表 ['小明', 18, 'BeiJing']​,你可以这样做:

    Python

    my_list = ['小明', 18, 'BeiJing']
    
    # 使用序列解包
    name, age, city = my_list
    
    print(f"姓名: {name}")  # 输出: 姓名: 小明
    print(f"年龄: {age}")    # 输出: 年龄: 18
    print(f"城市: {city}")    # 输出: 城市: BeiJing
    

    为什么这种方法好?

    1. 简洁高效: 一行代码就能完成多个变量的赋值,省去了逐个通过索引赋值的繁琐。

    2. 可读性强: 变量名直接反映了它们所代表的数据,让代码意图一目了然。

    3. Pythonic: 这是 Python 语言设计者推荐和鼓励的写法,符合 Python 的风格。

    序列解包的注意事项

    • 数量匹配: 等号左边的变量数量必须和右边列表(或可迭代对象)的元素数量严格一致。

      • 如果变量太多,而元素太少,会引发 ValueError: not enough values to unpack​。

      • 如果变量太少,而元素太多,会引发 ValueError: too many values to unpack​。

      Python

      # 错误示例:变量太多
      # a, b, c, d = [1, 2, 3] # ValueError
      
      # 错误示例:变量太少
      # a, b = [1, 2, 3] # ValueError
      
    • 星号表达式(*​)解包: 如果你想捕获列表的其余部分,可以使用星号表达式。这在元素数量不固定时非常有用。Python

      data = ['苹果', '香蕉', '橙子', '葡萄', '西瓜']
      first_fruit, second_fruit, *remaining_fruits = data
      print(f"第一个: {first_fruit}")     # 输出: 第一个: 苹果
      print(f"第二个: {second_fruit}")    # 输出: 第二个: 香蕉
      print(f"剩余水果: {remaining_fruits}") # 输出: 剩余水果: ['橙子', '葡萄', '西瓜']
      

    当你的列表结构固定且元素含义明确时,序列解包无疑是最优雅和高效的方法。

  13. 什么是深拷贝,什么是浅拷贝,什么情况下使用

    在 Python 中,拷贝(copy) 是指创建一个现有对象的副本。当处理复杂的数据结构,特别是包含嵌套对象(比如列表中的列表,字典中的字典)时,理解浅拷贝(shallow copy) 和深拷贝(deep copy) 的区别至关重要。


    什么是浅拷贝(Shallow Copy)?


    浅拷贝创建了一个新对象,但这个新对象仅复制了原始对象中最顶层的元素。如果原始对象包含对其他对象的引用(例如,一个列表包含其他列表作为元素),那么浅拷贝只会复制这些引用本身,而不是被引用的对象。这意味着,新对象和原始对象中的嵌套对象仍然指向内存中的同一个位置。

    如何创建浅拷贝

    1. 切片操作 [:]​ (适用于列表)

    2. ​list()​ 构造函数 (适用于列表)

    3. ​dict.copy()​ 方法 (适用于字典)

    4. ​copy​ 模块的 copy()​ 函数 (通用方法)


    什么是深拷贝(Deep Copy)?


    深拷贝创建了一个全新的独立对象。它不仅复制了原始对象的顶层元素,还会递归地复制原始对象中所有嵌套的对象。这意味着,深拷贝后的新对象与原始对象在内存中是完全独立的,对其中任何一个对象的修改都不会影响另一个。

    如何创建深拷贝

    深拷贝通常需要使用 Python 标准库 copy​ 模块中的 deepcopy()​ 函数。

    Python

    import copy
    
    original_list = [1, 2, [3, 4]]
    deep_copy_list = copy.deepcopy(original_list)
    
    print(f"原始列表: {original_list}")          # 输出: [1, 2, [3, 4]]
    print(f"深拷贝列表: {deep_copy_list}")    # 输出: [1, 2, [3, 4]]
    print(original_list is deep_copy_list)    # 输出: False (顶层对象不同)
    print(original_list[2] is deep_copy_list[2]) # 输出: False (嵌套对象也不同了)
    
    # 修改嵌套对象只影响其中一个列表
    original_list[2].append(5)
    print(f"修改后原始列表: {original_list}")     # 输出: [1, 2, [3, 4, 5]]
    print(f"修改后深拷贝列表: {deep_copy_list}") # 输出: [1, 2, [3, 4]] (未受影响)
    

    什么情况下使用深拷贝和浅拷贝?


    使用浅拷贝的情况

    当你的数据结构满足以下条件时,浅拷贝是合适的:

    1. 数据结构是扁平的: 也就是说,它不包含任何可变(mutble)的嵌套对象(例如,只包含数字、字符串或元组等不可变类型)。在这种情况下,浅拷贝和深拷贝的效果是一样的,浅拷贝的效率更高。

    2. 你希望共享部分数据: 当你明确希望新对象和原始对象共享某些嵌套的可变对象时,浅拷贝正是你想要的。例如,你可能需要创建配置文件的多个版本,它们共享大部分设置,只修改一小部分。

    3. 性能是关键因素: 深拷贝通常比浅拷贝更耗时、更耗内存,因为它需要递归遍历并复制所有对象。如果你的数据结构非常大且复杂,且你能够接受共享嵌套对象的风险,那么浅拷贝可能更高效。

    使用深拷贝的情况

    当你的数据结构满足以下条件时,深拷贝是必需的:

    1. 数据结构包含可变(mutable)的嵌套对象: 例如,列表包含列表、字典包含列表或字典,以及包含自定义类的实例等。

    2. 你需要完全独立的对象副本: 你希望对副本的任何修改都不会影响原始对象,反之亦然。这是为了保证数据的完整性和隔离性。

      • 配置管理: 当从一个基础配置创建多个变体时,你需要深拷贝以确保每个变体都是独立的。

      • 游戏状态: 复制游戏状态以实现“撤销”功能或尝试不同的游戏路径时,需要深拷贝来确保状态的完全独立。

      • 历史记录/快照: 当你需要保存一个数据结构在某个时间点的精确快照,并且不希望它受到后续修改的影响时。


    总结要点

    • 浅拷贝:复制顶层,嵌套的可变对象共享引用。

    • 深拷贝:复制所有层级,所有对象完全独立。

    记住,在处理复杂数据结构时,始终要思考你对副本的修改是否应该影响到原始数据。如果答案是“不应该”,那么很可能你就需要深拷贝。

  14. 列表是有序的还是无需的


    列表(List)在 Python 中是有序的。


    这意味着:

    • 元素有固定的位置: 列表中的每个元素都有一个确定的索引(从 0 开始),你可以通过这个索引来访问它。例如,my_list[0]​ 总是指向第一个元素。

    • 插入顺序会被保留: 当你向列表中添加元素时,它们会按照你添加的顺序被存储。当你遍历列表或打印列表时,元素的顺序和你添加它们的顺序是一致的。

    • 顺序是列表身份的一部分: 即使两个列表包含相同的元素,但如果它们的顺序不同,它们也被认为是不同的列表。

    举例说明:

    Python

    my_list = ['apple', 'banana', 'cherry']
    print(my_list[0])  # 输出:apple (始终是第一个)
    
    my_list.append('date')
    print(my_list)     # 输出:['apple', 'banana', 'cherry', 'date'] (保持了添加顺序)
    
    list_a = [1, 2, 3]
    list_b = [3, 2, 1]
    print(list_a == list_b) # 输出:False (顺序不同,所以不相等)
    

    与无序集合(如 set​)对比:

    相比之下,Python 中的 集合(Set) 是无序的。这意味着集合中的元素没有固定的位置或索引,并且你不能依赖它们的顺序。

    Python

    my_set = {'apple', 'banana', 'cherry'}
    print(my_set) # 每次打印的顺序可能不同,因为set是无序的
    

    所以,明确地说,Python 列表是有序的。

  15. 字典是有序的还是无需的

    字典(Dictionary)在 Python 中是否有序,取决于你使用的 Python 版本。


    Python 3.7 及更高版本:字典是有序的

    从 Python 3.7 开始,字典被正式保证为有序的。这意味着,当你向字典中添加键值对时,它们会按照插入的顺序进行维护。当你遍历字典,或者打印字典时,你会发现键值对的顺序和它们被添加时的顺序是完全一致的。

    这个改变是从 Python 3.6 开始引入的,但在 3.6 版本中,这只是一个 CPython(Python 的官方实现)的实现细节。到了 3.7 版本,它被提升为语言规范的一部分,也就是说,所有符合 Python 3.7 规范的解释器都必须保证字典的有序性。

    示例(Python 3.7+):

    Python

    my_dict = {}
    my_dict['apple'] = 1
    my_dict['banana'] = 2
    my_dict['cherry'] = 3
    
    print(my_dict)
    # 输出: {'apple': 1, 'banana': 2, 'cherry': 3}
    
    # 遍历时也会保持顺序
    for key, value in my_dict.items():
        print(f"{key}: {value}")
    # 输出:
    # apple: 1
    # banana: 2
    # cherry: 3
    

    Python 3.6:字典是有序的(作为实现细节)

    在 Python 3.6 中,字典的实现方式发生了变化,导致它们在大多数情况下看起来是有序的。然而,这并不是一个官方保证的特性。这意味着,虽然在 CPython 3.6 中你可能会观察到有序的行为,但在其他 Python 实现(如 PyPy)或其他情况下,这种顺序可能不被保证。


    Python 3.5 及更早版本:字典是无序的

    在 Python 3.5 及更早版本,以及 Python 2.x 系列中,字典是无序的。这意味着字典中的键值对没有固定的顺序,它们的存储和访问顺序可能会随着字典的修改、Python 版本的不同或运行环境的不同而变化。

    如果你在这些旧版本中需要有序字典,你需要使用 collections.OrderedDict​。


    总结

    • Python 3.7 及更高版本: 字典是有序的,并保证按插入顺序保存。

    • Python 3.6: 字典在 CPython 中有序(作为实现细节)。

    • Python 3.5 及更早版本: 字典是无序的。

    因此,在现代 Python 编程中(使用 Python 3.7+),你可以放心地依赖字典的有序性。

  16. 集合是什么


    什么是集合 (Set)?


    在 Python 中,集合 (Set) 是一种无序的不重复元素的集合。你可以把它们想象成数学中的集合概念。它们是可变的(mutable),这意味着你可以在创建后添加或删除元素。


    集合的主要特点

    1. 无序 (Unordered): 集合中的元素没有固定的顺序或索引。你不能通过索引来访问集合的元素,也不能依赖它们在内存中的排列顺序。每次打印集合,元素的顺序可能都不一样。

    2. 不重复元素 (No Duplicate Elements): 集合最大的特点就是不允许有重复的元素。如果你尝试向集合中添加一个已经存在的元素,集合不会改变。

    3. 可变 (Mutable): 你可以在集合创建后添加或删除元素。

    4. 元素必须是可哈希的 (Elements Must Be Hashable): 集合中的元素必须是不可变(immutable)的,例如数字、字符串、元组等。可变对象(如列表、字典、其他集合)不能作为集合的元素,因为它们是不可哈希的。


    如何创建集合

    1. 使用花括号 {}​: 这是最常用的创建集合的方式。注意,如果你创建空集合,不能只用 {}​,因为这会被认为是空字典。

    2. 使用 set()​ 构造函数:

      • 创建空集合时必须使用 set()​。

      • 可以从其他可迭代对象(如列表、元组、字符串)创建集合,这是一种常见的去重方法。


    集合的常见操作

    集合支持数学中集合的常见操作,这使得它们在处理唯一元素和关系时非常强大。

    1. 添加元素 (add()​):

    2. 移除元素 (remove()​, discard()​, pop()​, clear()​):

      • ​remove(element)​: 移除指定元素,如果元素不存在会引发 KeyError​。

      • ​discard(element)​: 移除指定元素,如果元素不存在不会引发错误。

      • ​pop()​: 随机移除并返回一个元素(因为集合是无序的,所以是随机的)。空集合调用会引发 KeyError​。

      • ​clear()​: 移除集合中的所有元素。

    3. 并集 (union()​ 或 |​): 返回两个或多个集合的所有唯一元素。

    4. 交集 (intersection()​ 或 &​): 返回两个或多个集合共有的元素。

    5. 差集 (difference()​ 或 -​): 返回在一个集合中但不在另一个集合中的元素。

    6. 对称差集 (symmetric_difference()​ 或 ^​): 返回在两个集合中但不同时存在于两个集合中的元素(即并集减去交集)。

    7. 子集 (issubset()​): 检查一个集合是否是另一个集合的子集。

    8. 超集 (issuperset()​): 检查一个集合是否是另一个集合的超集。


    集合的应用场景

    • 去重: 这是最常见的用途,将列表或其他可迭代对象转换为集合可以快速去除重复元素。

    • 成员检测: 检查某个元素是否在集合中(in​ 操作符)比在列表中通常更快,因为集合基于哈希表实现。

    • 数学集合操作: 执行并集、交集、差集等操作,这在处理多个数据集的关系时非常有用。

    理解集合的无序性和不重复性是关键,它使得集合成为处理唯一元素和进行高效成员测试的强大工具。

  17. 列表、字典、集合、元组的区别是什么,做表格进行对比。在什么场景下使用


    Python 中的列表 (List)、元组 (Tuple)、字典 (Dictionary) 和集合 (Set) 是四种核心的内置数据结构,它们各自有独特的特性和最佳使用场景。理解它们的区别对于编写高效和正确的 Python 代码至关重要。


    四大数据结构对比

    下面是一个表格,详细对比了这四种数据结构的特点:

    特性

    列表 (List)

    元组 (Tuple)

    字典 (Dictionary)

    集合 (Set)

    定义符号

    ​[]​方括号

    ​()​圆括号

    ​{}​花括号(键值对)

    ​{}​花括号(仅值),set()​创建空集合

    有序性

    有序(保持插入顺序)

    有序(保持插入顺序)

    有序(Python 3.7+ 保持插入顺序)

    无序

    可变性

    可变(可修改、添加、删除元素)

    不可变(创建后不能修改)

    可变(可修改、添加、删除键值对)

    可变(可添加、删除元素)

    元素类型

    可包含任意类型数据

    可包含任意类型数据

    键必须是可哈希的(不可变),值可以是任意类型

    元素必须是可哈希的(不可变)

    重复元素

    允许重复元素

    允许重复元素

    键不允许重复,值允许重复

    不允许重复元素

    主要用途

    序列、可变集合

    固定序列、不可变集合

    键值对映射,通过键查找值

    存储唯一元素,数学集合操作

    查找方式

    通过索引(list[index]​)

    通过索引(tuple[index]​)

    通过键(dict[key]​)

    不支持索引,通过in​检查成员

    内存占用

    相对较高(因可变性)

    相对较低

    较高(因键值对和哈希表)

    较高(因哈希表)


    什么场景下使用?

    1. 列表 (List)

    • 场景: 当你需要一个有序的、可变的元素集合,并且可能需要频繁地添加、删除或修改元素时。

    • 例子:

      • 存储用户在购物车中选择的商品列表。

      • 记录日志条目的序列。

      • 处理数据记录,需要按顺序访问或修改。

      • 实现堆栈(Stack)或队列(Queue)数据结构。

    2. 元组 (Tuple)

    • 场景: 当你需要一个有序的、不可变的元素集合时。元组一旦创建就不能修改,这使得它们在某些情况下更安全,也可以作为字典的键。

    • 例子:

      • 表示坐标 (x, y),如 (10, 20)​。

      • 函数返回多个值时,通常以元组的形式返回。

      • 作为字典的键(因为它是不可变的,所以可哈希)。

      • 存储固定不变的数据集合,如一周的七天名称,或月份名称。

    3. 字典 (Dictionary)

    • 场景: 当你需要存储键值对(key-value pairs),并通过唯一的键来快速查找和检索值时。

    • 例子:

      • 存储用户信息:{'name': 'Alice', 'age': 30, 'city': 'New York'}​。

      • 表示配置参数。

      • 缓存数据(通过键快速查找)。

      • 建立查找表,将一个值映射到另一个值。

    4. 集合 (Set)

    • 场景: 当你只需要存储唯一元素,并且不关心元素的顺序时。集合非常适合执行数学集合操作(如并集、交集、差集)。

    • 例子:

      • 快速去重: 将一个列表转换为集合来去除重复元素。

      • 成员资格测试: 高效地检查一个元素是否在一个大型集合中(element in my_set​)。

      • 查找两个数据集的共同元素(交集)。

      • 查找一个数据集中有而另一个数据集中没有的元素(差集)。


  18. 列表、字典、集合、元组的增删改查,怎么操作


    在 Python 中,列表 (List)、字典 (Dictionary)、集合 (Set) 和 元组 (Tuple) 是四种基本的数据结构。它们各自有不同的特性,因此在执行增加 (Add)、删除 (Delete)、修改 (Modify) 和查询 (Query) 操作时,方法也会有所不同。


    1. 列表 (List) - 有序、可变、允许重复

    列表是最灵活的序列类型,支持所有 CRUD (创建、读取、更新、删除) 操作。

    创建 (Create)

    • 字面量: my_list = [1, 2, 3, 'a', 'b']​

    • ​list()​ 构造函数: another_list = list(range(5))​

    增加 (Add)

    • ​append(element)​: 在列表末尾添加一个元素。Python

      my_list = [1, 2, 3]
      my_list.append(4)
      print(my_list) # [1, 2, 3, 4]
      
    • ​extend(iterable)​: 在列表末尾添加另一个可迭代对象的所有元素。Python

      my_list = [1, 2]
      my_list.extend([3, 4])
      print(my_list) # [1, 2, 3, 4]
      
    • ​insert(index, element)​: 在指定索引处插入元素。Python

      my_list = [1, 3, 4]
      my_list.insert(1, 2) # 在索引1处插入2
      print(my_list) # [1, 2, 3, 4]
      
    • ​+​ 运算符: 连接两个列表,创建新列表。Python

      list1 = [1, 2]
      list2 = [3, 4]
      combined_list = list1 + list2
      print(combined_list) # [1, 2, 3, 4]
      

    删除 (Delete)

    • ​del list[index]​: 根据索引删除元素。Python

      my_list = [1, 2, 3, 4]
      del my_list[1] # 删除索引1的元素 (2)
      print(my_list) # [1, 3, 4]
      
    • ​pop(index)​: 移除并返回指定索引的元素(默认移除最后一个)。Python

      my_list = [1, 2, 3, 4]
      removed_element = my_list.pop(2) # 移除并返回索引2的元素 (3)
      print(my_list) # [1, 2, 4]
      print(removed_element) # 3
      
    • ​remove(value)​: 移除第一个匹配的指定值。如果值不存在会引发 ValueError​。Python

      my_list = [1, 2, 3, 2]
      my_list.remove(2) # 移除第一个2
      print(my_list) # [1, 3, 2]
      
    • ​clear()​: 清空列表所有元素。Python

      my_list = [1, 2, 3]
      my_list.clear()
      print(my_list) # []
      

    修改 (Modify)

    • 通过索引赋值: 直接修改指定索引的元素。Python

      my_list = [1, 2, 3]
      my_list[1] = 20 # 将索引1的元素修改为20
      print(my_list) # [1, 20, 3]
      
    • 切片赋值: 修改一个范围内的元素,甚至改变列表大小。Python

      my_list = [1, 2, 3, 4, 5]
      my_list[1:3] = [20, 30] # 将索引1到2的元素修改
      print(my_list) # [1, 20, 30, 4, 5]
      
      my_list[1:3] = [200] # 也可以用不同数量的元素替换
      print(my_list) # [1, 200, 4, 5]
      

    查询 (Query)

    • 索引访问: list[index]​ 获取指定索引的元素。Python

      my_list = [10, 20, 30]
      print(my_list[0]) # 10
      print(my_list[-1]) # 30 (最后一个元素)
      
    • 切片访问: list[startstep]​ 获取子列表。Python

      my_list = [10, 20, 30, 40, 50]
      print(my_list[1:4]) # [20, 30, 40]
      print(my_list[::-1]) # [50, 40, 30, 20, 10] (倒序)
      
    • 循环遍历: 使用 for​ 循环迭代元素。Python

      my_list = ['a', 'b', 'c']
      for item in my_list:
          print(item)
      # a
      # b
      # c
      
    • ​len(list)​: 获取列表长度。

    • ​value in list​: 检查元素是否存在,返回 True​ 或 False​。

    • ​list.index(value)​: 返回指定值的第一个索引,如果值不存在会引发 ValueError​。

    • ​list.count(value)​: 返回指定值出现的次数。


    2. 字典 (Dictionary) - 无序 (Python 3.7+ 有序)、可变、键唯一

    字典通过键 (key) 来存储和检索值 (value),键必须是唯一的且可哈希(不可变)。

    创建 (Create)

    • 字面量: my_dict = {'name': 'Alice', 'age': 30}​

    • ​dict()​ 构造函数: another_dict = dict(name='Bob', age=25)​

    • ​dict.fromkeys(iterable, value)​: 从可迭代对象创建字典,所有键共享一个默认值。Python

      keys = ['a', 'b', 'c']
      default_dict = dict.fromkeys(keys, 0)
      print(default_dict) # {'a': 0, 'b': 0, 'c': 0}
      

    增加 (Add)

    • 通过键赋值: 如果键不存在,则添加新的键值对。Python

      my_dict = {'name': 'Alice'}
      my_dict['age'] = 30 # 添加新键值对
      print(my_dict) # {'name': 'Alice', 'age': 30}
      
    • ​update(other_dict)​: 合并另一个字典或键值对迭代器。Python

      my_dict = {'a': 1, 'b': 2}
      my_dict.update({'c': 3, 'd': 4})
      print(my_dict) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
      

    删除 (Delete)

    • ​del dict[key]​: 根据键删除键值对。Python

      my_dict = {'name': 'Alice', 'age': 30}
      del my_dict['age']
      print(my_dict) # {'name': 'Alice'}
      
    • ​pop(key, default)​: 移除并返回指定键的值,如果键不存在可以提供默认值。Python

      my_dict = {'name': 'Alice', 'age': 30}
      removed_age = my_dict.pop('age')
      print(my_dict) # {'name': 'Alice'}
      print(removed_age) # 30
      missing_key = my_dict.pop('city', 'Unknown') # 键不存在,返回默认值
      print(missing_key) # Unknown
      
    • ​popitem()​: 移除并返回最后一个插入的键值对 (Python 3.7+),如果字典为空会引发 KeyError​。Python

      my_dict = {'a': 1, 'b': 2}
      item = my_dict.popitem()
      print(item) # ('b', 2)
      print(my_dict) # {'a': 1}
      
    • ​clear()​: 清空字典所有键值对。Python

      my_dict = {'a': 1, 'b': 2}
      my_dict.clear()
      print(my_dict) # {}
      

    修改 (Modify)

    • 通过键赋值: 如果键已存在,则修改对应的值。Python

      my_dict = {'name': 'Alice', 'age': 30}
      my_dict['age'] = 31 # 修改age的值
      print(my_dict) # {'name': 'Alice', 'age': 31}
      
    • ​update(other_dict)​: 也可以用于修改,如果键已存在则更新其值。Python

      my_dict = {'a': 1, 'b': 2}
      my_dict.update({'b': 20, 'c': 30}) # 'b'被修改,'c'被添加
      print(my_dict) # {'a': 1, 'b': 20, 'c': 30}
      

    查询 (Query)

    • 通过键访问: dict[key]​ 获取指定键的值。如果键不存在会引发 KeyError​。Python

      my_dict = {'name': 'Alice', 'age': 30}
      print(my_dict['name']) # Alice
      
    • ​dict.get(key, default)​: 获取指定键的值,如果键不存在返回 None​ 或指定的默认值,不会引发错误。Python

      print(my_dict.get('age')) # 30
      print(my_dict.get('city', 'Unknown')) # Unknown
      
    • ​len(dict)​: 获取键值对数量。

    • ​key in dict​: 检查键是否存在,返回 True​ 或 False​。

    • ​dict.keys()​: 返回一个包含所有键的视图。

    • ​dict.values()​: 返回一个包含所有值的视图。

    • ​dict.items()​: 返回一个包含所有键值对(元组形式)的视图。Python

      for key, value in my_dict.items():
          print(f"{key}: {value}")
      

    3. 集合 (Set) - 无序、可变、元素不重复

    集合是无序的唯一元素集合,主要用于数学集合操作和快速成员测试。

    创建 (Create)

    • 字面量: my_set = {1, 2, 3}​ (注意不能用 {}​ 创建空集合,那是字典)

    • ​set()​ 构造函数:

      • ​empty_set = set()​

      • ​set_from_list = set([1, 2, 2, 3])​ # 结果是 {1, 2, 3}​

    增加 (Add)

    • ​add(element)​: 添加单个元素。如果元素已存在,集合不变。Python

      my_set = {1, 2}
      my_set.add(3)
      my_set.add(2) # 不会重复添加
      print(my_set) # {1, 2, 3}
      
    • ​update(iterable)​: 添加一个可迭代对象的所有元素。Python

      my_set = {1, 2}
      my_set.update([3, 4, 2])
      print(my_set) # {1, 2, 3, 4}
      

    删除 (Delete)

    • ​remove(element)​: 移除指定元素,如果元素不存在会引发 KeyError​。Python

      my_set = {1, 2, 3}
      my_set.remove(2)
      print(my_set) # {1, 3}
      
    • ​discard(element)​: 移除指定元素,如果元素不存在不会引发错误。Python

      my_set = {1, 3}
      my_set.discard(4) # 不会报错
      print(my_set) # {1, 3}
      
    • ​pop()​: 随机移除并返回一个元素(因为集合是无序的)。空集合调用会引发 KeyError​。Python

      my_set = {1, 2, 3}
      removed = my_set.pop() # 随机移除一个
      print(my_set) # 可能会是 {2, 3} 或 {1, 3} 等
      print(removed) # 可能是 1, 2, 或 3
      
    • ​clear()​: 清空集合所有元素。Python

      my_set = {1, 2, 3}
      my_set.clear()
      print(my_set) # set()
      

    修改 (Modify)

    • 集合的元素本身是不可变的(因为它们必须是可哈希的)。你不能“修改”集合中的某个元素,你只能删除旧的,然后添加新的。

    查询 (Query)

    • ​element in set​: 检查元素是否存在,返回 True​ 或 False​(非常高效)。Python

      my_set = {1, 2, 3}
      print(2 in my_set) # True
      print(4 in my_set) # False
      
    • ​len(set)​: 获取集合中的元素数量。

    • 集合间的操作:

      • ​union(other_set)​ 或 |​: 并集

      • ​intersection(other_set)​ 或 &​: 交集

      • ​difference(other_set)​ 或 -​: 差集

      • ​symmetric_difference(other_set)​ 或 ^​: 对称差集

      • ​issubset(other_set)​: 子集判断

      • ​issuperset(other_set)​: 超集判断


    4. 元组 (Tuple) - 有序、不可变、允许重复

    元组是不可变的序列,一旦创建就不能修改。这使得它们在某些情况下比列表更安全、更高效,并且可以用作字典的键。

    创建 (Create)

    • 字面量: my_tuple = (1, 2, 'a', 'b')​

    • 单个元素的元组: single_item_tuple = (5,)​ (注意逗号是必需的)

    • ​tuple()​ 构造函数: another_tuple = tuple([1, 2, 3])​

    增加 (Add)

    • 元组是不可变的,不能直接在原地增加元素。 你只能通过连接操作创建新的元组。Python

      my_tuple = (1, 2)
      new_tuple = my_tuple + (3,) # 创建一个新元组
      print(new_tuple) # (1, 2, 3)
      print(my_tuple)  # (1, 2) (原始元组未变)
      

    删除 (Delete)

    • 元组是不可变的,不能直接在原地删除元素。 你可以删除整个元组对象,但不能删除其中的某个元素。Python

      my_tuple = (1, 2, 3)
      del my_tuple # 删除整个元组变量
      # print(my_tuple) # 这会引发 NameError
      
    • 要“删除”元组中的某个元素,你实际上需要创建不包含该元素的新元组,这通常通过切片来实现。Python

      my_tuple = (1, 2, 3, 4)
      # 创建一个不包含2的新元组 (通过拼接切片)
      new_tuple_without_2 = my_tuple[:1] + my_tuple[2:]
      print(new_tuple_without_2) # (1, 3, 4)
      

    修改 (Modify)

    • 元组是不可变的,不能修改其任何元素。Python

      my_tuple = (1, 2, 3)
      # my_tuple[0] = 10 # 这会引发 TypeError
      
    • 如果元组中包含可变对象(如列表),你可以修改这些可变对象内部的元素,但这不算是修改元组本身。Python

      mutable_in_tuple = (1, [2, 3], 4)
      mutable_in_tuple[1].append(5) # 修改了元组内部的列表
      print(mutable_in_tuple) # (1, [2, 3, 5], 4)
      

    查询 (Query)

    • 索引访问: tuple[index]​ 获取指定索引的元素。Python

      my_tuple = (10, 20, 30)
      print(my_tuple[0]) # 10
      
    • 切片访问: tuple[startstep]​ 获取子元组。Python

      my_tuple = (10, 20, 30, 40, 50)
      print(my_tuple[1:4]) # (20, 30, 40)
      
    • 循环遍历: 使用 for​ 循环迭代元素。Python

      my_tuple = ('x', 'y', 'z')
      for item in my_tuple:
          print(item)
      
    • ​len(tuple)​: 获取元组长度。

    • ​value in tuple​: 检查元素是否存在。

    • ​tuple.index(value)​: 返回指定值的第一个索引。

    • ​tuple.count(value)​: 返回指定值出现的次数。