浅谈python中的@staticmethod和@classmethod
本篇我会针对staticmethod
和classmethod
这两个方法进行一个讲解,希望对每位朋友有帮助
在了解这两个方法之前我们先来回顾一下,在我们平时调用类里面的方法的时候是不是每次都进行了实例化,然后用实例化.方法进行调用的,但是这两个方法却不用进行实例化进行调用,当然了你进行实例化调用也是没有问题的,那这种怎么实现呢?很简单只要在这个方法前面加上@staticmethod
或@classmethod
就可以了,是不是感觉很神奇呢,并且这样子还有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
两种方法的区别
既然@staticmethod
和@classmethod
都可以直接类名.方法名()来调用,那他们之间又有什么区别呢?别急,看下面
- 首先
staticmethod
(静态方法),需要一个装饰器语法(@staticmethod
)将一个普通方法转换为静态方法classmethod
(类方法),需要一个装饰器语法(@classmethod
)将一个普通方法转换为类方法
- 然后
@staticmethod
不需要表示自身对象的self
和自身类的cls
参数,就跟使用函数一样。@classmethod
也不需要self
参数,但第一个参数需要是表示自身类的cls
参数。
- 最后
@staticmethod
没有表示自身对象的self
参数和自身类的cls
参数,所以如果在@staticmethod
中要调用到这个类的一些属性方法,如果我们使用实例化对象.方法名的时候,实例被忽略而类被使用,所以实际上还是类调用。- 而
@classmethod
因为有cls
参数,当然是可以实例化对象.方法名,来避免硬编码。
看了上面的解释我相信每个人对这两个方法有了一定的认识了,接下来我会用代码来重点分析下这两个方法的基本用法,以便于有一个直观的了解
staticmethod
class Person:
def __init__(self):
pass
def eat(self):
print('没有@staticmethod实例调用函数实例对象默认传入作为第一个参数')
p = Person()
p.eat() # 没有@staticmethod实例调用函数实例对象默认传入作为第一个参数
上面是我们平时写的类方法的调用,这样子写的时候这个方法必须加上self
参数,然后实例化对象自动作为第一个参数传入
class Person:
def __init__(self):
pass
@staticmethod
def eat():
print('有@staticmethod实例调用函数实例不传入做参数')
p = Person()
p.eat() # 有@staticmethod实例调用函数实例不传入做参数
Person.eat() # 有@staticmethod实例调用函数实例不传入做参数
当加上这个方法的时候,注意我们用了两种方法调用,一种实例化.方法来进行调用,他不会将实例传入作为参数,所以本质还是类的调用,一种是类.方法的调用
classmethod
class C:
def __init__(self, i):
print('__init__: %d' % i)
@classmethod
def f(cls, name):
print(name)
return C(int(name))
a = C(1)
# __init__: 1
b = C.f('10')
# 10
# __init__: 10
c = C(2).f('100')
# __init__: 2
# 100
# __init__: 100
观察一下发现这种可以直接通过类.方法来调用,也可以通实例.方法来调用
class C:
def __init__(self, i):
print('__init__: %d' % i)
@classmethod
def f(cls, name):
print(name)
return C(int(name))
class D(C):
pass
D.f('20')
# 20
# __init__: 20
以上例子说明了在继承中这种方法也同样适用于继承类
使用案例
说了这么多基本用法是会了,那么它到底有什么实际作用呢,我在下面列举了几个:
-
在很多情况下,一些函数与类相关,但不需要任何类或实例变量就可以实现一些功能。比如设置环境变量,修改另一个类的属性等等。假如我们想仅实现类之间的交互而不是通过实例,我们可以在类之外建立一个简单的函数来实现这个功能,但是这会使代码扩散到类之外,可能对未来代码维护产生问题。
-
classmethod
尤其适合用在当我们需要在创建真正的类实例之前做一些预设置的情况,因为实例建立之前显然你是不能使用实例方法的,你只能使用classmethod
.这样做的另一个好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符@classmethod
就可以了。相当于我们拥有了多样化的构造函数。 -
Python使用类时,通常需要为对象初始化绑定方法,绑定方法同样是对象,但是创建他们需要成本,而
staticmethod
就可以避免这些。 -
classmethod
因为有cls
参数,可以调用类的属性,类的方法等,避免了使用类名硬编码。classmethod
既具有普通实例方法访问类名称空间的能力,又具有静态方法随意使用的灵活性。 -
实例属性由实例更改,不会影响类属性。而类属性则可以由类方法
(classmethod)
来更改。
看下下面的一些例子:
@classmethod:
def get_student_num(cls_name):
return cls_name.num
class Student:
num = 0
def __init__(self):
Student.num += 1
s1 = Student()
s2 = Student()
print(get_student_num(Student)) # 2
我们定义了一个类属性,然后在最外面定义了一个函数实现了修改类属性,但是这种在外面定义一个函数是非常不利于维护的,看一下下面修改的例子
class Student:
num = 0
def __init__(self):
Student.num += 1
@classmethod
def get_student_num(cls):
return cls.num
s1 = Student()
s2 = Student()
print(Student.get_student_num()) # 2
从这个修改可以看出我们用了类方法,实现了修改类属性,这样就比上面整洁多了,在类里面也好维护多了
@staticmethod
def get_student_num():
return 1
class Student:
def get_student(self):
if get_student_num() == 1:
return 'zhangsan'
s1 = Student()
print(s1.get_student())
上面我们在外面定义了一个方法,然后类里面调用了这个方法
class Student:
@staticmethod
def get_student_num():
return 1
def get_student(self):
if self.get_student_num() == 1:
return 'zhangsan'
s1 = Student()
print(s1.get_student())
这次我们用静态方法定义了一个方法,这个方法跟类有关系但在运行时又不需要实例和类参与,所以这种情况下我们可以这样使用
以上就是所有关于这两个方法的讲解,希望大家积极补充