一文搞懂 python 中的 classmethod、staticmethod和普通的实例方法的使用场景

什么是类方法(classmethod)/静态方法(staticmethod)和普通成员方法?

首先看这样一个例子:

class A(object):
    def m1(self, n):
        # 属于实例对象,self 指代实例对象,
        print("self:", self)
       
    @classmethod
    def m2(cls, n):
        # 属于类对象A, cls指代类对象,就打印啥对象
        print("cls", cls)

    @staticmethod
    def m3(n):
        pass

    a = A()
    a.m1(1) # self: <__main__.A object at 0x7f78343e0290>
    A.m2(1) # self: <class '__main__.A'>
    A.m3(1) 

需要搞清楚 类对象cls和实例对象self

类对象和实例对象

三类方法(m1/m2/m3)和两类对象(A/a)的隶属关系

A.m1(A, 1) # self: <class '__main__.A'>
A.m1(a,1) # self: <__main__.A object at 0x7f78343e0290>
A.m1(int, 1) # self: <class 'int'>
a.m1(1) # self: <__main__.A object at 0x7f78343e0290>

小结:普通成员方法 (m1)隶属于类对象A和实例对象a, 常规用法是 a.m1()

A.m2(1) # cls:  <class '__main__.A'>
a.m2(1) # cls:  <class '__main__.A'>

小结: 类方法(m2) 隶属于类对象A, 实例对象a 因为是类对象A的实例化,所以通过 a 也能顺藤摸瓜地找到A,进而把cls 绑定到 类对象。

print(A.m3) # <function A.m3 at 0x7f2f7c1c8320>
print(a.m3) # <function A.m3 at 0x7f2f7c1c8320>

小结: 静态方法不和类对象A绑定,也不和实例对象a绑定,相当于普通方法,只不过是恰巧在类里面而已,通过类对象、实例对象都可以调用。

使用场景

静态方法 @staticmethod

class SignatureHandler(tornado.web.RequestHandler):
    def get(self):
        """
         根据签名判断请求是否来自微信
        """
        signature = self.get_query_argument("signature", None)
        echostr = self.get_query_argument("echostr", None)
        timestamp = self.get_query_argument("timestamp", None)
        nonce = self.get_query_argument("nonce", None)
        if self._check_sign(TOKEN, timestamp, nonce, signature):
            logger.info("微信签名校验成功")
            self.write(echostr)
        else:
            self.write("你不是微信发过来的请求")

    @staticmethod
    def _check_sign(token, timestamp, nonce, signature):
        sign = [token, timestamp, nonce]
        sign.sort()
        sign = "".join(sign)
        sign = sha1(sign).hexdigest()
        return sign == signature

_check_sign 中不需要调用 任何的类成员方法和成员属性,等价于普通函数放在了 类中。
小结: 如果方法 m3 的定义不需要调用 类的成员方法和成员变量,则 m3 可以 设计为 静态方法
疑问:静态方法的必要性?
静态方法和 实例对象无关, 和 类对象有关。比如
我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题,代码如下所示。

from math import sqrt


class Triangle(object):

    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

    def perimeter(self):
        return self._a + self._b + self._c

    def area(self):
        half = self.perimeter() / 2
        return sqrt(half * (half - self._a) *
                    (half - self._b) * (half - self._c))


def main():
    a, b, c = 3, 4, 5
    # 静态方法和类方法都是通过给类发消息来调用的
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
        # print(Triangle.perimeter(t))
        print(t.area())
        # print(Triangle.area(t))
    else:
        print('无法构成三角形.')


if __name__ == '__main__':
    main()

类方法 @classmethod

场景一:模拟java 定义多个构造函数

python中 定义 class 时,只能有一个 初始化方法,不能按照不同情况初始化类。可以借助 class 方法来实现这个需求。

# coding:utf-8


class Book(object):

    def __init__(self, title):
        self.title = title

    @classmethod
    def create(cls, title):
        book = cls(title=title)
        return book

book1 = Book("python")
book2 = Book.create("python and django")
print(book1.title)
print(book2.title)

```py
#### 场景二:类A中某方法 mx()调用了静态方法 m3,则 mx最好定义成 类方法  
```py
# coding:utf-8


class Foo(object):
    X = 1
    Y = 2

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / len(mixes)

    @staticmethod
    def static_method():
        return Foo.averag(Foo.X, Foo.Y)

    @classmethod
    def class_method(cls):
        return cls.averag(cls.X, cls.Y)

foo = Foo()
print(foo.static_method())
print(foo.class_method())

场景二中 推荐用 classmethod 而不是 staticmethod的原因:考虑继承的情况
# coding:utf-8


class Foo(object):
    X = 1
    Y = 2

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / len(mixes)

    @staticmethod
    def static_method():
        return Foo.averag(Foo.X, Foo.Y)

    @classmethod
    def class_method(cls):
        return cls.averag(cls.X, cls.Y)


class Son(Foo):
    X = 3
    Y = 5

    @staticmethod
    def averag(*mixes):
        return sum(mixes) / 3

p = Son()
print(p.static_method())
print(p.class_method())
# 1.5
# 2.6666666666666665


小结: 条件:static_method 和 class_method 都有调用 静态方法
average. 结论:经子类Son继承后, 调用class_method, 其内部 调用的是子类Son的属性和方法;调用 static_method ,其内部依然调用的是父类的属性和方法;因为 static_method 调用静态方法时,指定了具体的类名(例子中是Foo, 而使用class_method 则是 cls)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值