当前位置:首页>python>Python 函数竟然不用写返回类型?我多年 C# 习惯崩了

Python 函数竟然不用写返回类型?我多年 C# 习惯崩了

  • 2026-10-11 06:42:28
Python 函数竟然不用写返回类型?我多年 C# 习惯崩了

上周帮一个 C# 团队做 Python 培训,有个学员写了个函数:

defcalculate_price(quantity, unit_price):return quantity * unit_price

然后他问我:"返回类型写在哪?"

我说不用写。他不信,说"这编译器怎么知道返回的是 int 还是 float?"

我说 Python 没有编译器。他沉默了五秒,然后说:"那类型安全怎么办?"

这个问题,每个从 C# 转 Python 的人都会问。今天聊聊函数定义——Python 的"极简主义"到底能走到哪一步。

函数定义:告别那些仪式

C# 定义一个函数,仪式感拉满:

publicstaticintAdd(int a, int b){return a + b;}

Python 呢?

defadd(a, b):return a + b

不需要 public,不需要 static,不需要 int,不需要 {},甚至不需要分号。

这不是偷懒,是设计哲学——Python 认为"代码是写给人看的",类型信息应该在需要时才写。

要素
C#
Python
关键字
返回类型 方法名()def 函数名():
返回类型
必须声明
可选(类型注解)
参数类型
必须声明
可选(类型注解)
访问修饰符
public/private
没有(约定 _ 开头为私有)
静态方法
static
 关键字
不需要

参数:Python 的灵活度碾压 C#

默认参数

C# 从 4.0 才支持默认参数,Python 从出生就有:

defgreet(name, greeting="你好"):returnf"{greeting}, {name}!"greet("张三")           # "你好, 张三!"greet("张三", "早上好")  # "早上好, 张三!"

关键字参数

C# 的命名参数需要特殊语法,Python 直接用关键字:

defcreate_user(name, age, email=None, phone=None):return {"name": name, "age": age, "email": email, "phone": phone}# 只传必要参数user1 = create_user("张三", 25)# 跳过中间参数user2 = create_user("李四", 30, phone="13800138000")

C# 要实现"跳过中间参数",得用命名参数:

var user2 = Create(name: "李四", age: 30, phone: "13800138000");

Python 的关键字参数更自然——你不需要记住参数顺序,按名字传就行。

万能参数

C# 有 params,但只能接收固定类型的可变参数。Python 有 *args 和 **kwargs:

deflog_message(level, *args, **kwargs):    print(f"[{level}]")for msg in args:        print(f"  {msg}")for key, value in kwargs.items():        print(f"  {key}={value}")log_message("INFO", "用户登录", "IP=192.168.1.1", user="张三", action="login")

C# 没有 **kwargs 的等价物——如果你想传递动态的命名参数,得用 IDictionary<string, object> 然后手动处理。

仅位置/关键字参数

Python 3.8+ 引入了 / 和 * 分隔符,精确控制参数传递方式:

deffunc(a, b, /, c, d, *, e, f):# a, b: 只能按位置传# c, d: 可以按位置或关键字传# e, f: 只能按关键字传pass

C# 没有这个能力。这是 Python 参数控制的"终极形态"。

多返回值:告别 out 参数

C# 的 out 参数是出了名的啰嗦:

publicstaticvoidDivide(int a, int b, outint quotient, outint remainder){    quotient = a / b;    remainder = a % b;}int q, r;Divide(10, 3, out q, out r);

Python 直接 return 两个值:

defdivide(a, b):return a // b, a % bquotient, remainder = divide(10, 3)

原理是:Python 把返回值自动打包成元组,调用方自动解包。

函数是一等公民

在 Python 里,函数就是普通对象——可以赋值、可以传参、可以当返回值:

defapply(func, value):return func(value)# 传函数名result = apply(lambda x: x * 2, 5)  # 10# 函数作为字典值operations = {"add": lambda a, b: a + b,"sub": lambda a, b: a - b,"mul": lambda a, b: a * b,}result = operations["add"](3, 4)  # 7

C# 需要委托或 Func<T> 类型才能实现同样功能:

publicintApply(Func<int, int> func, intvalue){return func(value);}int result = Apply(x => x * 2, 5);

Python 不需要声明委托类型——直接传函数名就行。

文档字符串:比 XML 注释更实用

C# 用 XML 注释:

///<summary>/// 计算两数之和///</summary>///<param name="a">第一个数</param>///<param name="b">第二个数</param>publicintAdd(int a, int b);

Python 用文档字符串:

defadd(a, b):"""    计算两数之和    Args:        a: 第一个数        b: 第二个数    Returns:        两数之和    """return a + b# 直接访问文档help(add)print(add.__doc__)

Python 的文档字符串是运行时对象,可以通过 __doc__ 属性直接访问——比 C# 的 XML 注释更灵活。

默认参数的坑:可变对象

这是 Python 最经典的坑——默认参数是可变对象:

# 危险!默认参数是列表(可变对象)defappend_to(item, lst=[]):    lst.append(item)return lstprint(append_to(1))  # [1]print(append_to(2))  # [1, 2]  不是 [2]!

原因:默认参数在函数定义时创建,之后每次调用都复用同一个对象。

正确写法:

defappend_to(item, lst=None):if lst isNone:        lst = []    lst.append(item)return lst

C# 没有这个问题——C# 的默认参数是编译时常量。

我的真实感受

从 C# 转到 Python 后,函数定义是最让我"放松"的地方。

不再需要写返回类型、参数类型、访问修饰符——def add(a, b): return a + b 就够了。

但"放松"不等于"随意"。Python 3.5+ 引入了类型注解,让你在需要时可以添加类型信息:

defadd(a: int, b: int) -> int:return a + b

类型注解是可选的——你可以选择在关键函数上添加类型信息,让 IDE 和 mypy 帮你检查。

Python 的函数定义是"先干活再说"——不需要签合同就能开工。但如果你想要类型安全,类型注解随时可以加上。

下一篇聊聊 Lambda 表达式——Python 的 lambda 只能写一行,但这一行能干的事比你想的多。

最新文章

随机文章