程序园

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

2019-11-01  本文已影响0人  赵阳_c149

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而不是实例!

上一篇下一篇

猜你喜欢

热点阅读