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


当前回答

让我先告诉一下用@classmethod修饰的方法和@staticmethod修饰的方法之间的相似性。

相似性:它们都可以在类本身上调用,而不仅仅是类的实例。所以,在某种意义上,这两种方法都是Class的方法。

区别:类方法将接收类本身作为第一个参数,而静态方法不接收。

因此,在某种意义上,静态方法并不绑定到类本身,只是因为它可能具有相关功能而挂在那里。

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

其他回答

首先,让我们从一个示例代码开始,我们将使用它来理解这两个概念:

class Employee:

    NO_OF_EMPLOYEES = 0
  
    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary
        self.increment_employees()

    def give_raise(self, amount):
        self.salary += amount

    @classmethod
    def employee_from_full_name(cls, full_name, salary):
        split_name = full_name.split(' ')
        first_name = split_name[0]
        last_name = split_name[1]
        return cls(first_name, last_name, salary)

    @classmethod
    def increment_employees(cls):
        cls.NO_OF_EMPLOYEES += 1

    @staticmethod
    def get_employee_legal_obligations_txt():
        legal_obligations = """
        1. An employee must complete 8 hours per working day
        2. ...
        """
        return legal_obligations

Class方法

类方法接受类本身作为隐式参数,以及(可选地)定义中指定的任何其他参数。重要的是要理解类方法不能访问对象实例(就像实例方法一样)。因此,类方法不能用于更改实例化对象的状态,而是能够更改该类的所有实例之间共享的类状态。当我们需要访问类本身时,类方法通常很有用——例如,当我们想要创建工厂方法时,即创建类实例的方法。换句话说,类方法可以作为替代构造函数。

在我们的示例代码中,可以通过提供三个参数来构造Employee的实例;first_name、last_name和薪水。

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.first_name)
print(employee_1.salary)

'Andrew'
85000

现在,让我们假设有可能在单个字段中提供雇员的姓名,在该字段中,名字和姓氏用空格分隔。在本例中,我们可以使用名为employee_from_full_name的类方法,该方法总共接受三个参数。第一个是类本身,这是一个隐式参数,这意味着在调用方法时不会提供它-Python将自动为我们执行此操作:

employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(employee_2.first_name)
print(employee_2.salary)

'John'
95000

请注意,也可以从对象实例调用employee_from_full_name,尽管在这种情况下,这没有什么意义:

employee_1 = Employee('Andrew', 'Brown', 85000)
employee_2 = employee_1.employee_from_full_name('John Black', 95000)

我们可能想要创建类方法的另一个原因是,当我们需要更改类的状态时。在我们的示例中,类变量NO_OF_EMPLOYEES跟踪当前为公司工作的员工数量。每次创建Employee的新实例时都会调用此方法,并相应地更新计数:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')
employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')

Number of employees: 1
Number of employees: 2

静态方法

另一方面,在静态方法中,实例(即self)和类本身(即cls)都不会作为隐式参数传递。这意味着此类方法不能访问类本身或其实例。现在有人可能会争辩说,静态方法在类的上下文中并不有用,因为它们也可以放在助手模块中,而不是作为类的成员添加它们。在面向对象的编程中,将类构造成逻辑块非常重要,因此,当我们需要在类下添加方法时,静态方法非常有用,因为它在逻辑上属于该类。在我们的示例中,名为get_eemployee_legal_entributions_txt的静态方法只返回一个字符串,该字符串包含公司每个员工的法律义务。此函数不与类本身或任何实例交互。它可能被放置在不同的帮助器模块中,但是,它只与这个类相关,因此我们必须将它放置在Employee类下。

可以直接从类本身访问静态方法

