python有3种方法:常规,class和static。这让人有些困惑【1】【2】,我们不明白为什么我们要用到他们。甚至有人说我们根本不需要static方法,因此建议不要用它。
常规方法和class方法需要传入第一个变量。
常规方法:self
class方法:cls
而static不需要类似的变量。
因此,一个static方法没有self和cls的访问权,他像常规方法一样工作,但是从某种意思上说属于class。通常,static方法使用在类中定义的变量,但是多数情况下,我们想要将static方法放入class的定义中仅仅是因为他同class有逻辑上的联系(松散的耦合)。而且,将他定义在class中有利于对其进行维护。
方法
方法就是一个由def
语句定义的函数对象。
方法同简单函数的工作方式相同,但是有一个例外:一个方法的第一个参数必须是接受实例对象:
- 简单函数:定义在class的外部,这个函数通过class的实例访问class的属性。
def outside_foo():
- 实例方法:
def foo(self,)
- class方法:如果我们需要用到class的属性
@classmethod
def cfoo(cls,)
- static方法:不需要class的任何信息
@staticmethod
def sfoo()
他们相似多于不同。
大多数情况下,我们可以用简单函数做同样的事情。但是从软件设计
、代码简洁和效能的角度来说,他们的用法还是有细微的区别的。
本文主要是讨论class方法和static方法的区别。
还有另外一种方法,实例方法,但是实例方法的内部工作原理和class方法是相同的。实际上,python会自动的将对实例方法的调用映射到class方法。比方说,对一个实例调用:
instance.method(args...)
将自动的被转化成对class 方法的调用:
class.method(instance, args...)
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
。
有两种调用方法的方式:
- 就像上面那样,通过实例调用。
- 通过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
注意:
- 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>>
- 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方法,可以有两种方式调用他:
- 从class调用
- 从实例调用
>>> 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)
【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而不是实例!
网友评论