当前位置: 首页 > news >正文

一文搞懂 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)

相关文章:

  • 跨境电商平台有哪些?列举5个/seo信息网
  • 游戏网站开发计划书案例目录/关键词优化方法
  • 怎么修改网站上传附件大小/大连中小企业网络营销
  • wordpress的注入/李飞seo
  • 网站建设做微营销/seo技术优化技巧
  • 网站建设需求文档/可以免费打广告的网站
  • Redis缓存和数据库一致性
  • JavaScript - 代理与反射(代理基础)
  • 消息批处理端口说明
  • Win10注册表损坏进不了系统怎么U盘重装系统?
  • python学习笔记---面向对象编程【廖雪峰】
  • QMAKE_POST_LINK QMAKE_PRE_LINK解释
  • 物流企业如何确保网络安全?
  • 【工具使用】Kvaser CANKing的使用
  • QEMU零知识学习5 —— QEMU安装
  • pom.xml文件详解
  • python学习笔记---高级特性【廖雪峰】
  • [激光原理与应用-65]:激光器-器件 - 多模光纤(宽频光纤)、单模光纤的原理与区别