Python中 staticmethod 和 classmethod 的区别

Python中 staticmethod 和 classmethod 的区别

在Python 中,有 @staticmethod、@classmethod装饰器和self、cls参数。初学时,确实不容易搞清楚。
Python中3种方式定义类方法:1、常规方式;2、@classmethod修饰方式;3、@staticmethod修饰方式。
其中:@staticmethod:静态方法装饰器;@classmethod:类方法装饰器。

常规类方法需要通过self参数隐式的传递当前类对象的实例。
@staticmethod修饰的方法定义与普通函数是一样的。
@classmethod修饰的方法class_foo()需要通过cls参数传递当前类对象。

self和cls的区别不是强制的,只是PEP8中一种编程风格,slef通常用作实例方法的第一参数,cls通常用作类方法的第一参数。即通常用self来传递当前类对象的实例,cls传递当前类对象。

一、定义的方法

class A(object):
    def foo(self, x):
        print("执行常规类 foo(%s,%s)" % (self, x))
        
    @classmethod
    def class_foo(cls, x):
        print("执行@classmethod修饰类 class_foo(%s,%s)" % (cls, x))
        
    @staticmethod
    def static_foo(x):
        print("执行@staicmethod修饰类 static_foo(%s)" % x)

mycls = A()
mycls.foo('5')
mycls.class_foo('5')
mycls.static_foo('5')

输出:
执行常规类 foo(<main.A object at 0x0000023CF16201D0>,5)
执行@classmethod修饰类 class_foo(<class ‘main.A’>,5)
执行@staicmethod修饰类 static_foo(5)

实际项目中的应用场景

二、常规类调用(代码续上)

mycs.foo(1)    

#输出: 执行常规类 foo(<main.A object at 0x0000023CF16201D0>,1)

如果采用类对像A直接调用会出错。如下

A.foo(1)

输出:
Traceback (most recent call last):
File “”, line 1, in
TypeError: foo() missing 1 required positional argument: ‘x’

但foo可以采用下面的调用方式,显式传递实例参数a。

A.foo(mycls, 1)

输出:执行常规类 foo(<main.A object at 0x000001FE36E31050>,1)

三、静态方法调用:
要调用一个静态方法,一般使用形式是:「 类名.方法名() 」,可以采用「 实例名.方法名() 」

A.static_foo(5)   #类名.方法名

输出:执行@staicmethod修饰类 static_foo(5)

mycls.static_foo(5)   #实例名.方法名

输出:执行@staicmethod修饰类 static_foo(5)

通常不建议使用“实例名.方法名”这种方式。

四、类静态变量的调用方法 :「 类名.变量名 」,静态方法内部引用其它静态方法时,也是:「 类名.变量名 」

class Test(object):
    name = "Python演示"  # 静态变量(类变量)

    @staticmethod   # 第1个静态方法
    def foo_staticmethod1():
        print(Test.name + '静态方法1')   # 引用静态变量

    @staticmethod   # 第2个静态方法
    def foo_staticmethod2():
        print(Test.name + '静态方法2')   # 引用静态变量
        print('静态方法2调用',end='')
        print(Test.foo_staticmethod1())

#输出:Python演示静态方法1
Test.foo_staticmethod1()

#输出:
#Python演示静态方法2
#静态方法2调用Python演示静态方法1
Test.foo_staticmethod2()

print(Test.name)  #输出:Python演示

五、静态方法内部调用普通方法,访问实例属性:普通方法和实例属性都必须通过实例对象去引用,不能直接使用类名去访问

class Test(object):
    def __init__(self):
        self.desc = "实例属性,只能通过实例对象调用"

    def norm_method(self):
        #普通方法
        print('普通方法被调用!')

    @staticmethod
    def foo_staticmethod():
        #静态方法,实例
        instance = Test()

        # 获取实例属性
        print(instance.desc)

        # 调用普通方法
        instance.norm_method()

Test.foo_staticmethod()

输出如下:
实例属性,只能通过实例对象调用
普通方法被调用!

五、类方法
@classmethod
装饰器 @classmethod 修饰的方法称为类方法。在使用的时候,会将其自身作为第一个参数 cls 传递给类方法本身。

1、要调用一个类方法,一般使用形式是:「 类名.方法名() 」

class Test(object):
    @classmethod
    def foo_cls(cls) :
        print('测试')

if __name__ == '__main__' :
    Test.foo_cls()  #k输出:测试

和静态方法类似,也可以实例化一个类对象,通过这个对象去调用类方法,但是不建议使用这种方式。

2、调用静态变量

静态方法内部引用静态变量有两种方式,分别是:
「 类名.变量名 」
「 cls.变量名 」
需要说明的是,由于 cls 就是代表外层类本身,所以上面这两种方式等效。

class Test(object):
    # 静态变量(类变量)
    name = "类方法测试"

    # 类方法,第一个参数为cls,代表类本身
    @classmethod
    def foo_classmethod(cls):
        # 调用静态变量方式一
        print('参数CLS调用' + cls.name)

        # 调用静态变量方式二
        print('类名调用' + Test.name)

Test.foo_classmethod()

输出:
参数CLS调用类方法测试
类名调用类方法测试

4、类方法内部调用普通方法,访问实例属性
需要通过 cls 变量实例化一个类对象,然后通过这个对象去调用普通方法和实例属性。

class Test(object):

    def __init__(self):
        self.val = '这是一个类属性'

    def norm_method(self) :   # 普通方法
        print('调用普通方法。')

    @classmethod   #用@classmethod装饰器修改类方法
    def foo_cls(cls) :
        p = cls()  # 实例化
        # 第一种调用方法
        print(p.val)
        p.norm_method()
        # 第二种调用方法
        print(cls().val)
        cls().norm_method()

Test.foo_cls()

输出:
这是一个类属性
调用普通方法 。
这是一个类属性
调用普通方法 。

六、区别
普通方法、静态方法、类方法的区别
普通方法:第一个参数 self 代表实例对象本身,可以使用 self 直接引用定义的实例属性和普通方法;如果需要调用静态方法和类方法,通过「 类名.方法名() 」调用即可。

静态方法:使用「 类名.静态变量 」引用静态变量,利用「 类名.方法名() 」调用其他静态方法和类方法;如果需要调用普通方法,需要先实例化一个对象,然后利用对象去调用普通方法。

类方法:第一个参数 cls 代表类本身(等价),通过「 cls.静态变量 」或「 类名.静态变量 」引用静态变量,利用「 cls.方法名() 」或「 类名.方法名() 」去调用静态方法和类方法;如果需要调用普通方法,需要先实例化一个对象,然后利用对象去调用普通方法。

静态方法和类方法是针对类定义的,除了可以使用类名去调用,也可以使用实例对象去调用,但是不建议使用实例对象调用。

5.最后
一般来说,如果方法内部涉及到实例对象属性的操作,建议用普通方法;如果方法内部没有操作实例属性的操作,仅仅包含一些工具性的操作,建议使用静态方法;而如果需要对类属性,即静态变量进行限制性操作,则建议使用类方法。

参考文章:
1、https://cloud.tencent.com/developer/article/1692519,作者AirPython
2、https://www.cnblogs.com/elie/p/5876210.html,作者:Eliefly

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值