diff --git a/2022/10/16/mcforge1.12.2-0/index.html b/2022/10/16/mcforge1.12.2-0/index.html index 4bb1635..6253d8f 100644 --- a/2022/10/16/mcforge1.12.2-0/index.html +++ b/2022/10/16/mcforge1.12.2-0/index.html @@ -25,7 +25,7 @@ - + diff --git a/2022/10/16/mcforge1.12.2-1/index.html b/2022/10/16/mcforge1.12.2-1/index.html index 514dfd7..f717922 100644 --- a/2022/10/16/mcforge1.12.2-1/index.html +++ b/2022/10/16/mcforge1.12.2-1/index.html @@ -25,7 +25,7 @@ - + diff --git a/2022/11/06/aimp_stereo_install/index.html b/2022/11/06/aimp_stereo_install/index.html index 25f2bd9..a1fbbd4 100644 --- a/2022/11/06/aimp_stereo_install/index.html +++ b/2022/11/06/aimp_stereo_install/index.html @@ -25,7 +25,7 @@ - + diff --git a/2023/03/18/maa_wifi_connect/index.html b/2023/03/18/maa_wifi_connect/index.html index b03411b..c0fd6f6 100644 --- a/2023/03/18/maa_wifi_connect/index.html +++ b/2023/03/18/maa_wifi_connect/index.html @@ -25,7 +25,7 @@ - + diff --git a/2023/07/11/rosa_dev_exp/index.html b/2023/07/11/rosa_dev_exp/index.html index c3f9ca8..1ca3d81 100644 --- a/2023/07/11/rosa_dev_exp/index.html +++ b/2023/07/11/rosa_dev_exp/index.html @@ -25,7 +25,7 @@ - + diff --git a/2023/07/16/forge_dev_gradlemirror/index.html b/2023/07/16/forge_dev_gradlemirror/index.html index eb0c9ce..88bdf3f 100644 --- a/2023/07/16/forge_dev_gradlemirror/index.html +++ b/2023/07/16/forge_dev_gradlemirror/index.html @@ -25,7 +25,7 @@ - + diff --git a/2023/08/10/wsl_c_dev_vscode/index.html b/2023/08/10/wsl_c_dev_vscode/index.html index 8860405..b8ed27a 100644 --- a/2023/08/10/wsl_c_dev_vscode/index.html +++ b/2023/08/10/wsl_c_dev_vscode/index.html @@ -25,7 +25,7 @@ - + diff --git a/2023/09/17/about_code_aesthetic/index.html b/2023/09/17/about_code_aesthetic/index.html index f48e127..1206bdd 100644 --- a/2023/09/17/about_code_aesthetic/index.html +++ b/2023/09/17/about_code_aesthetic/index.html @@ -25,7 +25,7 @@ - + diff --git a/2023/09/17/about_oo_and_po/index.html b/2023/09/17/about_oo_and_po/index.html index c30139c..3a03b3e 100644 --- a/2023/09/17/about_oo_and_po/index.html +++ b/2023/09/17/about_oo_and_po/index.html @@ -25,7 +25,7 @@ - + diff --git a/2024/02/10/how_to_use_rinf/index.html b/2024/02/10/how_to_use_rinf/index.html index 44a6b02..52921c2 100644 --- a/2024/02/10/how_to_use_rinf/index.html +++ b/2024/02/10/how_to_use_rinf/index.html @@ -25,7 +25,7 @@ - + diff --git a/2024/05/08/oop_with_fp_via_method_chain/index.html b/2024/05/08/oop_with_fp_via_method_chain/index.html index 9829d17..92ed36a 100644 --- a/2024/05/08/oop_with_fp_via_method_chain/index.html +++ b/2024/05/08/oop_with_fp_via_method_chain/index.html @@ -25,7 +25,7 @@ - + diff --git a/2024/07/25/async_block_concurrent/index.html b/2024/07/25/async_block_concurrent/index.html index d7848cf..a66217d 100644 --- a/2024/07/25/async_block_concurrent/index.html +++ b/2024/07/25/async_block_concurrent/index.html @@ -25,7 +25,7 @@ - + diff --git a/2024/09/01/impl_macros_in_python/index.html b/2024/09/01/impl_macros_in_python/index.html index fbb10fe..0650274 100644 --- a/2024/09/01/impl_macros_in_python/index.html +++ b/2024/09/01/impl_macros_in_python/index.html @@ -25,7 +25,7 @@ - + @@ -311,9 +311,9 @@

1
2
3
4
5
6
7
8
#define VAL 123

int main(void){
printf("%d", VAL);
return 0;
}

>>> 123

Python 与 模块

Python 的模块是这样引入的

-

import -> finder/loader -> [compile to py_code] -> write to __py_cache__

+

import -> finder/loader -> [compile to py_code] -> write to __py_cache__

是否重新编译写入 py_code 到缓存拥有校验,校验的标准可能是文件的时间戳或是哈希值

-

如果校验结果相同,会尝试复用 py_cache__,而 sys.dont_write_bytecode 会影响读写 __py_cache

+

如果校验结果相同,会尝试复用 __py_cache__,而 sys.dont_write_bytecode 会影响读写 __py_cache__

Python 与 编译

Python 的编译是使用 builtins.compile 进行的,我们只需对这个函数进行修改,捕获并修改 source 便可达到宏的效果

builtins.compile 的其他参数可以帮你确定模块,例如 filename

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys
import builtins

sys.dont_write_bytecode = True

origin_compile = builtins.compile

def wrapper(source, *args, **kwargs):
return origin_compile(source.replace(...), *args, **kwargs)

