Python中静态方法语法是如何选择的?

14 投票
8 回答
1215 浏览
提问于 2025-04-15 14:35

我已经用Python编程一段时间了,发现声明静态方法的语法有点奇怪。

一个普通的方法是这样声明的:

def mymethod(self, params)
   ...
   return

而静态方法是这样声明的:

def mystaticethod(params)
   ...
   return
mystaticmethod = staticmethod(mystaticmethod)

如果你不加上静态方法的那一行,编译器会抱怨说缺少self。

这其实是个很复杂的方式来做一些很简单的事情,而在其他语言中,只需要用一个关键词和简单的语法就能搞定。有没有人能告诉我这种语法是怎么演变过来的?难道这只是因为在已有的语言中加入了类的概念吗?

因为我可以把静态方法的那一行放到类的后面,这也说明解析器在处理这些内容时工作得很辛苦。

我知道后来加入了装饰器语法,但我想了解一下最初的语法是怎么设计出来的。从语言设计的角度来看,我唯一能想到的就是静态方法的应用会把函数对象转变成静态方法。

8 个回答

4

在Python中,静态方法的概念可以追溯到Python 2.2引入的“新式类”。在这之前,类中的方法其实就是普通的函数,它们被当作类的属性来存储:

class OldStyleClass:
    def method(self):
        print "'self' is just the first argument of this function"

instance = OldStyleClass()
OldStyleClass.method(instance) # Just an ordinary function call
print repr(OldStyleClass.method) # "unbound method..."

当你在实例上调用这些方法时,系统会特别处理,自动把实例作为第一个参数传给函数:

instance.method() # 'instance' is automatically passed in as the first parameter
print repr(instance.method) # "bound method..."

在Python 2.2中,类的系统经过重新设计,变成了“新式类”,这些类都是从object继承而来的。新式类的一个特点是“描述符”,简单来说,就是一个在类中负责描述、获取和设置类属性的对象。描述符有一个__get__方法,这个方法会接收类和实例作为参数,并返回请求的类或实例的属性。

描述符让我们可以用一个统一的接口来实现类属性的复杂行为,比如属性、类方法和静态方法。例如,staticmethod描述符可以这样实现:

class staticmethod(object):
    """Create a static method from a function."""

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls=None):
        return self.func

我们可以把这个和一个假设的普通方法的纯Python描述符进行对比,后者是默认用于类属性中的所有普通函数(虽然这并不是实例方法查找时的确切情况,但它确实处理了自动传入的'self'参数):

class method(object):
    """Create a method from a function--it will get the instance
    passed in as its first argument."""

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls=None):
        # Create a wrapper function that passes the instance as first argument
        # to the original function
        def boundmethod(*args, **kwargs):
            return self.func(self, *args, **kwargs)
        return boundmethod

所以,当你写method = staticmethod(method)时,其实是在创建一个新的描述符,这个描述符的工作就是返回原始函数,而不做任何改变,并把这个描述符存储在类的“method”属性中。

如果觉得这样做似乎很麻烦,只是为了拿回原来的函数——没错,确实是这样。但因为普通方法调用是默认情况,所以静态方法和类方法需要单独实现,而描述符提供了一种通过一个简单的接口来启用这些和其他复杂行为的方法。

正如其他人已经指出的,Python 2.4引入的装饰器语法提供了一种更方便的方式来声明静态方法,但这只是语法上的便利,并没有改变静态方法的工作方式。

想了解更多关于新式类和描述符的细节,可以查看http://www.python.org/doc/2.2.3/whatsnew/sect-rellinks.htmlhttp://users.rcn.com/python/download/Descriptor.htm

11

voyager和adurdin在解释发生了什么方面做得很好:在Python 2.2引入了新式类和描述符后,出现了新的深层语义可能性。最明显有用的例子(静态方法、类方法、属性)都得到了内置描述符类型的支持,而这一切并没有引入新的语法(装饰器的语法@foo是在之后的几个版本中添加的,那时新描述符已经充分证明了它们在实际中的有用性)。我并不觉得自己有资格代言Guido(需要Tim Peters的时候他去哪儿了!),但那时我已经是Python的贡献者,参与了这些开发,我可以确认确实是这样的。

voyager提到这让我想起C语言的观察非常到位:我一直认为Python比那些模仿C语言语法的语言(比如大括号、if/while后面的括号等)更能体现“C的精神”。“C的精神”实际上在ISO C标准的(非规范性)理由部分中有描述,包括五个原则(其中没有一个需要大括号!),我认为Python符合其中的4.5个(如果你感兴趣,网上有我关于“程序员的Python”的演讲视频,里面有详细讲解)。

特别是,C的精神“只提供一种方式来完成一个操作”与Python的禅意“应该有一种——最好只有一种——明显的方式来做到这一点”相吻合。我相信C和Python是唯一两个明确采用这种统一性和非冗余设计理念的广泛使用语言(这是一种理想,不能完全实现,比如如果a和b是整数,a+b和b+a应该是两种显而易见的求和方式!)——但这是一个值得追求的目标!

12

静态方法是在Python中引入的时间比类晚得多。类的引入可以追溯到很早,可能在1.0版本之前就已经有了,而静态方法大约是在2.0版本时才出现的。静态方法是通过对普通方法进行修改来实现的——你需要从一个函数创建一个静态方法对象,才能得到一个静态方法,而编译器默认生成的是实例方法。

在Python中,很多东西都是在使用过程中逐渐引入和改进的。最开始引入静态方法时,是为了在不增加新语法的情况下让大家理解它的用法(而且Python对语法的改变是比较保守的)。我不是Guido,所以不太清楚他当时的想法,这只是我的猜测,但Python的发展通常比较缓慢,都是逐步进行的,并在积累经验后进行改进(特别是,他们不喜欢在没有找到正确方法之前就添加新功能。这可能就是为什么一开始静态方法没有特别语法的原因)。

不过,正如mjv所提到的,现在有一种更简单的方法,这种方法是在2.2或2.3版本中引入的,叫做“装饰器”:

@staticmethod
def mystaticmethod(params)
    ...
    return

@staticmethod的语法其实就是在方法定义后面加上mystaticmethod = staticmethod(mystaticmethod)的简化写法。

撰写回答