Python中@staticmethod有什么作用?

7
我已经编写了这个简短的测试/示例代码,以便更好地理解Python中静态方法的工作原理。
class TestClass:
    def __init__(self, size):
        self.size = size

    def instance(self):
        print("regular instance method - with 'self'")

    @staticmethod
    def static():
        print("static instance method - with @staticmethod")

    def static_class():
        print("static class method")


a = TestClass(1000)

a.instance()
a.static()
TestClass.static_class()

这段代码运行正确,没有返回任何错误。我的问题是:
  1. 我是否理解"self"的含义为:“这个方法将从一个实例中被调用”?

  2. 那么,@staticmethod背后的逻辑是什么 - 是创建可从实例中调用的静态方法吗?这难道不正是静态方法的作用吗?

  3. 为什么第二种方法比第三种更受欢迎呢?(我假设由于装饰器的存在,这是有意义的。)第三个选项似乎更简单和直接。


1
请注意,这在2.x版本中根本不起作用,而a.static_class()在任何版本中都无法使用。 - jonrsharpe
1
TestClass.static()a.static_class()添加到您的测试用例中,并观察发生了什么。这可能有助于说明为什么@staticmethod很有用。 - Graham Dumpleton
“这不是静态方法的本意吗?” - 能够在实例上调用它的优点是cls.whatever()self.whatever()将正确工作,使您可以将类名保留在类和实例方法之外,并正确处理继承。 - jonrsharpe
3个回答

6
这里是一篇关于Python静态方法的文章。总结如下:
  • 实例方法:需要将实例作为第一个参数
  • 类方法:需要将类作为第一个参数
  • 静态方法:不需要将实例或类作为第一个参数

举例

当调用带参数的方法时,更容易看出它们的工作原理。以下是一个修改后的示例:

class TestClass:

    weight = 200                             # class attr 

    def __init__(self, size):
        self.size = size                     # instance attr

    def instance_mthd(self, val):
        print("Instance method, with 'self':", self.size*val)

    @classmethod
    def class_mthd(cls, val):
        print("Class method, with `cls`:", cls.weight*val)

    @staticmethod
    def static_mthd(val):
        print("Static method, with neither args:", val)

a = TestClass(1000)

a.instance_mthd(2)
# Instance method, with 'self': 2000

TestClass.class_mthd(2)
# Class method, with `cls`: 400

a.static_mthd(2)
# Static method, with neither args: 2

注意事项

总的来说,您可以从访问的角度来思考每种方法:

  • 如果您需要访问实例或实例组件(例如实例属性),请使用实例方法,因为它将self作为第一个参数传递。
  • 同样地,如果您需要访问类,请使用类方法。
  • 如果既不需要实例也不需要类的访问,可以使用静态方法。

在上面的示例中,对于每种方法类型都传递了相同的参数,但是通过selfcls,实例和类属性的访问方式有所不同。

请注意,通过使用self.__class__,可以从实例方法中访问类组件,从而避免了使用类方法的需要。

    ...

    def instance_mthd2(self, val):
        print("Instance method, with class access via `self`:", self.__class__.weight*val)
    ...

a.instance_mthd2(2)
# Instance method, with class access via `self`: 400

关于您的问题:

  1. 是的。虽然变量名self是一种约定,但它与实例有关。
  2. 静态方法可用于在同一类下分组相似的实用方法。
  3. 对于类内方法,您需要将self添加为第一个参数,或者使用@staticmethod修饰该方法。没有参数的“未装饰方法”将引发错误。

另请参阅

  • R. Hettinger 的演讲 Python's Class Development Toolkit - 通过示例清楚地解释了每种方法类型的目的。

从我的例子中,你认为static_class()是你定义的类方法吗? - rbrtk
1
不太对。使用@classmethod修饰并将cls作为第一个参数传递,这样它就是一个类方法了。 - pylang

2

方法作用于被调用的实例上。惯例上,实例作为第一个参数传递,通常称为self

类方法类似,但作用于整个类对象而不是其中一个实例。它们非常方便作为构造函数和工厂函数,或者用于配置设置和其他影响整个类或所有实例的情况,而不是单个实例。

第三个选项是静态方法,它是奇怪的。它们既不传递实例也不传递类。他们对于在程序的类结构中嵌套实用程序函数以进行组织是很好的,但以一种明确地表示(对于代码审查者,“linting”和程序检查工具等),您有意不依赖于实例或类值的方式。这样,您就不会收到关于未使用的self的“变量已声明但从未使用”的警告。

从调用者的角度来看,静态方法看起来像任何其他方法调用。如果没有@staticmethod可用,您可以使用普通的实例或类方法(尽管存在过多的“变量未使用!”Linter警告风险)。因此,与类方法不同,静态方法是Python的完全可选部分。它们不会为语言添加任何功能;相反,它们提供了一种使开发人员意图更清晰的方法。


1

以下是您问题的答案:

问题1:

我是否正确理解,“self”可以理解为“该方法将从实例中调用”?

不完全正确。 self意味着函数的第一个参数应该是类的实例。例如:

def my_class_function(self)

可以称为:
self.my_class_function()
OR,
my_class_function(self)

此外,不必使用self作为对类对象的引用。您可以使用任何东西(只要它是有效的变量),但在各个地方遵循的标准是使用self
问题2:那么@staticmethod背后的逻辑是什么 - 它是创建静态方法以从实例调用的吗?这难道不正是静态方法所关注的吗?
在函数中使用@staticmethod变量时,您不需要在函数内部使用类对象的任何引用,即您不需要使用self来访问类的任何属性或函数。
问题3:为什么第二种方法比第三种更受欢迎? (我假设由于装饰器存在,因此有一点意义。)第3个选项似乎更简单直接。
使用第二种方法即使用@staticmetod,您可以使用类对象从类外部调用函数,而不像第三种方法(不使用装饰器)那样,函数的范围在类内部。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接