前言
Python与SAS是数据分析工作中常用的两款工具。二者都支持动态变量嵌入与文本格式化,以此简化代码编写。Python格式化字符和SAS Macro宏变量正是实现该功能的核心手段,但二者的语法和运行逻辑各不相同。本文将简单对比两者的用法与差异,方便大家在实操中灵活选用。欢迎同行友好交流、探讨指正。
首次发布:2026-06-13
1. 核心概念定义
什么是格式化字符串?
定义:任何包含占位符的字符串模板,涵盖 %、str.format()、f-string 三种风格。比如,"Hello, %s", "Hello, {0}", f"Hello, {name}"都是格式化字符串。本文重点介绍的是 f-string。
下面的代码是一个具体的例子:
name = "Yuang"
s = f"""Hello, everyone!
My name is {name}!"""
print(s)
输出的内容是:
Hello, everyone!
My name is Yuang!
其中,Python 使用三重引号后,可以在字符串内保留换行.
怎么样?这个用法是不是有点像 SAS 宏变量。在字符串中通过占位符引用变量,最终生成包含变量实际值的文本。sas 是 %let 定义+ ampersand (&) 解析,而python 是直接在 "格式化字符串字面量" 中用花括号将定义的变量括起来。
格式化字符串字面量 (Formatted String Literal) 是指 f"..." 或 F"..." 形式的字符串字面量。而这里面花括号包裹的内容被称为替换字段。
字面量 就是代码里「直接写出来的值」,比如 5、3.1415926 是数字的字面量,True/False 是布尔字面量。其实,替换字段也支持表达式,如 f"{x**2 + math.sqrt(y):.2f}"。
上述的表达式是什么意思呢?即x 的平方 + y 的平方根,最后再保留2位小数。
2. 变量的作用域
请先看一段 Python 代码:
x = 100
deffunc(x):
template = f"x is {x}"
print(template)
func(5)
我们首先给变量 x 赋值为100,然后函数参数也设置为 x, 我们会发现运行 func(5) 打印的值仍然是 5。因为 python 的变量查找遵循 LEGB 查找顺序, 优先级是这样的:
- Local:函数内部定义的变量、参数、for 循环变量等。
- Enclosing:外层函数的局部变量(对嵌套的内层函数而言)。
- Global:模块(即 .py 文件)顶层定义的变量,或者在函数内用 global 声明的变量。
- Built-in:Python 内置的名字,如 print、len、Exception 等。
看看这一段程序就很直观了:
len = "我是全局的 len"# G,覆盖了内置的 len
defouter():
len = "outer 的 len"# E(相对于 inner)
definner():
len = "inner 的 len"# L
print(len) # 先找 L,找到 "inner 的 len"
inner()
print(len) # 这里的 L 是 outer 的,打印 "outer 的 len"
outer()
print(len) # G,打印 "我是全局的 len"
而 SAS 宏变量的作用域是先查找当前宏的局部符号表,调用当前宏的上一层宏的局部符号表(调用者),逐级向外,最后是全局符号表。
SAS 解析宏变量的值取决于运行时是谁调用了它,作用域是动态的,他不像 Python 一样,作用域由代码的书写位置决定。我举一个例子:
%macro outer;
%let x = 10;
%inner
%mend outer;
%macro inner;
%put &x; /* 此处查找 x */
%mend inner;
%macro another;
%let x = 999;
%outer /* 在 another 内部调用 outer,outer 再调用 inner */
%mend another;
%another
上面这段 SAS 代码展示了3个宏的嵌套, 这里调用 %inner 的是 %outer, 所以 %put &x;在日志中的显示为 10。那么,如果我直接用宏 %another 调用 %inner 呢?
%macro another;
%let x = 999;
%inner
%mend another;
%another
那么输出的就是999了,因为 macro variable 取决于“谁在调用”而不是“在哪里定义”。
3. 通过条件生成代码
我们知道,SAS 宏 %IF 在宏处理器展开阶段工作,根据宏变量的值决定哪段代码被送往编译器。
如下面的代码块:在 sas 宏中, macro processor 会检测到 %if, 然后根据宏变量 type 判断需要执行的代码。这里执行的语句是 proc means 而不是 proc print.
%lettype = summary;
%macro report;
%if &type = summary %then %do;
proc means data=sashelp.shoes; run;
%end;
%else %do;
proc print data=sashelp.shoes; run;
%end;
%mend;
%report
那么,如果 Python 其实也可以实现这一功能。通常而言,python 的函数(对标 sas 宏)直接在 if 中用变量判断就可以了,比如这样:
from datetime import datetime
defgenerate_report(scores, report_type):
"""
scores: dict,如 {'语文':88, '数学':92, '英语':79}
report_type: str,取值 'summary' 或 'full'
"""
if report_type == "summary":
defcalc_summary(data):
return sum(data) / len(data)
result = calc_summary(scores.values())
print(f"汇总结果:{result:.2f}")
else:
defprint_full(data):
for k, v in data.items():
print(f"{k}: {v}")
print_full(scores)
print(f"完整数据输出于 {datetime.now():%Y-%m-%d}")
# 调用
scores = {'语文':88, '数学':92, '英语':79}
generate_report(scores, "summary")
generate_report(scores, "full")
当判断条件需要基于多个外部输入、动态拼接成特定模式,或者需要把非字符串类型强制统一为字符串再做比较时,f-string 在 if 条件里就非常有用。比如这样:
# 从外部获取用户和操作类型(例如 API 参数、表单、文件配置)
user = input("用户名: ")
action = input("操作: ")
# 需要组合成一个模式来判断权限
iff"{user}_{action}" == "Alice_delete":
print("允许删除")
else:
print("权限不足")
有一个点值得讨论, sas 中可以用 %if %then %do; %end; %else %do; %end; 控制变量或部分语句的生成。比如这样:
%macro analysis(type);
proc means data=sales;
var price;
%if &type = detailed %then %do;
/* 只在详细模式下增加 class 和 weight */
class region;
weight quantity;
%end;
run;
%mend;
上述代码中,%if 仅仅控制了部分语句。但 Python 的 if/else 只能控制“整块代码”执行,不能生成半行代码,需要另辟蹊径,通过其他方式执行。
✍️ 文 / Yuang
临研数据人: 用数字讲述药物故事