用@staticmethod修饰的方法和用@classmethod修饰的方法有什么区别?
当前回答
我将用一个例子来解释基本的区别。
class A(object):
x = 0
def say_hi(self):
pass
@staticmethod
def say_hi_static():
pass
@classmethod
def say_hi_class(cls):
pass
def run_self(self):
self.x += 1
print self.x # outputs 1
self.say_hi()
self.say_hi_static()
self.say_hi_class()
@staticmethod
def run_static():
print A.x # outputs 0
# A.say_hi() # wrong
A.say_hi_static()
A.say_hi_class()
@classmethod
def run_class(cls):
print cls.x # outputs 0
# cls.say_hi() # wrong
cls.say_hi_static()
cls.say_hi_class()
1-我们可以直接调用静态和类方法,而无需初始化
# A.run_self() # wrong
A.run_static()
A.run_class()
2-静态方法不能调用self方法,但可以调用其他静态和类方法
3-静态方法属于类,根本不会使用对象。
4-类方法不绑定到对象,而是绑定到类。
其他回答
关于如何在Python中使用静态、类或抽象方法的权威指南是本主题的一个很好的链接,总结如下。
@staticmethod函数只不过是在类中定义的函数。它可以在不首先实例化类的情况下调用。它的定义通过继承是不可变的。
Python不必为对象实例化绑定方法。它简化了代码的可读性,并且不依赖于对象本身的状态;
@classmethod函数也可以在不实例化类的情况下调用,但它的定义遵循子类,而不是父类,通过继承,可以被子类重写。这是因为@classmethod函数的第一个参数必须始终是cls(class)。
工厂方法,用于使用例如某种预处理为类创建实例。静态方法调用静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法
顾名思义,类方法用于更改类而不是对象。要对类进行更改,它们将修改类属性(而不是对象属性),因为这就是更新类的方式。这就是类方法将类(通常用“cls”表示)作为第一个参数的原因。
class A(object):
m=54
@classmethod
def class_method(cls):
print "m is %d" % cls.m
另一方面,静态方法用于执行未绑定到类的功能,即它们不会读取或写入类变量。因此,静态方法不将类作为参数。使用它们是为了使类可以执行与类的目的不直接相关的功能。
class X(object):
m=54 #will not be referenced
@staticmethod
def static_method():
print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
要决定是使用@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
我认为提供一个纯Python版本的staticmethod和classmethod将有助于在语言级别上理解它们之间的区别(请参阅Descriptor Howto Guide)。
这两个都是非数据描述符(如果您首先熟悉描述符,那么更容易理解它们)。
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
def inner(*args, **kwargs):
if cls is None:
cls = type(obj)
return self.f(cls, *args, **kwargs)
return inner
首先,让我们从一个示例代码开始,我们将使用它来理解这两个概念:
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中静态方法和类方法的区别是什么?