builtins.compile = wrapper

import ...
diff --git a/about/index.html b/about/index.html index 20fe6fe..3bd2a66 100644 --- a/about/index.html +++ b/about/index.html @@ -25,8 +25,8 @@ - - + + @@ -305,7 +305,7 @@

Contact 📞

diff --git a/board/index.html b/board/index.html index 96d6217..356196e 100644 --- a/board/index.html +++ b/board/index.html @@ -23,8 +23,8 @@ - - + + diff --git a/local-search.xml b/local-search.xml index 73a123e..6049e20 100644 --- a/local-search.xml +++ b/local-search.xml @@ -8,7 +8,7 @@ /2024/09/01/impl_macros_in_python/ - 如何在 Python 中实现宏展开?

Python 的语法以灵活性见长,有时候可能需要动态地对一个模块进行修改,也就是为人熟知的 Monkey Patch

普通的修改对于一些静态生成的常量效果有限,例如下面这段代码,尽管可以修改 generator,但并不会影响调用这个函数生成的变量的值。

1
2
3
4
def generator(name): return "static " + name

python = generator("python")
rust = generator("rust")

那么有没有一种方法能够解决这个问题而又不直接修改这个文件?这个过程有点类似于许多语言的宏展开机制,因此本篇文章也取名为 如何在 Python 中实现宏展开?

什么是宏 (Macro)

简单地解释,宏就是在编译前对源代码进行替换。

C 中的宏最容易理解,下面这段代码就是对 VAL 进行了替换,不难理解.

1
2
3
4
5
6
7
8
#define VAL 123

int main(void){
printf("%d", VAL);
return 0;
}

>>> 123

Python 与 模块

Python 的模块是这样引入的

import -> finder/loader -> [compile to py_code] -> write to __py_cache__

是否重新编译写入 py_code 到缓存拥有校验,校验的标准可能是文件的时间戳或是哈希值

如果校验结果相同,会尝试复用 py_cache__,而 sys.dont_write_bytecode 会影响读写 __py_cache

Python 与 编译

Python 的编译是使用 builtins.compile 进行的,我们只需对这个函数进行修改,捕获并修改 source 便可达到宏的效果

builtins.compile 的其他参数可以帮你确定模块,例如 filename

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys
import builtins

sys.dont_write_bytecode = True

origin_compile = builtins.compile

def wrapper(source, *args, **kwargs):
return origin_compile(source.replace(...), *args, **kwargs)

builtins.compile = wrapper

import ...

Saleyo

Saleyo 提供了一套工具,推荐尝试使用😊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# targetmodule
def generate(name):
return name + " hell world"


class StaticMap:
FIELD = generate("hello")

# mixin
from typing import Any, Union
from saleyo.decorator.compile import CompileToken, CompileBoundary


@CompileToken(lambda info: "targetmodule.py" in str(info.filename))
def mixin_a(token: Union[str, bytes, Any]):
if not isinstance(token, bytes):
return
return token.replace(b"hell world", b"bye")


with CompileBoundary(): # Force to compile
from targetmodule import StaticMap

print(StaticMap().FIELD) # hello bye

>>> hello bye
]]>
+ 如何在 Python 中实现宏展开?

Python 的语法以灵活性见长,有时候可能需要动态地对一个模块进行修改,也就是为人熟知的 Monkey Patch

普通的修改对于一些静态生成的常量效果有限,例如下面这段代码,尽管可以修改 generator,但并不会影响调用这个函数生成的变量的值。

1
2
3
4
def generator(name): return "static " + name

python = generator("python")
rust = generator("rust")

那么有没有一种方法能够解决这个问题而又不直接修改这个文件?这个过程有点类似于许多语言的宏展开机制,因此本篇文章也取名为 如何在 Python 中实现宏展开?

什么是宏 (Macro)

简单地解释,宏就是在编译前对源代码进行替换。

C 中的宏最容易理解,下面这段代码就是对 VAL 进行了替换,不难理解.

1
2
3
4
5
6
7
8
#define VAL 123

int main(void){
printf("%d", VAL);
return 0;
}

>>> 123

Python 与 模块

Python 的模块是这样引入的

import -> finder/loader -> [compile to py_code] -> write to __py_cache__

是否重新编译写入 py_code 到缓存拥有校验,校验的标准可能是文件的时间戳或是哈希值

如果校验结果相同,会尝试复用 __py_cache__,而 sys.dont_write_bytecode 会影响读写 __py_cache__

Python 与 编译

Python 的编译是使用 builtins.compile 进行的,我们只需对这个函数进行修改,捕获并修改 source 便可达到宏的效果

builtins.compile 的其他参数可以帮你确定模块,例如 filename

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys
import builtins

sys.dont_write_bytecode = True

origin_compile = builtins.compile

def wrapper(source, *args, **kwargs):
return origin_compile(source.replace(...), *args, **kwargs)

builtins.compile = wrapper

import ...

Saleyo

Saleyo 提供了一套工具,推荐尝试使用😊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# targetmodule
def generate(name):
return name + " hell world"


class StaticMap:
FIELD = generate("hello")

# mixin
from typing import Any, Union
from saleyo.decorator.compile import CompileToken, CompileBoundary


@CompileToken(lambda info: "targetmodule.py" in str(info.filename))
def mixin_a(token: Union[str, bytes, Any]):
if not isinstance(token, bytes):
return
return token.replace(b"hell world", b"bye")


with CompileBoundary(): # Force to compile
from targetmodule import StaticMap

print(StaticMap().FIELD) # hello bye

>>> hello bye
]]>