)
一、最基础def 是定义() 是调用def say_hello(): print(Hello!)——只定义不执行。函数体被存起来不打印任何东西。say_hello()——有括号 调用 真正执行才会打印 Hello!。如果删掉调用那行程序什么都不输出函数只定义、从没被喊去跑。❗ 误区以为装饰器会自动让函数运行。不会必须显式写名字()才执行。二、无参装饰器名字被重新贴到 wrapper 上def my_decorator(func): def wrapper(): print(函数执行前) func() print(函数执行后) return wrapper my_decorator def say_hello(): print(Hello!) say_hello()执行流程def my_decorator定义装饰器函数不跑。def say_hello定义原函数不跑。my_decorator等价于say_hello my_decorator(say_hello)调用 my_decorator传入原函数内部定义 wrapper不跑return wrapper把 wrapper 交出去say_hello这个名字现在指向 wrapper不再是原函数。say_hello()实际执行wrapper()函数执行前 Hello! 函数执行后关键结论装饰器本质 函数名 装饰器(函数名)把名字重新绑定到新函数。装饰器执行时那行只定义和返回不打印打印发生在你后续调用时。没有调用那行 → 什么都不输出。三、Python 的指向不是 C 指针重要纠偏❗ 当时类比错了的地方已全部纠正原错误类比正确概念方法是内存块指针从上往下扫到哪执行到哪def语句本身执行了创建函数对象但函数体不执行只是存起来等调用指向像 C 指针知道内存哪块就跳去执行Python 名字是引用/reference标签贴对象不是 C 指针不能指针运算占多少内存要关心函数对象很小几百字节Python 有自动 GC学习阶段不用管内存Python 有指针没有指针有调用栈、有 list动态数组、有 numpy.ndarrayAI 必用Java 学习者对照表学过 Java基本能直接迁移Java 认知Python 对应方法定义不运行要 obj.method() 才调✅ 一样def 不跑func() 才跑对象靠引用、有 GC、有调用栈/堆✅ Python 一样且一切皆对象连函数都是primitive 在栈、对象在堆Python 更统一全是对象、全靠引用无裸指针、不能指针运算✅ Python 也没有要 new 才创建对象Python不用 new赋值即创建要手动 free/deletePython 自动 GC不用管唯一要忘掉的C 的手动内存管理 指针算术。Python 比 Java 还省心。四、(*args, **kwargs)装饰器的万能接口def wrapper(*args, **kwargs): result original_function(*args, **kwargs) return result调用target_function()空括号→args()空元组、kwargs{}空字典啥都没接。若target_function(1, 2, name小澪)→args(1,2)、kwargs{name:小澪}。写法接住存为*args位置参数按顺序的值元组 tuple**kwargs关键字参数名字值字典 dict定义处的*/**收集打包收到的参数。调用处的original_function(*args, **kwargs)的*/**解包摊开传回去。作用让装饰器通用不管被装饰函数要不要参数都能透传。无参函数传空进去正好。❗ 误区以为(*args, **kwargs)是必须填两个。不是是能接住任意参数的兜底不传就都是空的。五、带参装饰器装饰器工厂 多一层def repeat(num_times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(num_times): func(*args, **kwargs) return wrapper return decorator repeat(3) def say_hello(): print(Hello!) say_hello()三层接参最关键别混层函数接住来自1repeat(num_times)配置参数 3repeat(3)2decorator(func)被装饰函数 say_hello语法3wrapper(*args, **kwargs)调用时参数say_hello()❗ 当时误解repeat(3)把 say_hello 传给了 repeat。错repeat(3)先算 → 传num_times3→ 返回decorator内部记住 num_times3。然后decorator才把say_hello传给decorator的func。不是同一个函数接的两个东西是先吃配置、再吐出一个定制装饰器去接函数。对比无参装饰器无参df带参repeat(3)df 直接接原函数repeat 先接 3 → 吐 decoratordecorator 再接原函数两层df / wrapper三层repeat / decorator / wrapper多出的repeat层唯一作用接收配置参数返回真正能装饰函数的 decorator→ 所以叫装饰器工厂。六、for _ in range(n)循环 n 次range(3)产生0, 1, 2从 0 到 n-1共 n 个。for _ in range(n): 循环 n 次每轮执行缩进块。_是占位变量名表示这个数我不用纯占位写for i in ...也一样只是 i 没被用。带参装饰器打印三次就是for _ in range(num_times)里num_times3决定的改成 5 就打印 5 次。range()不是列表是惰性序列省内存学习阶段只需知道range(n) 0 到 n-1 共 n 个数。七、今日核心心法背下来def造函数不跑名字()才跑。装饰器函数名 装饰器(函数名)名字被重新指向新函数。没写调用 → 不输出要输出必须显式名字()。名字是引用不是指针内存 Python 自动管。(*args, **kwargs)是装饰器万能接口定义处收集、调用处解包。带参装饰器多一层工厂先收配置、再产装饰器、最后接函数。range(n)for 循环 n 次_是占位变量。