专栏首页JetpropelledSnakePython入门之@classmethod与@staticmethod

Python入门之@classmethod与@staticmethod

本章内容:

    1. @classmethond

    2. @staticmethod

========================================

  Python面向对象编程中,类中定义的方法可以是 @classmethod 装饰的 类方法 ,也可以是 @staticmethod 装饰的 静态方法 ,用的最多的还是不带装饰器的 实例方法 ,如果把这几个方法放一块,对初学者来说无疑是一头雾水,那我们该如何正确地使用它们呢?

先来个总结:

定义:

绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    1. 绑定到类的方法:用classmethod装饰器装饰的方法。

                为类量身定制

                类.boud_method(),自动将类当作第一个参数传入

              (其实对象也可调用,但仍将类当作第一个参数传入)

    2. 绑定到对象的方法:没有被任何装饰器装饰的方法。

               为对象量身定制

               对象.boud_method(),自动将对象当作第一个参数传入

             (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

非绑定方法:用staticmethod装饰器装饰的方法

         不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

    注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

  再来看一个简单示例:

class A(object):
    def m1(self,n):
        print('self:', self)
    
    @classmethod
    def m2(cls,n):
        print('cls':cls)

    @staticmethod
    def m3(n):
        pass

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

  在类中一共定义了3个方法,

    m1 是实例方法,第一个参数必须是 self(约定俗成的);

    m2 是类方法,第一个参数必须是cls(同样是约定俗成);

    m3 是静态方法,参数根据业务需求定,可有可无。

  当程序运行时,大概发生了这么几件事(结合下面的图来看)。

    第一步:代码从第一行开始执行class命令,此时会创建一个类A对象(没错,类也是对象,一切皆对象嘛)同时初始化类里面的属性和方法,记住,此刻实例对象还没创建出来。

    第二步、三步:接着执行a = A(),系统自动调用类的构造器,构造出实例对象a。

    第四步:接着调用a.m1(), m1是实例方法,内部会自动把实例对象a传递给self参数进行绑定;也就是说, self 和 a 指向的都是同一个实例对象。 

    第五步:调用A.m2(1),Python内部隐式地把类对象传递给cls参数,cls和A都指向类对象。

  严格意义上来说,左边的都是变量名,是对象的引用,右边才是真正的对象。为了描述方便,我直接把 a 称为对象,你应该明白我说对象其实是它所引用右边的那个真正的对象。

  再来看看每个方法各有什么特性:

    实例方法 :

      print(A.m1) 

print(A.m1)>>:
# A.m1在py2中显示为<unbound method A.m1> 
<function A.m1 at 0x000002BF7FF9A488> 

print(a.m1)>>:
<bound method A.m1 of <__main__.A object at 0x000002BF7FFA2BE0>> 

A.m1是一个还没有绑定实例对象的方法,对于未绑定方法,调用 A.m1 时必须显示地传入一个实例对象进去,而 a.m1是已经绑定了实例的方法,python隐式地把对象传递给了self参数,所以不再手动传递参数,这是调用实例方法的过程。

A.m1(a, 1)  == a.m1(1) 
#上面的等式左右两边的意义是相同的

如果未绑定的方法 A.m1 不传实例对象给 self 时,就会报参数缺失错误,在 py3 与 py2 中,两者报的错误不一致,python2 要求第一个参数self是实例对象,而python3中可以是任意对象。

A.m1() >>:
TypeError: m1() missing 1 required positional argument: 'n' 
#类方法 

print(A.m2) >>:
<bound method A.m2 of <class '__main__.A'>> 
print(a.m2) >>:
<bound method A.m2 of <class '__main__.A'>> 
#加了@classmethod,都自动绑定了类对象A的方法

m2是类方法,不管是 A.m2 还是 a.m2,都是已经自动绑定了类对象A的方法,对于后者,因为python可以通过实例对象a找到它所属的类是A,找到A之后自动绑定到 cls。

A.m2(1)  == a.m2(1) 
# 上面的等式左右两边的意义是相同的

这使得我们可以在实例方法中通过使用 self.m2()这种方式来调用类方法和静态方法。

def m1(self, n): 
    print("self:", self) 
    self.m2(n) 

@staticmethod静态方法 

print(A.m3) >>:
<function A.m3 at 0x000002BF7FF9A840> 
print(a.m3) >>:
<function A.m3 at 0x000002BF7FF9A840> 

  m3是类里面的一个静态方法,跟普通函数没什么区别,与类和实例都没有所谓的绑定关系,它只不过是碰巧存在类中的一个函数而已。不论是通过类还是实例都可以引用该方法。

A.m3(1) == a.m3(1) 
# 以上等式左右两边的意义一样

静态方法的使用场景:      如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务。例如下面是微信公众号开发中验证微信签名的一个例子,它没有引用任何类或者实例相关的属性和方法。

from hashlib import sha1 
import tornado.web 
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  

  类方法的使用场景有:

    作为工厂方法创建实例对象,例如内置模块 datetime.date 类中就有大量使用类方法作为工厂方法,以此来创建date对象。

class date: 
    def __new__(cls, year, month=None, day=None): 
        self = object.__new__(cls) 
        self._year = year 
        self._month = month 
        self._day = day 
    return self 

    @classmethod 
    def fromtimestamp(cls, t): 
        y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t) 
    return cls(y, m, d) 
    @classmethod 
        def today(cls): 
        t = _time.time() 
    return cls.fromtimestamp(t)             

  如果希望在方法里面调用静态类,那么把方法定义成类方法是合适的,因为要是定义成静态方法,那么你就要显示地引用类A,这对继承来说可不是一件好事情。

