用@staticmethod修饰的方法和用@classmethod修饰的方法有什么区别?


当前回答

我开始用C++学习编程语言,然后是Java,然后是Python,所以这个问题也困扰了我,直到我理解了每种语言的简单用法。

类方法:Python不像Java和C++那样没有构造函数重载。为了实现这一点,可以使用classmethod。以下示例将对此进行解释

让我们考虑一个Person类,它接受两个参数first_name和last_name,并创建Person的实例。

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

现在,如果您需要仅使用一个名称(仅使用first_name)创建一个类,那么在Python中不能这样做。

当您尝试创建对象(实例)时,这将给您一个错误。

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

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

然而,您可以使用@classmethod实现以下相同的功能

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

静态方法:这相当简单,它不绑定到实例或类,您可以使用类名简单地调用它。

所以,在上面的示例中,假设您需要验证first_name不应超过20个字符,您可以简单地这样做。

@staticmethod  
def validate_name(name):
    return len(name) <= 20

您可以使用类名调用

Person.validate_name("Gaurang Shah")

其他回答

要决定是使用@staticmethod还是@classmethod,必须查看方法内部。如果您的方法访问类中的其他变量/方法,请使用@classmethod。另一方面,如果您的方法不涉及类的任何其他部分,则使用@staticmethod。

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to another class,
        #       you don't have to rename the referenced class 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Making juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing apple %d...' % apple)
        cls._counter += 1

也许一些示例代码会有所帮助:注意foo、class_foo和static_foo的调用签名的不同:

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({x})")

a = A()

下面是对象实例调用方法的常用方法。对象实例a作为第一个参数隐式传递。

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>, 1)

使用classmethods,对象实例的类作为第一个参数而不是self隐式传递。

a.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)

也可以使用类调用class_foo。事实上,如果你定义了类方法,这可能是因为您打算从类而不是从类实例调用它。A.foo(1)会引发TypeError,但A.class_foo(1)工作正常:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)

人们发现类方法的一个用途是创建可继承的替代构造函数。


使用staticmethods,self(对象实例)和cls(类)都不会作为第一个参数隐式传递。它们的行为类似于普通函数,只是您可以从实例或类调用它们:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

静态方法用于将与类有某种逻辑联系的函数分组到该类。


foo只是一个函数,但当你调用.foo时,你不只是得到函数,您将得到函数的“部分应用”版本,其中对象实例a作为函数的第一个参数。foo需要2个参数,而a.foo只需要1个参数。

a绑定到foo。这就是以下术语“约束”的含义:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

对于.class_foo,a不绑定到class_foo,而类a绑定到class-foo。

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

这里,对于staticmethod,即使它是一个方法,a.static_foo也只返回一个没有参数约束的好的ole函数。static_foo需要1个参数,并且.static_foo也需要1个参数。

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

当然,当用类A调用static_foo时也会发生同样的情况。

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

从其文档中定义静态方法和类方法。以及何时使用静态方法和何时使用类方法。

静态方法类似于java和C#中的静态方法,它不会使用类的任何初始化值,只需要从外部进行操作即可。类方法:通常用于继承重写,当我们重写一个方法时,然后使用CLS实例来判断是否要调用子类或父类的方法。以防您希望同时使用同名和不同签名的方法。

静态方法(函数)->方法

Convert a function to be a static method.

A static method does not receive an implicit first argument.
To declare a static method, use this idiom:

     class C:
         @staticmethod
         def f(arg1, arg2, ...):
             ...

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()).  The instance is ignored except for its class.

Static methods in Python are similar to those found in Java or C++.
For a more advanced concept, see the classmethod builtin.
"""

classmethod(函数)->方法

Convert a function to be a class method.

A class method receives the class as implicit first argument,
just like an instance method receives the instance.
To declare a class method, use this idiom:

  class C:
      @classmethod
      def f(cls, arg1, arg2, ...):
          ...

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()).  The instance is ignored except for its class.
If a class method is called for a derived class, the derived class
object is passed as the implied first argument.

Class methods are different than C++ or Java static methods.
If you want those, see the staticmethod builtin.

我开始用C++学习编程语言,然后是Java,然后是Python,所以这个问题也困扰了我,直到我理解了每种语言的简单用法。

类方法:Python不像Java和C++那样没有构造函数重载。为了实现这一点,可以使用classmethod。以下示例将对此进行解释

让我们考虑一个Person类,它接受两个参数first_name和last_name,并创建Person的实例。

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

现在,如果您需要仅使用一个名称(仅使用first_name)创建一个类,那么在Python中不能这样做。

当您尝试创建对象(实例)时,这将给您一个错误。

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

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

然而,您可以使用@classmethod实现以下相同的功能

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

静态方法:这相当简单,它不绑定到实例或类,您可以使用类名简单地调用它。

所以,在上面的示例中,假设您需要验证first_name不应超过20个字符,您可以简单地这样做。

@staticmethod  
def validate_name(name):
    return len(name) <= 20

您可以使用类名调用

Person.validate_name("Gaurang Shah")

只有第一个参数不同:

normal方法:当前对象作为(附加)第一个参数自动传递classmethod:当前对象的类自动作为(附加的)第一个参数传递staticmethod:不会自动传递额外的参数。传递给函数的就是得到的。

更详细地说。。。

正常方法

“标准”方法,如在所有面向对象的语言中。当调用对象的方法时,会自动为其提供一个额外的参数self作为其第一个参数。即,方法

def f(self, x, y)

必须使用2个参数调用。self是自动传递的,它是对象本身。类似于这个神奇地出现在例如java/c++中,只有在python中才显式显示。

实际上,第一个参数不必称为self,但它是标准的约定,所以请保留它

类方法

装饰方法时

@classmethod
def f(cls, x, y)

自动提供的参数不是self,而是self的类。

静态法

装饰方法时

@staticmethod
def f(x, y)

该方法根本没有给出任何自动参数。它只提供调用它的参数。

用法

classmethod主要用于替代构造函数。staticmethod不使用对象的状态,甚至不使用类本身的结构。它可以是类外部的函数。它只放在类中,用于对具有类似功能的函数进行分组(例如,像Java的Math类静态方法)

class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)