• 第一章 Python的基础语法


    一、python 的数据结构

    1.1 基础数据结构

    首先我们介绍一下python的基础数据结构:Python中的数据结构是组织和存储数据的方式,它们使得数据的处理更为高效和灵活。Python内置了多种数据结构,主要包括列表(list)、元组(tuple)、集合(set)和字典(dict)。

    1. 列表(List):列表是Python中最常用的数据结构之一,它是一个有序的元素集合。列表中的元素可以是任意类型,包括数字、字符串、其他列表等。列表是可变的,这意味着你可以添加、删除或修改列表中的元素。
    2. 元组(Tuple):元组与列表类似,也是一个有序的元素集合。然而,元组是不可变的,一旦创建,就不能修改其元素。这使得元组在某些情况下更为安全,例如,当你不希望数据被意外修改时。
    3. 集合(Set):集合是一个无序的、不重复元素的集合。集合主要用于成员关系测试和消除重复元素。集合支持集合运算,如并集、交集和差集等。
    4. 字典(Dict):字典是一个无序的键值对集合。字典中的每个元素都是一个键值对,键是唯一的,用于查找对应的值。字典在存储需要快速查找的数据时非常有用。

    1.2 collections

    在Python中,collections模块提供了一些特殊的高阶容器数据类型,这些类型扩展了Python内置的容器类型,提供了更多有用的功能和操作。下面是collections模块中一些常用容器数据类型的介绍:

    1. namedtuple: namedtuple是一个工厂函数,用于创建具有名称的元组子类。这使得元组的每个元素都拥有一个描述性的名称,从而提高了代码的可读性。通过名称来访问元组的元素,而不是使用索引,可以使代码更加直观。
    2. deque: deque是一个双端队列,支持从队列的两端添加和移除元素。相比于Python的列表(list),deque在处理队列操作(如插入和删除)时更加高效,尤其是在队列的两端。
    3. Counter: Counter是一个字典子类,用于计数可哈希对象。它提供了计数和统计功能,常用于统计元素出现的次数。Counter对象支持多种数学运算,如加法、减法、交集和并集等。
    4. OrderedDict: OrderedDict是一个保持元素插入顺序的字典子类。普通的Python字典是无序的,而OrderedDict则记住了元素被插入时的顺序。这使得OrderedDict在处理需要保持顺序的场景时非常有用。
    5. defaultdict: defaultdict是一个字典子类,它提供了一个默认值工厂函数。当查询一个不存在的键时,defaultdict会自动使用工厂函数创建一个默认值,而不是引发KeyError异常。这可以简化代码,避免不必要的错误处理。
    6. ChainMap: ChainMap可以将多个字典链接在一起,形成一个逻辑上的单个字典。在查找时,ChainMap会按照字典的链接顺序进行查找,直到找到对应的键为止。这提供了一种方便的方式来组合多个字典,并简化查找操作。

    1.3 heapq

    heapq是Python标准库中的一个模块,它提供了堆队列算法的实现,也称为优先队列算法。堆是一种特殊的树形数据结构,通常表现为一个完全二叉树,它满足堆属性:父节点的值总是大于或等于(最大堆)或小于或等于(最小堆)其子节点的值。在Python的heapq模块中,默认实现的是最小堆

    heapq模块的主要用途是实现对一组数据进行排序和取最小(或最大)值,它支持添加、弹出、替换等操作,同时提供了建堆、堆排序等高级功能。这些操作的时间复杂度通常较低,因此heapq在处理大量数据时非常高效。

    二、python 的基础语法

    Python 是一种解释型、交互式、面向对象的编程语言。它的语法简洁清晰,易于学习,并且具有强大的功能。

    2.1 Python的变量与赋值

    在Python中,变量是一个标识符(名称),它用于存储程序中使用的值。赋值操作是将一个值赋给变量的过程。Python中的变量不需要事先声明,可以直接使用赋值语句来创建和初始化。

    2.1.1 变量的命名规则
    • 变量名只能包含字母、数字和下划线。
    • 变量名不能以数字开头。
    • 变量名是区分大小写的,例如myVarmyvar被视为两个不同的变量。
    • 避免使用Python的保留字(如ifforwhile等)作为变量名。
    2.1.2 赋值操作

    在Python中,赋值操作使用单个等号=。等号左边是变量名,右边是要赋给变量的值。

    # 赋值给变量
    x = 10  # 整数赋值
    y = 3.14  # 浮点数赋值
    name = "Alice"  # 字符串赋值
    is_student = True  # 布尔值赋值
    
    # 赋值后,变量的值可以改变
    x = 20  # 现在x的值是20
    
    # 也可以将其他变量的值赋给变量
    z = x  # 现在z的值也是20,且如果x改变,z不会随之改变(除非再次赋值)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.1.2 变量的类型

    Python是动态类型语言,意味着变量的类型在运行时确定,并且可以改变。

    # 初始化为整数
    a = 10
    print(type(a))  # 输出: 
    
    # 改变为字符串
    a = "hello"
    print(type(a))  # 输出: 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.1.3 变量的作用域

    变量的作用域决定了变量在程序中的可见性和生命周期。Python中的变量作用域可以是:

    • 局部作用域:在函数或方法内部定义的变量。
    • 全局作用域:在函数或方法外部定义的变量。
    • 内建作用域:内建函数和异常的名字(例如abs()len()等)。

    当在函数内部引用一个变量时,Python首先检查局部作用域,然后是全局作用域,最后是内建作用域。

    # 全局作用域中的变量
    global_var = 100
    
    def my_function():
        # 局部作用域中的变量
        local_var = 200
        print(local_var)  # 输出200
        print(global_var)  # 输出100,因为可以访问全局作用域中的变量
    
    my_function()
    print(local_var)  # 报错,因为local_var只在my_function的局部作用域中定义
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.1.4 链式赋值和多重赋值

    Python还支持链式赋值和多重赋值。

    # 链式赋值
    a = b = c = 10  # a, b, 和 c 现在都指向同一个整数对象10
    
    # 多重赋值
    x, y, z = 1, 2, 3  # x现在是1, y现在是2, z现在是3
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.2 运算符

    运算符运算符类型运算符描述优先级示例
    +算术加法5a + b
    -算术减法5a - b
    *算术乘法4a * b
    /算术除法4a / b
    //算术整除(向下取整)4a // b
    %算术取模(求余数)4a % b
    **算术幂运算3a ** b
    +=赋值加法赋值-a += b
    -=赋值减法赋值-a -= b
    *=赋值乘法赋值-a *= b
    /=赋值除法赋值-a /= b
    //=赋值整除赋值-a //= b
    %=赋值取模赋值-a %= b
    **=赋值幂赋值-a **= b
    ==比较等于6a == b
    !=比较不等于6a != b
    >比较大于6a > b
    <比较小于6a < b
    >=比较大于等于6a >= b
    <=比较小于等于6a <= b
    and逻辑逻辑与-a and b
    or逻辑逻辑或-a or b
    not逻辑逻辑非-not a
    &位运算按位与-a & b
    |位运算按位或-a | b
    ^位运算按位异或-a ^ b
    ~位运算按位取反-~a
    <<位运算左移-a << b
    >>位运算右移-a >> b
    is身份判断两个对象是否相同(即是否引用自同一个对象)-a is b
    is not身份判断两个对象是否不同-a is not b
    in成员检查一个值是否存在于一个序列中-a in b
    not in成员检查一个值是否不存在于一个序列中-a not in b

    为了确保优先级,请写代码的同时加入上括号。

    2.3 控制流算法

    在 Python 中,控制流指的是程序执行过程中根据某些条件或逻辑来改变执行路径的机制。这包括条件语句、循环语句以及异常处理。以下是 Python 中控制流的一些关键组件的详细介绍:

    2.3.1 条件语句

    if 语句:用于根据条件执行不同的代码块。

    if condition:
        # 执行当 condition 为 True 时的代码块
    elif another_condition:
        # 执行当 condition 为 False 且 another_condition 为 True 时的代码块
    else:
        # 执行当所有条件都不满足时的代码块
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三元运算符:是 if-else 语句的简洁形式,用于在单行中根据条件返回两个值中的一个。

    value_if_true if condition else value_if_false
    
    • 1
    2.3.2 循环语句

    for 循环:用于遍历序列(如列表、元组、字符串等)或任何可迭代对象的元素。

    for item in iterable:
        # 对每个 item 执行代码块
    
    • 1
    • 2

    while 循环:只要给定的条件为真,就重复执行代码块。

    while condition:
        # 当 condition 为 True 时,执行代码块
    
    • 1
    • 2
    2.3.3 异常处理

    try-except 语句:用于捕获和处理运行时错误或异常。

    try:
        # 尝试执行的代码块
    except ExceptionType:
        # 当发生 ExceptionType 异常时执行的代码块
    else:
        # 当 try 块成功执行完毕后执行的代码块
    finally:
        # 无论是否发生异常,都会执行的代码块
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    raise 语句:用于显式地引发一个异常。

    raise ExceptionType("Error message")
    
    • 1
    2.3.4 中断和继续循环

    break 语句:用于跳出当前循环(无论是 for 循环还是 while 循环)。

    for item in iterable:
        if condition:
            break  # 当满足条件时跳出循环
    
    • 1
    • 2
    • 3

    continue 语句:用于跳过当前循环的剩余部分,并立即开始下一次迭代。

    for item in iterable:
        if condition:
            continue  # 当满足条件时跳过当前迭代
        # 执行其余的代码
    
    • 1
    • 2
    • 3
    • 4
    2.3.5 上下文管理器

    with 语句:用于简化资源管理,如文件操作、网络连接或锁定机制。上下文管理器确保资源在使用后被正确清理。

    with context_expression as variable:
        # 在此块内,variable 绑定到 context_expression 的结果
        # 当退出此块时,context_expression 定义的清理操作被执行
    
    • 1
    • 2
    • 3

    以上是 Python 中控制流的一些主要组件。它们允许你根据条件、循环或异常来组织你的代码,以创建灵活和健壮的程序。

    2.4 类与对象

    当然可以。在Python中,类和对象是面向对象编程(OOP)的两个核心概念。面向对象编程是一种编程范式,它使用“对象”来设计应用程序和软件。对象通常具有属性和方法,它们分别表示对象的数据和行为。

    2.4.1 类(Class)

    类是一个抽象的概念,它定义了对象的属性和方法。你可以将类视为对象的蓝图或模板,它描述了如何创建特定类型的对象。类定义了对象的状态(属性)和行为(方法)。

    在Python中,你可以使用class关键字来定义一个类。下面是一个简单的类定义示例:

    class Dog:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        
        def bark(self):
            print(f"{self.name} is barking!")
        
        def describe(self):
            print(f"{self.name} is {self.age} years old.")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这个例子中,Dog是一个类,它有两个属性(nameage)和两个方法(barkdescribe)。__init__方法是一个特殊方法,用于初始化新创建的对象的状态。当你创建一个Dog对象时,Python会自动调用这个方法。

    2.4.2 对象(Object)

    对象是类的实例。你可以通过调用类来创建一个或多个对象。每个对象都有它自己的属性和方法,并且这些属性和方法是从类中继承的。

    下面是如何创建一个Dog类的实例(即对象)的示例:

    # 创建Dog类的实例(对象)
    my_dog = Dog("Buddy", 3)
    
    # 访问对象的属性
    print(my_dog.name)  # 输出: Buddy
    print(my_dog.age)   # 输出: 3
    
    # 调用对象的方法
    my_dog.bark()       # 输出: Buddy is barking!
    my_dog.describe()   # 输出: Buddy is 3 years old.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这个例子中,my_dog是一个Dog类的实例(对象)。我们给my_dog对象赋予了特定的属性值(name为"Buddy",age为3),并调用了它的方法。

    2.4.3 继承

    在面向对象编程中,一个类可以继承另一个类的属性和方法。这允许你创建更通用的类,并从这些通用类中派生更具体的类。在Python中,你可以使用class关键字和继承的语法来定义子类。

    class Labrador(Dog):
        def swim(self):
            print(f"{self.name} is swimming!")
    
    # 创建Labrador类的实例
    my_labrador = Labrador("Labrador Retriever", 2)
    
    # 调用继承自Dog的方法
    my_labrador.bark()       # 输出: Labrador Retriever is barking!
    my_labrador.describe()   # 输出: Labrador Retriever is 2 years old.
    
    # 调用Labrador特有的方法
    my_labrador.swim()       # 输出: Labrador Retriever is swimming!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这个例子中,Labrador类继承自Dog类,因此它自动获得了Dog类的所有属性和方法。此外,Labrador类还定义了一个特有的方法swim

    通过类和对象,你可以构建复杂且可维护的程序结构,实现代码重用和模块化,以及更好地组织和理解你的程序逻辑。

    2.5 导入模块

    在Python中,导入模块是一个重要的功能,它允许你使用其他Python文件(即模块)中定义的函数、类和变量。这样,你可以将代码组织成多个可重用的组件,并在需要时将它们导入到你的主程序中。

    2.5.1 导入整个模块

    你可以使用import语句来导入整个模块。一旦模块被导入,你就可以使用点操作符(.)来访问模块中的函数、类或变量。

    import math
    
    # 使用模块中的函数
    radius = 5
    area = math.pi * radius ** 2
    print(area)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这个例子中,我们导入了math模块,并使用它来计算圆的面积。注意我们使用了math.pi来访问math模块中定义的pi常量。

    2.5.2 导入模块中的特定部分

    如果你只对模块中的某些函数或变量感兴趣,你可以使用from ... import ...语法来只导入它们。

    from math import sqrt, pi
    
    # 直接使用函数和变量,无需前缀
    side_length = 9
    diagonal = sqrt(2) * side_length
    area_of_circle = pi * (side_length / 2) ** 2
    print(diagonal)
    print(area_of_circle)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这个例子中,我们只导入了math模块中的sqrtpi函数/变量,因此我们可以直接使用它们的名字,而不需要加上math.前缀。

    2.5.3 导入模块并为其指定别名

    如果模块的名字太长或者可能与你的程序中的其他部分冲突,你可以使用as关键字为它指定一个别名。

    import numpy as np
    
    # 使用别名来调用模块中的函数
    array = np.array([1, 2, 3, 4])
    print(array)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这个例子中,我们导入了numpy模块并为其指定了别名np。这样,我们就可以使用np来代替numpy来调用它的函数和类。

    2.5.4 从包中导入模块

    Python的模块可以组织成包(packages),包是包含多个模块的目录,这些模块通常具有某种共同的功能或目的。你可以使用点操作符来从包中导入模块。

    假设你有一个名为mypackage的包,它包含一个名为mymodule的模块,你可以这样导入它:

    from mypackage import mymodule
    
    # 使用模块中的函数或类
    result = mymodule.some_function()
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    或者,如果你知道你要从哪个模块中导入什么,你可以直接这样做:

    from mypackage.mymodule import some_function
    
    # 直接使用函数
    result = some_function()
    print(result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2.5.5 动态导入模块

    有时,你可能希望在运行时根据某些条件动态地导入模块。这可以通过使用importlib模块的import_module函数来实现。

    import importlib
    
    module_name = 'math'  # 可以是任何有效的模块名
    module = importlib.import_module(module_name)
    
    # 使用导入的模块
    print(module.sqrt(16))  # 输出: 4.0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这个例子中,我们根据module_name变量的值动态地导入了模块,并使用它来计算平方根。

    导入模块是Python编程中的一个基本且强大的功能,它允许你构建大型、可维护的程序,同时保持代码的清晰和模块化。

    2.6 迭代器

    在Python中,迭代器(Iterator)是一个可以记住遍历的位置的对象,它可以从头到尾访问数据集合的元素。迭代器是数据集合的访问接口,它只能从前往后访问数据集合中的元素,并且只能访问一次。迭代器提供了一种不依赖于索引的迭代访问方法,使得遍历数据集合变得更为简单和高效。

    迭代器对象实现了迭代器协议,即它必须拥有__iter__()方法和__next__()方法。__iter__()方法返回迭代器对象本身,而__next__()方法返回数据集合的下一个元素。当没有更多元素可供访问时,__next__()方法将引发StopIteration异常。

    Python中的许多内置数据结构(如列表、元组、字典和集合)都是可迭代的,这意味着它们可以自动与for循环或其他迭代器操作一起使用。然而,不是所有的数据结构都是迭代器。为了将一个可迭代对象转换为迭代器,我们可以使用内置的iter()函数。

    下面是一个简单的迭代器示例:

    class MyIterator:
        def __init__(self, data):
            self.data = data
            self.index = 0
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.index < len(self.data):
                result = self.data[self.index]
                self.index += 1
                return result
            else:
                raise StopIteration
    
    # 使用迭代器
    my_iterator = MyIterator([1, 2, 3, 4, 5])
    for item in my_iterator:
        print(item)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这个示例中,我们定义了一个名为MyIterator的类,它实现了迭代器协议。我们可以创建这个类的实例,并将其用于for循环中,以迭代访问数据集合中的元素。

    总的来说,迭代器提供了一种强大而灵活的方式来遍历数据集合。通过使用迭代器,我们可以避免在遍历过程中使用索引,并且可以更容易地处理动态变化的数据集合。

    2.7 装饰器

    在Python中,装饰器(Decorator)是一个高级功能,它允许你修改或增强函数、方法或类的行为,而无需改变其源代码。装饰器本质上是一个接受函数作为参数的可调用对象(通常是一个函数),并返回一个修改后的函数。

    装饰器的语法使用@符号,它放在要修饰的函数或类的定义之前。这使得装饰器的应用非常直观和简洁。

    下面是一个简单的装饰器示例,它用于记录函数的执行时间:

    import time
    
    def timing_decorator(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(f"Function {func.__name__} took {end_time - start_time:.6f} seconds to execute.")
            return result
        return wrapper
    
    @timing_decorator
    def my_function():
        time.sleep(1)  # 模拟耗时操作
        print("Function executed!")
    
    # 调用函数时,装饰器会自动生效
    my_function()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这个例子中,timing_decorator是一个装饰器函数,它接受一个函数func作为参数,并返回一个新的函数wrapperwrapper函数在调用原函数之前和之后分别记录了时间,并打印出执行耗时。当我们在my_function的定义前使用@timing_decorator语法时,Python解释器会自动将my_function替换为timing_decorator(my_function)的返回值,即wrapper函数。因此,当我们调用my_function()时,实际上是调用了wrapper(),从而实现了执行时间的记录。

    装饰器可以用于多种场景,比如日志记录、性能分析、权限校验、缓存等。它们提供了一种灵活且可重用的方式来扩展函数或类的功能,而无需修改其源代码。这使得代码更加模块化和可维护。

    需要注意的是,装饰器在函数定义时立即执行,并且会改变原有函数的引用。因此,在使用装饰器时需要谨慎,确保理解其工作原理和潜在影响。

    三、Python的机制

    Python的各类机制主要包括其执行机制内存管理机制错误处理机制以及模块导入机制等。下面我将逐一为您介绍这些机制。

    3.1 执行机制

    Python 是一种解释型语言,它的执行机制涉及几个关键步骤:

    1. 编译:当Python脚本运行时,它首先将源代码(.py文件)逐行编译成字节码。这些字节码是源代码的底层、平台无关的表现形式,用于提高执行速度。如果Python具有写入权限,它会将这组字节码保存为.pyc文件,以便下次运行时直接加载,从而提高启动速度。
    2. 执行:编译后的字节码被发送到Python虚拟机(PVM)上执行。PVM是Python的运行引擎,负责迭代运行字节码指令,完成操作。

    3.2 内存管理机制

    Python具有自动内存管理机制,主要包括引用计数垃圾回收分代收集

    1. 引用计数:Python通过引用计数来跟踪对象的引用次数。当对象的引用计数减少到0时,Python的垃圾回收器会将其内存释放。
    2. 垃圾回收:当引用计数无法处理循环引用时,Python的垃圾回收器会介入,通过标记-清除算法来识别并释放不再使用的对象。
    3. 分代收集:Python还采用了分代收集策略,根据对象的存活时间来优化垃圾回收过程。

    3.3 错误处理机制

    Python使用异常处理机制来处理运行时错误。通过tryexceptfinallyraise等关键字,开发者可以捕获和处理异常,确保程序的健壮性。

    3.4 全局解释器锁

    全局解释器锁(Global Interpreter Lock,GIL)是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。在Python语言的主流实现CPython中,GIL是一个货真价实的全局线程锁,用于确保在任何给定时间只有一个线程执行Python字节码。

    具体来说,在解释器解释执行任何Python代码时,都需要先获得这把锁。在遇到I/O操作时,GIL会释放,以便其他线程有机会执行。对于纯计算的程序,没有I/O操作,解释器会每隔100次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过sys.setcheckinterval来调整)。因此,虽然CPython的线程库直接封装了操作系统的原生线程,但CPython进程作为一个整体,同一时间只会有一个获得了GIL的线程在运行,其他的线程都处于等待状态,等待GIL的释放。

    GIL的存在主要是由于CPython解释器的设计和实现方式。它作为一种互斥锁,用于保护Python对象和内存管理机制,防止多个线程同时访问和修改Python解释器的内部状态。

    然而,GIL也带来了一些局限性。它限制了多线程Python程序充分利用多个CPU核心的能力,特别是对于CPU密集型任务。这可能会影响需要大量计算的应用程序的性能。不过,对于I/O密集型任务,虽然多线程的优势不明显,但Python线程在等待I/O操作完成时仍然可以释放GIL,因此仍具有一定的优势。

    3.5 模块导入机制

    Python的模块导入机制允许开发者将代码组织成多个可重用的组件(即模块)。通过import语句,开发者可以导入并使用其他模块中的函数、类和变量。此外,Python还支持动态导入模块和从包中导入模块,为大型项目的组织提供了灵活性。

    3.6 其他机制

    除了上述机制外,Python还具有其他重要的机制,如上下文管理(通过with语句实现)、迭代器与生成器(用于高效遍历数据集合)、装饰器(用于修改或增强函数、方法或类的行为)等。

    这些机制共同构成了Python强大的功能和易用性,使其在各种应用场景中都能发挥出色。

  • 相关阅读:
    【操作系统】2.2 操作系统的调度
    div盒子放在页面正中间,添加旋转动画的时候,盒子向右下偏移
    Python - 生成二维码、条形码
    记录react native 环境配置 brew install watchman 警告问题
    神机百炼3.53-Kruskal
    JVM第十六讲:调试排错 - Java 线程分析之线程Dump分析
    从GitHub到GitLab,半导体巨头Arm更换阵营的5大理由
    网络爬虫的架构
    Android动态更换图标
    SAS学习2(data步,input语句,从文件中读取数据)
  • 原文地址:https://blog.csdn.net/qq_44961028/article/details/136620939