print(Employee.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

或从类的实例:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

工具书类

Python中静态方法和类方法的区别是什么?

@类方法:

可以通过cls和直接通过类名调用类变量和实例、类和静态方法,但不能通过实例变量。可以通过对象和类名直接调用。第一个参数需要cls,否则无法调用@classmethod,并且按照惯例使用cls的名称,因此其他名称而不是cls仍然有效。

@静态方法:

既可以由对象调用,也可以直接由类名调用。可以通过类名而不是实例变量直接调用类变量和实例、类和静态方法。不需要self或cls。

*在回答Python中什么是“实例方法”时,我还详细解释了实例方法?

@类方法:

例如,@classmethod可以通过cls和直接通过类名调用类变量和实例、类和静态方法,@classmethod可以通过对象和直接通过类名称调用,如下所示:

class Person:
    x = "Hello"
    def __init__(self, name):
        self.name = name
    
    @classmethod # Here
    def test1(cls):
        print(cls.x)   # Class variable by `cls`
        cls.test2(cls) # Instance method by `cls`
        cls.test3()    # Class method by `cls`
        cls.test4()    # Static method by `cls`
        print()
        print(Person.x)       # Class variable by class name
        Person.test2("Test2") # Instance method by class name
        Person.test3()        # Class method by class name
        Person.test4()        # Static method by class name
    
    def test2(self):
        print("Test2")
        
    @classmethod
    def test3(cls):
        print("Test3")
        
    @staticmethod
    def test4():
        print("Test4")

obj = Person("John")
obj.test1() # By object

# Or

Person.test1() # By class name

输出:

Hello
Test2
Test3
Test4

Hello
Test2
Test3
Test4

而且,@classmethod不能同时通过cls和直接通过类名调用实例变量,因此如果@classmethod试图同时通过cls和直接通过类名称调用实例变量(如下所示):

# ...
    
    @classmethod
    def test1(cls):
        print(cls.name) # Instance variable by `cls`
        
        # Or

        print(Person.name) # Instance variable by class name
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

出现以下错误:

AttributeError:类型对象“Person”没有属性“name”

如果@classmethod没有cls:

# ...
    
    @classmethod
    def test1(): # Without "cls"
        print("Test1")
  
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

@无法调用classmethod,则出现如下错误:

TypeError:test1()采用0个位置参数,但给出了1个

而且,cls的名称在约定中使用,因此其他名称而不是cls仍然有效,如下所示:

# ...

    @classmethod
    def test1(orange):
        print(orange.x)      # Class variable
        orange.test2(orange) # Instance method
        orange.test3()       # Class method
        orange.test4()       # Static method

# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

输出:

Hello
Test2
Test3
Test4

@静态方法:

例如,@staticmethod既可以按对象调用,也可以按类名直接调用,如下所示:

class Person:
    x = "Hello"
    def __init__(self, name):
        self.name = name

    @staticmethod # Here
    def test1():
        print("Test1")
    
    def test2(self):
        print("Test2")
        
    @classmethod
    def test3(cls):
        print("Test3")
        
    @staticmethod
    def test4():
        print("Test4")

obj = Person("John")
obj.test1() # By object

# Or

Person.test1() # By class name

输出:

Test1

而且,@staticmethod可以通过类名直接调用类变量和实例、类和静态方法,而不是实例变量,如下所示:

# ...
    
    @staticmethod
    def test1():
        print(Person.x)       # Class variable
        Person.test2("Test2") # Instance method
        Person.test3()        # Class method
        Person.test4()        # Static method
            
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

输出:

Hello
Test2
Test3
Test4

并且,如果@staticmethod尝试调用实例变量,如下所示:

# ...
    
    @staticmethod
    def test1():
        print(Person.name) # Instance variable
            
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

出现以下错误:

AttributeError:类型对象“Person”没有属性“name”

而且,@staticmethod不需要self或cls,因此如果@staticmmethod具有self或cl,则需要传递如下所示的参数:

# ...
    
    @staticmethod
    def test1(self): # With "self"
        print(self)

    # Or

    @staticmethod
    def test1(cls): # With "cls"
        print(cls)

# ...

obj = Person("John")
obj.test1("Test1") # With an argument

# Or

Person.test1("Test1") # With an argument

输出:

Test1

否则,如果不传递如下所示的参数:

# ...
    
    @staticmethod
    def test1(self): # With "self"
        print("Test1")

    # Or

    @staticmethod
    def test1(cls): # With "cls"
        print("Test1")

# ...

obj = Person("John")
obj.test1() # Without an argument

# Or

Person.test1() # Without an argument

出现以下错误:

TypeError:test1()缺少1个必需的位置参数:“self”

TypeError:test1()缺少1个必需的位置参数:“cls”

只有第一个参数不同:

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)

这是一篇关于这个问题的短文

@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。@classmethod函数也可以在不实例化类的情况下调用,但它的定义通过继承遵循子类,而不是父类。这是因为@classmethod函数的第一个参数必须始终是cls(class)。

静态方法是一种对所调用的类或实例一无所知的方法。它只获取传递的参数,而不是隐式的第一个参数。它在Python中基本上是无用的——您可以只使用模块函数而不是静态方法。

另一方面,类方法是一种方法,它将被调用的类或被调用的实例的类作为第一个参数传递。当您希望该方法成为类的工厂时,这很有用:因为它获得了作为第一个参数调用的实际类,所以即使涉及子类,您也可以始终实例化正确的类。例如,观察类方法dict.fromkeys()在子类上调用时如何返回子类的实例:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>