Python Advanced Programming
Python Advanced Programming
Table of Contents
Meta Programming
元编程(Meta Programming) 是一种编程技术,允许程序在运行时动态地生成、分析或修改代码结构,甚至改变自身的行为。它的核心思想是“编写能够操作代码的代码”,从而提升抽象层次,减少重复工作,增强灵活性。
Core Principles
- 代码即数据(Code as Data)
将代码视为可操作的数据结构(如字符串、抽象语法树/AST),允许程序像处理普通数据一样生成或修改代码。 - 运行时与编译时
- 编译时元编程:在编译阶段生成或转换代码(如宏、模板)。
- 运行时元编程:在程序运行时动态修改行为(如反射、动态方法生成)。
- 自省(Introspection)
程序能够分析自身的结构(如获取类的方法、检查参数类型)。 - 反射(Reflection)
在运行时动态调用或修改代码(如通过类名实例化对象、动态调用方法)。
这么说貌似有点抽象了!我们来看一个具体的例子:
Hello world!
1 |
|
程序将会输出Hello, world
。exec()
是 Python 中用来执行 Python 代码字符串的(比如 exec("print(1+1)")
)。
这就是Meta Programming中Code as Data的思想,即编写操作代码的代码。
如果这样想的话,各种脚本文件(bash,Dockerfile,Cmakelists)不都是Code as Data的思想吗?
当然,你也可以使用os
库实现更加高级的操作:
1 |
|
在有了最基本的感知之后,我们来一条一条分析Meta Programming的基本思想。
编译时 & 运行时元编程
其实C++中的宏就是一种编译时元编程。即在编译的阶段将#define
的东西转换进程序中然后开始编译。
当然这不止于C++,例如Rust中的println!()
就是宏而不是函数。
Decorators
下面本文将要介绍装饰器(Decorators)。
语法糖(英语:Syntactic sugar)是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。语法糖通常是常见操作的简写,也可以用另一种更冗长的形式表达:程序员可以选择使用较短的形式还是较长的形式,但通常会使用较短的形式,因为它更短,更容易输入和阅读。
装饰器是Python中一种强大的语法糖,允许在不修改原函数代码的情况下扩展或修改函数的行为。例如运行下面的代码:
1 |
|
程序输出如下:
1 |
|
可以看到,装饰器在不改变原函数的情况下实现了对函数的包裹,即内部的wrapper()
,这样相当于函数控制函数,同时各函数之间的代码相互独立。
我们来看两个更加复杂的装饰器:
- Time Decorator
1 |
|
程序的输出结果:
1 |
|
- Decorator for type check (Reflection for Meta Programming)
因为Python是一种弱类型的编程语言,我们可以在写一个decorator来实现简单的语法类型检查。
*args
代表接受任意的关键字参数,而**kwargs
可以接受任意的位置参数。
1 |
|
Python的类型审查相对比较松散,因此通过类型审查的decorator可以使Python函数的编写变的更加安全。
1 |
|
- Decorators with parameters
1 |
|
Applications
Make & Makefile
Linux中有自带的make
工具,可以实现编译的脚本自动化,和.sh
非常的类似,但是相比于Bash脚本更加的高级。
- Makefile:主要用于构建和管理项目,特别是编译型语言的编译过程
- Bash脚本:通用的自动化脚本工具,用于执行各种系统任务
Makefile很大的优势是他会最小化编译的成本,例如如果没有任何的源代码或者dependencies发生改变,Makefile不会重新编译这部分的dependencies。同时,Makefile有自己的语法框架,相比于灵活的bash脚本更加的规整一点。
例如,在编译C++程序的时候:
1 |
|
Makefile要求代码中要给出目标(target)、依赖(dependencies)和命令(commands)。例如在语句:
1 |
|
program
就是最后编译需要生成的binary files,而main.o utils.o
就是这一个语句需要使用的dependencies,然后在第二行就使用特定的命令行语句来实现。
这一点相对于Bash脚本来说更规整,尤其对于C++这种需要编译语言。(不过Cmakelists才是神中神)
我们再来举一个例子:
1 |
|
运行make
之后,程序的输出结果如下:
1 |
|
不过谁会这样使用Makefile呢?🤣不如直接写bash
Duck Typing
Introduction to Duck Typing
Duck Typing is a programming concept often used in dynamically-typed languages, where the type or class of an object is determined by its behavior (methods and properties) rather than its explicit inheritance or interface implementation. The term originates from the phrase:
“If it walks like a duck and quacks like a duck, then it must be a duck.”
In essence, Duck Typing focuses on what an object can do rather than what it is. This approach provides flexibility and promotes code reusability, but it also requires careful handling to avoid runtime errors due to missing methods or properties.
Key Characteristics of Duck Typing
Behavior Over Type:
- An object’s suitability is determined by the presence of specific methods or properties, not by its class or type.
- For example, if an object has a
quack()
method and awalk()
method, it can be treated as a “duck” regardless of its actual class.
Flexibility:
- Code can work with any object that implements the required behavior, making it highly adaptable and reusable.
- This eliminates the need for strict type hierarchies or interfaces.
Common in Dynamic Languages:
- Duck Typing is frequently used in dynamically-typed languages like Python, Ruby, and JavaScript, where type checking is done at runtime rather than compile time.
Example
Below is an example that demonstrates Duck Typing using a Duck
class, a Person
class, and even a Chicken
class. The key idea is that any object with the required methods (quack()
and walk()
) can be treated as a “duck.”
1 |
|
Explanation of the Code
Duck Typing in Action:
- The
check_if_duck
function does not check the type of theanimal
object. Instead, it checks whether the object has thequack()
andwalk()
methods. - If the object has these methods, it is treated as a “duck,” regardless of whether it is an instance of
Duck
,Person
, or evenChicken
.
- The
Flexibility and Reusability:
- The
Chicken
class is not a duck, but it can still be treated as one because it implements the required methods. - This demonstrates how Duck Typing allows objects of different types to be used interchangeably, as long as they exhibit the expected behavior.
- The
Runtime Risks:
- If an object passed to
check_if_duck
does not have the required methods (e.g., aDog
class withoutquack()
andwalk()
), the program will raise anAttributeError
at runtime.
- If an object passed to