class A: 

    @staticmethod 
    def m1() 
        pass 

    @staticmethod 
    def m2(): 
       A.m1()  # bad 

    @classmethod 
    def m3(cls): 
        cls.m1()  # good                

本篇参考:Python之禅公共号文章

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python面试题目之Python函数默认参数陷阱

    python中,万物皆对象。python中不存在所谓的传值调用,一切传递的都是对象的引用,也可以认为是传址。

    Jetpropelledsnake21
  • Python面试题之Python面向对象编程汇总

    面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Clas...

    Jetpropelledsnake21
  • Python入门之面向对象编程(三)Python类的魔术方法

    python类中有一些方法前后都有两个下划线,这类函数统称为魔术方法。这些方法有特殊的用途,有的不需要我们自己定义,有的则通过一些简单的定义可以实现比较神奇的功...

    Jetpropelledsnake21
  • python 单例模式

    class Singleton(type): def init(self, *args, *kwargs): self.__instance = None ...

    用户5760343
  • 大象与犀牛的新卫士:人工智能与无人机

    据外媒报道,波士顿深度学习神经网络公司Neurala和林德伯格基金会(Lindbergh Foundation)的空中牧羊人(Air Shepherd)宣布将开...

    人工智能快报
  • 第六篇 爬虫技术之天天基金网(3) 实战篇

    hello,大家好,今天我们来用代码实现一下节前分析的那篇如何抓取基金排名的数据:

    还是牛6504957
  • 信息收集工具exiftool获取图片中的exif信息

    EXIF信息,是可交换图像文件的缩写,是专门为数码相机的照片设定的,可以记录数码照片的属性信息和拍摄数据。EXIF可以附加于JPEG、TIFF、RIFF等文件之...

    bboysoul
  • 应用是微软在云计算大战中最重要的武器

    10月23日消息,据国外媒体报道,能够成就或毁灭微软云计算业务的不是平台规模,而是应用。但这并不是说规模不重要。微软在全球有19个数据中心,每年在数据中心基础架...

    静一
  • AWK 深入浅出教程

    ---- 概述 awk是一门解释性文本处理语言,它在文本处理领域中非常强大和方便。awk有三个主要的类型是: AWK - 原先来源于 AT & T 实验室的的A...

    BrianLv
  • 面试题六期-中高级测试工程师基础知识必备之selenium篇

    引言:自动化永远是避不开的,反正你入职的岗位要不要用自动化,你必须得会一点,加分项。这一块包括,自动化一些理念和自动化的工具使用。有同学说问题太多难消化,那就每...

    测试小兵

扫码关注云+社区

领取腾讯云代金券