美文网首页程序园
一个Java 程序员的python学习之路8- Class方法和

一个Java 程序员的python学习之路8- Class方法和

作者: 赵阳_c149 | 来源:发表于2019-11-01 10:21 被阅读0次

    python有3种方法:常规,class和static。这让人有些困惑【1】【2】,我们不明白为什么我们要用到他们。甚至有人说我们根本不需要static方法,因此建议不要用它。

    常规方法和class方法需要传入第一个变量。
    常规方法:self
    class方法:cls
    而static不需要类似的变量。

    因此,一个static方法没有self和cls的访问权,他像常规方法一样工作,但是从某种意思上说属于class。通常,static方法使用在类中定义的变量,但是多数情况下,我们想要将static方法放入class的定义中仅仅是因为他同class有逻辑上的联系(松散的耦合)。而且,将他定义在class中有利于对其进行维护。

    方法

    方法就是一个由def语句定义的函数对象。

    方法同简单函数的工作方式相同,但是有一个例外:一个方法的第一个参数必须是接受实例对象:

    1. 简单函数:定义在class的外部,这个函数通过class的实例访问class的属性。
    def outside_foo():
    
    1. 实例方法:
    def foo(self,)
    
    1. class方法:如果我们需要用到class的属性
    @classmethod
    def cfoo(cls,) 
    
    1. static方法:不需要class的任何信息
    @staticmethod
    def sfoo() 
    

    他们相似多于不同。
    大多数情况下,我们可以用简单函数做同样的事情。但是从软件设计
    、代码简洁和效能的角度来说,他们的用法还是有细微的区别的。

    本文主要是讨论class方法和static方法的区别。

    还有另外一种方法,实例方法,但是实例方法的内部工作原理和class方法是相同的。实际上,python会自动的将对实例方法的调用映射到class方法。比方说,对一个实例调用:

    instance.method(args...)
    

    将自动的被转化成对class 方法的调用:

    class.method(instance, args...)
    

    翻译自:
    https://www.bogotobogo.com/python/python_differences_between_static_method_and_class_method_instance_method.php

    Class 方法和实例方法

    假设我们有以下class,包含实例方法foo()

    # a.py
    class A:
       message = "class message"
    
       @classmethod
       def cfoo(cls):
          print(cls.message)
    
       def foo(self, msg):
          self.message = msg
          print(self.message)
    
       def __str__(self):
          return self.message
    

    因为方法foo()是被设计来处理实例的,因此一般是通过class的实例调用方法:

    >>> from a import A
    >>> a = A()
    >>> a.foo('instance call')
    instance call
    

    当我们调用他的时候,python自动的将self替换为实例对象a,然后msg获得了传入的字符串:instance call

    有两种调用方法的方式:

    1. 就像上面那样,通过实例调用。
    2. 通过class名。
    >>> A.foo('class call')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unbound method foo() must be called with A instance as first argument (got str instance instead)
    

    我们得到了error,因为python需要实例的信息,而我们没有提供实例的信息。所以,就像error信息提示的那样,我们得用以下的形式,通过class名字来调用实例方法:

    >>> A.foo(a, 'class call')
    class call
    

    注意到我们将实例a作为一个参数传给了foo()方法。

    通过class和实例的调用效果相同,只要我们传入了相同的实例变量。

    而对于class方法,我们只需要通过class名A调用他。

    >>> A.cfoo()
    class message
    

    注意:

    1. Bound 方法(实例调用):为了调用实例方法,我们必须显式的提供实例对象作为第一个参数。换句话说,一个bound 方法对象会记录self实例和引用的方法。所以,一个bound方法可以在之后没有实例传入的情况下作为一个简单函数调用。python自动将实例和函数打包在了bound方法对象中,所以我们在调用方法的时候,不需要传入一个实例。也就是说,当调用一个bound方法对象的时候,python自动提供一个实例:这个实例被用来创建bound方法对象。这意味这bound方法对象通常是可以和函数对象交互的,而且可以用他们来作为Callback函数之类功能的接口。
    >>> class Callback:
    ...   def __init__(self, color):
    ...     self.color = color
    ...   def changeColor(self):
    ...     print(self.color)
    ... 
    >>> obj = Callback('red')
    >>> cb = obj.changeColor
    >>> cb()
    red
    

    instance.method()的方式返回一个bound方法对象:

    >>> obj.changeColor
    <bound method Callback.changeColor of <__main__.Callback instance at 0x7f95ebe27f80>>
    
    1. Unbound方法(class 调用):以Class.methond()方式可以返回一个unbound方法:
    >>> Callback.changeColor
    <unbound method Callback.changeColor>
    

    为了调用这个方法,必须显式的提供一个实例对象作为第一个参数:

    >>> obj2 = Callback('purple')
    >>> t = Callback.changeColor
    >>> t(obj2)
    purple
    

    这里的t就是一个unbound方法对象(在3.0中就是函数),然后我们传入了实例。
    在python3.0中,unbound 方法的概念被丢弃了,我们在这里讨论的unbound方法在3.0中是被作为简单函数处理的。

    现实世界中的class方法例子

    有时,class方法被用来定义附加的构造函数

    ...
        # Additional constructors
    
        @classmethod
        def fromtimestamp(cls, t):
            "Construct a date from a POSIX timestamp (like time.time())."
            y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
            return cls(y, m, d)
    
        @classmethod
        def today(cls):
            "Construct a date from time.time()."
            t = _time.time()
            return cls.fromtimestamp(t)
    ...
    

    static 方法

    当我们需要处理同class而不是实例关联的数据的时候,static方法就派上用场了。

    static方法从不接受自动传入的self参数,不管是通过class还是实例调用。他们通常保存了跨所有实例的信息,而不是为实例提供行为。

    定义static方法的方式已经有了一些变化,这里不探讨定义static方法在python各版本之间的不同之处,而只是展示了一种典型的定义方法,即用函数decorator(@)修饰static方法:

    class S:
       @staticmethod
       def foo():
          ...
    

    从内部来说,这一定义和name rebinding有相同的作用:

    class S:
       def foo():
          ...
       foo = staticmethod(foo)
    

    假设我们有一个典型的实例计数代码:

    # s.py
    class S:
       nInstances = 0
       def __init__(self):
          S.nInstances = S.nInstances + 1
    
       @staticmethod
       def howManyInstances():
          print('Number of instances created: ', S.nInstances)
    

    之后,我们创建3个实例:

    >>> from s import S
    >>> 
    >>> a = S()
    >>> b = S()
    >>> c = S()
    

    现在,我们就有了一个static方法,可以有两种方式调用他:

    1. 从class调用
    2. 从实例调用
    >>> S.howManyInstances()
    ('Number of instances created: ', 3)
    >>> a.howManyInstances()
    ('Number of instances created: ', 3)
    

    实例、static和class方法

    这里是一个简单的例子,包含了所有类型的方法:

    class Methods:
      def i_method(self,x):
        print(self,x)
    
      def s_method(x):
        print(x)
    
      def c_method(cls,x):
        print(cls,x)
    
      s_method = staticmethod(s_method)
      c_method = classmethod(c_method)
    
    obj = Methods()
    
    obj.i_method(1)
    Methods.i_method(obj, 2)
    
    obj.s_method(3)
    Methods.s_method(4)
    
    obj.c_method(5)
    Methods.c_method(6)
    

    输出【3】:

    (<__main__.Methods instance at 0x7f7052d75950>, 1)
    (<__main__.Methods instance at 0x7f7052d75950>, 2)
    3
    4
    (<class __main__.Methods at 0x7f7052d6d598>, 5)
    (<class __main__.Methods at 0x7f7052d6d598>, 6)
    

    下面的代码的输出是一样的:

    class Methods:
      def i_method(self,x):
        print(self,x)
    
      @staticmethod
      def s_method(x):
        print(x)
    
      @classmethod
      def c_method(cls,x):
        print(cls,x)
    
    obj = Methods()
    
    obj.i_method(1)
    Methods.i_method(obj, 2)
    
    obj.s_method(3)
    Methods.s_method(4)
    
    obj.c_method(5)
    Methods.c_method(6)
    

    翻译自:
    https://www.bogotobogo.com/python/python_differences_between_static_method_and_class_method_instance_method.php

    【1】译者的注释:python的static方法类似java的static方法,而python的class方法在java中没有类似的对应。但是,正像文章中说的那样,“有时,class方法被用来定义附加的构造函数。”,从这个角度来说,python的class方法和java的构造函数类似。
    【2】static方法和class方法是从2.2开始引入的。见《Learning Python,5th Edition》,by Mark Lutz,第32章“Advanced Class Topic”。
    【3】很神奇的行为,当通过实例调用class方法的时候,python自动传入的是class而不是实例!

    相关文章

      网友评论

        本文标题:一个Java 程序员的python学习之路8- Class方法和

        本文链接:https://www.haomeiwen.com/subject/ljutbctx.html