zoukankan      html  css  js  c++  java
  • python @classmethod 和 @staticmethod区别,以及类中方法参数cls和self的区别

    staticmethod

    首先来看@staticmethod,这个装饰器很好理解,就是让类中的方法变成一个普通的函数(因为是普通函数,并没有绑定在任何一个特定的类或者实例上。所以与不需要对象实例化就可以直接调用)。可以使用类或者类的实例调用,并且没有任何隐含参数的传入,所以不需要self(参数名是随便定的)。

    >>> class C(object):
    ...     @staticmethod
    ...     def add(a,b):
    ...             return a+b
    ...     def get_weight(self):
    ...             return self.add(1,2)
    ... 
    >>> C.add
    <function add at 0x1d32668>
    >>> C().add
    <function add at 0x1d32668>
    >>> C.get_weight
    <unbound method C.get_weight>

    总结:

    1、当一个函数逻辑上属于一个类又不依赖与类的属性的时候,可以使用 @staticmethod。

    2、使用 @staticmethod 可以避免每次使用的时都会创建一个对象的开销。

    3、@staticmethod 可以使用类和类的实例调用。但是不依赖于类和类的实例的状态。

    classmethod

    再看@classmethod,我们对比下加与不加装饰前后函数

    不加装饰前

    >>> class C(object):
    ...     weight = 12
    ...     def get_weight(self):
    ...             print self
    ... 
    >>> C.get_weight
    <unbound method C.get_weight>
    >>> C().get_weight
    <bound method C.get_weight of <__main__.C object at 0x25d7b10>>
    >>> C().get_weight()
    <__main__.C object at 0x25d7a50>
    >>> myc = C()
    >>> myc.get_weight
    <bound method C.get_weight of <__main__.C object at 0x25d7a50>>
    >>> myc.get_weight()
    <__main__.C object at 0x25d7a50>

    通过例子知道,C().get_weight和myc.get_weight都是绑定在对象C的一个实例上的。(e.g. <bound method C.get_weight of <__main__.C object at 0x25d7b10>>)

    顺便通过下面的代码,看下get_weight的self到底接收的是什么:

    #继续上面的代码
    >>> C.get_weight() #异常报错 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method get_weight() must be called with C instance as first argument (got nothing instead) >>> C.get_weight(C()) #将C的一个实例显示传递给self <__main__.C object at 0x25d7b50> >>> myc.get_weight() #myc.get_weight()隐藏了第一个参数myc <__main__.C object at 0x25d7a50> >>>

    调用的实例隐藏的作为一个参数self传递过去了,self 只是一个普通的参数名称(参数名是随便定的),不是关键字。

    加@classmethod装饰后

    >>> class C(object):
    ...     weight = 12
    ...     @classmethod
    ...     def get_weight(cls):
    ...             print cls
    ... 
    >>> C.get_weight
    <bound method type.get_weight of <class '__main__.C'>>
    >>> C().get_weight
    <bound method type.get_weight of <class '__main__.C'>>
    >>> myc = C()
    >>> myc.get_weight
    <bound method type.get_weight of <class '__main__.C'>>
    >>> myc.get_weight()
    <class '__main__.C'>
    >>> C.get_weight()
    <class '__main__.C'>

    可以看出,C类和C类的实例都能调用 get_weight 而且调用结果完全一样。 我们看到 weight 是属于 C类的属性,当然也是C的实例的属性(元对象__get__机制)。

    再看一下get_weight的参数cls到底接收的是什么,可以看到C.get_weight()和myc.get_weight()接收的都是C类(e.g. <class '__main__.C'> )而不是C类的实例,cls只是一个普通的函数参数,调用时隐含的传递过去。

    总结:

    1、classmethod 是类对象与函数的结合。

    2、可以使用类和类的实例调用,但是都是将类作为隐含参数传递过去。

    3、使用类来调用 classmethod 可以避免将类实例化的开销。

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

    补充:

    @staticmethod 装饰器会让 foo 的 __get__ 返回一个函数,而不是一个方法。看下面的例子

    >>> class C(object):
    ...     def foo(self):
    ...         pass
    ...
    >>> C.foo
    <unbound method C.foo>
    >>> C().foo
    <bound method C.foo of <__main__.C object at 0xb76ddcac>>
    >>>

    所谓 bound method ,就是方法对象的第一个函数参数绑定为了这个类的实例(所谓 bind )。这也是那个 self 的由来。

    那,我们加入@staticmethod之后:

    >>> class C(object):
    ...     @staticmethod
    ...     def foo():
    ...         pass
    ...
    >>> C.foo
    <function foo at 0xb76d056c>
    >>> C.__dict__['foo'].__get__(None, C)
    <function foo at 0xb76d056c>

    装饰器会让 foo 的 __get__ 返回一个函数,而不是一个方法。

    参考资料:

    Stackoverflow上的Python问题精选 http://pyzh.readthedocs.org/en/latest/python-questions-on-stackoverflow.html#id14

    Python中的method http://blog.jobbole.com/53989/

    python中的元类编程 http://www.ibm.com/developerworks/cn/linux/l-pymeta/index.html

  • 相关阅读:
    Python-zip压缩-解压
    使用zabbix监控nginx
    原已经安装好的nginx,现在需要添加一个未被编译安装的模块:
    zabbix 邮件告警配置
    lnmp-zabbix
    Mysql新建用户和数据库并授权
    git标签管理
    Ubuntu下安装tomcat
    git分支管理
    Java拦截器
  • 原文地址:https://www.cnblogs.com/kennyhr/p/3935465.html
Copyright © 2011-2022 走看看