普贤教程 › 基础教程
函数
函数用 def 定义,参数支持默认值,返回值只有一个——但可以是列表或字典,所以"返回多个值"并不受限。
定义与调用
px
def add(a, b): return a + b print(str(add(1, 2)))
text
3没有 function、没有 fn(fn 是匿名函数的写法,见后文)、没有类型标注。参数名写错会在运行时得到「未定义变量」。
默认参数
px
def greet(name, greeting = "你好"): return greeting + "," + name print(greet("晨曦")) print(greet("晨曦", "早上好"))
text
你好,晨曦 早上好,晨曦
有默认值的参数必须放在最后。调用时可以只传前面的参数。
返回值
没有 return 的函数返回 null:
px
def log(msg): print("[log] " + msg) var r = log("hi") print(str(r == null))
text
[log] hi
true要返回多个值,用字典或列表:
px
def divmod2(a, b): return {"q": a // b, "r": a % b} var m = divmod2(17, 5) print(str(m["q"]) + " 余 " + str(m["r"]))
text
3 余 2
递归
px
def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2) print(str(fib(10)))
text
55递归深度递归太深会耗尽栈。深层遍历(目录树、嵌套 JSON)优先写成迭代 + 显式栈。
px
# 迭代版:阶乘 def fact(n): var r = 1 var i = 2 while i <= n: r *= i i += 1 return r print(str(fact(10)))
text
3628800作用域
规则只有两条,但第二条和很多语言不同,必须记住:
- 用
var/let声明,创建局部变量,同名时遮蔽外层; - 不声明直接赋值,写入的是已存在的那个外层变量——不会新建局部变量。
px
var counter = 0 def shadow(): var counter = 1 # 局部变量,外层不受影响 return counter def touch(): counter = 9 # 直接赋值 = 改外层的 counter print(str(shadow())) print(str(counter)) touch() print(str(counter))
text
1 0 9
因此"在函数里加一句赋值"有可能悄悄改掉全局状态。这不是 bug,是语言的选择:声明即局部,赋值即外层。写函数时想清楚要不要改变量。
要跨调用保存状态,用字典或把状态作为参数显式传递:
px
def bump(state): state["n"] = state["n"] + 1 return state["n"] var st = {"n": 0} print(str(bump(st))) print(str(bump(st))) print(str(st["n"]))
text
1 2 2
函数是一等值
函数可以存在变量里、当参数传:
px
def double(x): return x * 2 def apply(f, x): return f(x) print(str(apply(double, 21)))
text
42匿名函数 fn
短小的函数可以写成 fn(参数): 表达式 形式,常见于回调与字典分派:
px
var ops = { "inc": fn(x): x + 1, "dec": fn(x): x - 1, "sq": fn(x): x * x } def run(name, x): if not ops.has(name): return Err("未知操作:" + name) return Ok(ops[name](x)) print(str(run("sq", 7).ok())) print(str(run("nope", 1).is_err()))
text
49 true
分派表写法
参数多、分支多时,用字典把「名字 → 处理函数」映射起来,比一长串 elif 好维护:
px
def handle_add(body): return Ok(body["a"] + body["b"]) def handle_mul(body): return Ok(body["a"] * body["b"]) let ROUTES = {"add": handle_add, "mul": handle_mul} def dispatch(action, body): if not ROUTES.has(action): return Err("未知动作:" + action) return ROUTES[action](body) print(str(dispatch("add", {"a": 2, "b": 3}).ok())) print(str(dispatch("mul", {"a": 2, "b": 3}).ok()))
text
5 6
嵌套函数
px
def outer(x): def inner(y): return y + 1 return inner(x) * 2 print(str(outer(3)))
text
8内层函数能读到外层的变量。这在写小工具时很方便,但别用它来当"隐式全局状态"。
写函数的几条建议
- 一个函数只做一件事,超过 40 行考虑拆。
- 参数超过 4 个时,改成传一个字典。
- 可能失败的函数返回
Ok/Err,不要返回null让调用方猜。 - 不要在函数里改传进来的字典「顺便」当返回值——要么返回,要么显式修改 out 参数,并在注释里写明。