Python冷知识


预计阅读时间:12 分钟

1.在一些比较简单的程序里,多线程要比单线程快一些。这是因为多线程能提高CPU的利用率

2.@staticmethod是静态方法修饰器。作用:正常来说在class中,一个方法必须要传递一个self参数,但是可以使用该修饰器定义一个静态方法,不依靠self也可以正常运行

3.在Python中,函数定义后的 -> int 是类型注解(Type Hint),仅用于提示函数的返回值预期是 int 类型,但不会强制转换返回值类型。如果实际返回的类型不符合注解,Python不会报错,也不会自动转换。

4.Python 行内 for 循环详解

什么是行内 for 循环

Python 中的「行内 for 循环」通常指的是推导式(Comprehension),它允许用一行简洁的代码来创建新的数据结构。主要包括四种类型:

1. 列表推导式 (List Comprehension) - 最常用

基本语法

[expression for item in iterable]

示例

传统写法:

squares = []
for x in range(5):
    squares.append(x ** 2)

列表推导式写法:

squares = [x ** 2 for x in range(5)]
# 结果: [0, 1, 4, 9, 16]

2. 带条件的列表推导式

语法

[expression for item in iterable if condition]

示例

传统写法:

even_squares = []
for x in range(10):
    if x % 2 == 0:
        even_squares.append(x ** 2)

列表推导式写法:

even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
# 结果: [0, 4, 16, 36, 64]

3. 嵌套循环的列表推导式

语法

[expression for item1 in iterable1 for item2 in iterable2]

示例

传统写法:

pairs = []
for i in range(2):
    for j in range(3):
        pairs.append((i, j))

列表推导式写法:

pairs = [(i, j) for i in range(2) for j in range(3)]
# 结果: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]

4. 其他类型的推导式

集合推导式 (Set Comprehension)

names = ["Alice", "Bob", "Alice", "Charlie"]
unique_names = {name for name in names}
# 结果: {'Charlie', 'Bob', 'Alice'} (顺序可能不同)

字典推导式 (Dictionary Comprehension)

numbers = [1, 2, 3]
squares_dict = {x: x ** 2 for x in numbers}
# 结果: {1: 1, 2: 4, 3: 9}

生成器表达式 (Generator Expression)

# 列表推导式 - 立即创建整个列表
list_comp = [x ** 2 for x in range(1000000)]

# 生成器表达式 - 惰性求值,节省内存
gen_exp = (x ** 2 for x in range(1000000))
# 结果: <generator object <genexpr> at 0x...>

使用建议和最佳实践

1. 优先考虑可读性

清晰的情况:

result = [x * 2 for x in numbers if x > 5]

可能过于复杂的情况(建议用传统循环):

# 不推荐 - 难以阅读
complex_result = [x * y for x in list_a if x > 5 for y in list_b if y < x]

2. 不要用于副作用

不推荐(创建无用列表):

[print(name) for name in names]  # 错误用法

推荐写法:

for name in names:
    print(name)  # 正确用法

3. 生成器表达式更省内存

当只需要迭代结果一次时,优先使用生成器表达式:

# 处理大文件时特别有用
large_data = (process_line(line) for line in open('large_file.txt'))
for item in large_data:
    # 逐行处理,不占用大量内存
    pass

总结表格

类型 语法 输出结果 特点
列表推导式 [x for x in ...] list 最常用
集合推导式 {x for x in ...} set 自动去重
字典推导式 {k:v for k,v in ...} dict 键值对
生成器表达式 (x for x in ...) generator 惰性求值,省内存

核心原则: 在保持代码可读性的前提下使用推导式,避免为了简洁而牺牲代码的清晰度。

5. 迭代器只能遍历一次,就会耗尽

6. 线程池的原理

新建线程系统需要分配资源、终止线程系统需要回收资源 如果可以重用线程,则可以减去新建/终止的开销

使用线程池的好处:

  1. 提升性能:因为减去了大量新建、终止线程的开销,重用了线程资源
  2. 适用场景:适合处理突发性大量请求或需要大量线程完成任务、但实际任务处理时间较短
  3. 防御功能:能有效避免系统因为创建线程过多,而导致系统负荷过大相应变慢等问题
  4. 代码优势:使用线程池的语法比自己新建线程执行线程更加简洁

7.线程的五种生命状态:新建、就绪、运行、阻塞、终止

8.如果遇到了CPU密集型计算,多线程反而会降低执行速度

9.函数装饰器(注解)使用方法

一、什么是“注解”

在 Python 里,其实没有真正的“注解”语法。我们常说的 “@login_required” 等,其实是 函数装饰器(Function Decorator)

语法糖形式:

@decorator
def func():
    ...

等价于:

def func():
    ...
func = decorator(func)

也就是说:

@decorator 只是语法糖,在定义函数时自动调用 decorator(func),并用返回值替换原函数。

二、简单示例:自定义装饰器

def my_decorator(func):
    def wrapper():
        print("调用前")
        func()
        print("调用后")
    return wrapper

@my_decorator
def hello():
    print("你好,Python!")

hello()

输出结果:

调用前
你好,Python!
调用后

