Python中类方法的self参数是否必需?为何无self方法可运行?
Python 3.6.5中类内特殊方法的行为解析
先还原你提到的两种代码场景:
场景1:无self也无staticmethod的情况
class Foo: def bar(num): return num * 2 A1 = bar(1) # 正常运行,A1的值为2 print(Foo.bar(17)) # 正常运行,输出34
场景2:添加staticmethod后报错的情况
class Foo: @staticmethod def bar(num): return num * 2 A1 = bar(1) # 触发TypeError
一、这种写法是否合规?
完全合规。Python并没有强制要求类体中的所有函数必须是实例方法(带self)、类方法(带cls)或静态方法。类体本质上就是一个代码块,里面定义的函数在类定义阶段就是普通函数,只要调用时参数匹配就能正常执行。
二、运行原理
类定义阶段的调用逻辑:
类在被定义时,Python会按顺序执行类体里的每一行代码。此时bar只是类体内部的普通函数,还没有被绑定为类的方法,所以直接调用bar(1)和调用全局普通函数没有区别,只要参数数量匹配就没问题。类定义完成后的调用逻辑:
在Python 3中,类中未被装饰的无self函数,会被当作普通函数对象存储在类的命名空间里。所以通过Foo.bar(17)调用时,本质就是直接调用这个普通函数,参数匹配就可以正常运行(和Python 2不同,Python 2会把这类函数包装成「未绑定方法」,必须传入实例才能调用)。
三、添加staticmethod后报错的原因
@staticmethod装饰器会把原函数包装成一个静态方法对象。在类定义阶段直接调用bar(1)时,这个静态方法对象还没完成和类的绑定,Python会把静态方法实例本身作为第一个参数传递给原函数,导致参数数量不匹配(原函数只需要1个参数,现在传入了静态方法对象+1两个参数),因此触发TypeError。
如果要在类内调用静态方法,需要通过类名调用,比如改成A1 = Foo.bar(1)(注意类定义到这一步时,Foo已经被创建)。
四、存在的隐患
- 可读性差,易混淆:其他开发者看到类内的
bar函数,会默认它是实例方法,尝试用实例调用(比如foo = Foo(); foo.bar(1))时,会把实例作为第一个参数传入bar,导致参数不匹配报错。 - 不符合编码规范:虽然Python语法允许,但这种写法违背了类方法的常规设计逻辑,会增加代码的维护成本,团队协作时容易引发误解。
内容的提问来源于stack exchange,提问作者J.K.
相关产品推荐
相关产品推荐

