前言
上一篇文章讲抽象类的时候,我们看的langchain-chatchat 代码里,往下翻还有两个很有意思的装饰器@staticmethod和@classmethod,那么他们分别有什么作用呢
一、@staticmethod 装饰器
@staticmethod 是 Python 中的一个装饰器,用于定义类方法。与普通的实例方法不同,@staticmethod 装饰的方法不需要访问类的实例或类的任何属性,因此它不传递 self 或 cls 参数。这使得静态方法可以在不创建类实例的情况下直接通过类名调用。
@staticmethod 的主要作用是将函数与类相关联,但不需要访问类的实例或类的任何属性。这意味着静态方法不依赖于类的状态,也不对类的任何属性进行修改。静态方法通常用来实现与类相关的实用函数或辅助方法。
下面是一个例子,展示了如何使用 @staticmethod 装饰器定义一个静态方法:
class MathUtils:
@staticmethod
def add(x, y):
return x + y
result = MathUtils.add(3, 5)
print(result) # 输出: 8
在这个例子中,我们定义了一个 MathUtils 类,并使用 @staticmethod 装饰器定义了一个 add 静态方法。我们可以直接通过类名调用这个方法,而不需要创建 MathUtils 的实例。
常见的 @staticmethod 使用错误包括:
- 忘记使用 @staticmethod 装饰器:如果忘记使用 @staticmethod 装饰器定义静态方法,则方法会被视为实例方法,需要使用实例调用。例如:
class MathUtils:
def add(x, y): # 缺少 @staticmethod 装饰器
return x + y
result = MathUtils.add(3, 5) # 错误: 需要使用实例调用
- 错误地传递 self 或 cls 参数:由于静态方法不需要访问实例或类的属性,不需要传递 self 或 cls 参数。如果错误地传递了这些参数,则会引发错误。例如:
class MathUtils:
@staticmethod
def add(self, x, y): # 错误: 不需要 self 参数
return x + y
result = MathUtils.add(3, 5) # 错误: 多余的参数
总结:@staticmethod 装饰器用于定义类方法,可以在不创建类实例的情况下直接通过类名调用。它主要用于实现与类相关的实用函数或辅助方法,并不需要访问类的实例或类的任何属性。常见的错误包括忘记使用装饰器和错误地传递 self 或 cls 参数。
二、@classmethod装饰器简介
在Python中,@classmethod是一个装饰器,它用于定义一个类方法。类方法是属于整个类而不是实例的方法。类方法可以通过类本身被调用,也可以通过实例被调用。
@classmethod装饰器的作用是将一个普通方法转换为类方法,并且将类本身作为第一个参数传递给该方法。通过这个特性,类方法可以在不创建实例的情况下访问类的属性和调用类的其他方法。
下面是一个示例,详细介绍了@classmethod的用法和常见错误:
class MyClass:
count = 0
def __init__(self, name):
self.name = name
MyClass.count += 1
@classmethod
def get_count(cls):
return cls.count
def instance_method(self):
return self.name
# 类方法可以通过类本身调用
print(MyClass.get_count()) # 输出:0
# 类方法可以通过实例调用
instance = MyClass("example")
print(instance.get_count()) # 输出:1
# 类方法可以访问类的属性
print(MyClass.count) # 输出:1
# 类方法可以调用类的其他方法
print(instance.instance_method()) # 输出:example
常见错误:
- 忘记给类方法加上@classmethod装饰器:如果忘记给方法加上@classmethod装饰器,那么这个方法就不会被转换为类方法,无法直接通过类或实例调用。
class MyClass:
count = 0
def get_count(cls): # 没有@classmethod装饰器
return cls.count
print(MyClass.get_count()) # 报错:TypeError: get_count() missing 1 required positional argument: 'cls'
- 忘记传递cls参数:在类方法的定义中,必须显式地将类本身作为第一个参数传递给方法。如果忘记传递cls参数,那么在方法内部无法访问类的属性和调用类的其他方法。
class MyClass:
count = 0
@classmethod
def get_count(): # 没有传递cls参数
return cls.count
print(MyClass.get_count()) # 报错:TypeError: get_count() takes 0 positional arguments but 1 was given
三、它们的区别
@staticmethod和@classmethod是Python中两种装饰器,用于定义类内的静态方法和类方法。它们有以下相同和不同之处:
相同之处:
- 都是Python中用于修饰方法的装饰器。
- 都可以直接通过类名调用,无需实例化对象。
不同之处:
- @staticmethod修饰的方法是一个静态方法,该方法与类无关,只是为了方便组织代码而将其放在类中。静态方法与实例无关,因此无法访问实例变量,也无法修改实例变量。静态方法可以通过类名直接调用,无需实例化对象。
- @classmethod修饰的方法是一个类方法,该方法与类有关,可以访问类变量,并且可以通过cls参数访问类本身。类方法可以通过类名或者实例名调用,会自动传递类对象作为第一个参数。
示例代码如下:
class MyClass:
class_variable = "Hello" # 类变量
@staticmethod
def static_method():
print("This is a static method")
@classmethod
def class_method(cls):
print("This is a class method")
print("Class variable:", cls.class_variable)
# 静态方法调用
MyClass.static_method() # 输出:This is a static method
# 类方法调用
MyClass.class_method() # 输出:This is a class method \n Class variable: Hello
# 通过实例调用类方法
obj = MyClass()
obj.class_method() # 输出:This is a class method \n Class variable: Hello
在上述代码中,静态方法可以直接通过类名调用,而类方法可以通过类名或者实例名调用。
四、python 类的方法参数中 cls和self的区别
细心的读者应该能发现在上面使用的参数是cls,例如:
@classmethod
def class_method(cls):
print("This is a class method")
在Python类的方法中,cls和self都是特殊的参数。
-
self是一个约定俗成的参数名,表示对象本身,类的实例方法中必须包含该参数。通过self,可以访问对象的属性和方法。当调用实例方法时,self参数会被自动传入。
-
cls是一个约定俗成的参数名,表示类本身,类方法中必须包含该参数。通过cls,可以访问类的属性和调用类的其他方法。当调用类方法时,cls参数会被自动传入。
需要注意的是,self和cls并不是关键字,只是约定俗成的参数名,你也可以使用其他的参数名,但最好遵循约定以保持代码的可读性。
总结
看代码的时候还是不要囫囵吞枣,看细一点,感觉python不像别的语言那么随意,它的很多细节都是不可改变的,例如本文的 cls和self 如果混用就会报错,它的 @staticmethod和@classmethod 虽然很相似,但使用上也有很大区别的。