解释: 1. 定义 hello() 时,Python 自动执行 hello = my_decorator(hello) 2. my_decorator() 返回一个新的函数 wrapper 3. 调用 hello() 实际上执行的是 wrapper()

三、@符号语法的执行时机

在 Python 解释器加载函数定义时@ 语法会立即执行。

print("开始定义")

@my_decorator
def test():
    print("运行 test")

print("定义结束")

执行顺序:

开始定义
定义结束

此时已经执行了 my_decorator(test),定义的 test 已被替换为装饰后的函数。

四、带参数的装饰器(装饰器工厂)

简单示例1:

def decorator_with_args(arg):
    print("外层被执行:收到参数", arg)
    def decorator(func):
        print(arg) #这里可以使用外层参数
        print("中层被执行:收到函数", func.__name__)
        def wrapper():
            print("内层被执行:真正运行函数")
            func()
        return wrapper
    return decorator

@decorator_with_args("ABC")
def test():
    print("执行 test()")

test()

输出:

外层被执行:收到参数 ABC
ABC
中层被执行:收到函数 test
内层被执行:真正运行函数
执行 test()

简单示例2:

如果想写成这种形式:

@login_required(login_url='/login/')

那就是一个装饰器工厂。示例如下:

def login_required(login_url='/accounts/login/'):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"需要登录,跳转到:{login_url}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

执行过程: 1. login_required('/login/') 返回一个具体的装饰器函数; 2. 该装饰器函数再包裹目标函数; 3. 最终形成嵌套结构。

简单示例3:

def decorator_with_args(arg):
    print("外层被执行:收到参数", arg)

    def decorator(func):
        print(arg)  # 可以访问外层参数
        print("中层被执行:收到函数", func.__name__)

        def wrapper(*args, **kwargs):  # ← 关键修改:接收任意参数
            print("内层被执行:真正运行函数")
            print("test函数收到的位置参数:", args)
            print("test函数收到的关键字参数:", kwargs)
            return func(*args, **kwargs)  # ← 记得传递参数给原函数

        return wrapper

    return decorator


@decorator_with_args("ABC")
def test(x, y):
    print("执行 test()", x, y)


# 调用时传入两个参数
test(10, 20)

简单示例4:

# main.py
def decorator_with_args(arg):
    print("外层被执行:收到参数", arg)

    def decorator(func):
        print(arg)  # 可以访问外层参数
        print("中层被执行:收到函数", func.__name__)

        def wrapper(*args, **kwargs):  # ← 关键修改:接收任意参数
            print("内层被执行:真正运行函数")
            print("test函数收到的位置参数:", args)
            print(args[0])
            print("test函数收到的关键字参数:", kwargs)
            return func(*args, **kwargs)  # ← 记得传递参数给原函数

        return wrapper

    return decorator
# 6.py
from main import decorator_with_args


@decorator_with_args("ABC") # 此时(定义的时候)就已经执行了外层
def test(x, y):
    print("执行 test()", x, y)

print("-----分隔线-----")
print("调用 test(10, 20)")
test(10, 20)

输出:

外层被执行:收到参数 ABC
ABC
中层被执行:收到函数 test
-----分隔线-----
调用 test(10, 20)
内层被执行:真正运行函数
test函数收到的位置参数: (10, 20)
10
test函数收到的关键字参数: {}
执行 test() 10 20

10. *args**kwargs 是什么?

  • *args:代表 任意数量的位置参数,会被打包成一个元组。

  • **kwargs:代表 任意数量的关键字参数,会被打包成一个字典。

示例:

def show(*args, **kwargs):
    print("args:", args)
    print("kwargs:", kwargs)

show(1, 2, 3, name="Tom", age=18)

输出:

args: (1, 2, 3)
kwargs: {'name': 'Tom', 'age': 18}

11.魔术方法

  1. __name__ == "__main__" : 在该程序作为主程序(直接运行的程序)时执行以下代码
  2. __file__:获取当前脚本所在路径

12.多继承中的MRO调用规则:

  1. Method Resolution Order(MRO) 决定了多继承中方法或属性的查找顺序
  2. super() 会调用 MRO 中当前类之后的第一个类的方法。
  3. 避免直接调用父类构造函数,多继承时用 super() 可确保每个父类方法只调用一次。

示例代码:

class A:
    def __init__(self):
        print("A init")

class B(A):
    def __init__(self):
        print("B init")
        super().__init__()

class C:
    def __init__(self):
        print("C init")

class D(B, C):
    def __init__(self):
        print("D init")
        super().__init__()

print("MRO:", D.__mro__)
d = D()

输出:

MRO: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.C'>, <class 'object'>)
D init
B init
A init
C init

说明: - super().__init__() 按 MRO 顺序调用父类构造函数。

  • 每个类的 __init__ 都只调用一次,不会重复。

13.raise关键字:主动抛出异常

在 Python 中,raise 是一个 关键字,用于 主动触发异常(即抛出异常),通常用于错误处理或控制程序流程。它可以让程序在遇到不符合预期的情况时停止执行或交给上层捕获处理。


本文由 changchang 原创,转载请注明出处。

📖相关推荐