Python中的@staticmethod和@classmethod有什么区别?

@staticmethod装饰的function和用@staticmethod装饰的function有@staticmethod @classmethod

也许有一些示例代码会有帮助:注意fooclass_foostatic_foo的调用签名的static_foo

 class A(object): def foo(self,x): print "executing foo(%s,%s)"%(self,x) @classmethod def class_foo(cls,x): print "executing class_foo(%s,%s)"%(cls,x) @staticmethod def static_foo(x): print "executing static_foo(%s)"%xa=A() 

以下是对象实例调用方法的常用方法。 对象实例a隐式地作为第一个parameter passing。

 a.foo(1) # executing foo(<__main__.A object at 0xb7dbef0c>,1) 

在类方法中 ,对象实例的类作为第一个参数隐式传递,而不是self

 a.class_foo(1) # executing class_foo(<class '__main__.A'>,1) 

你也可以使用这个类来调用class_foo 。 实际上,如果你定义了一些类方法,可能是因为你打算从类而不是类实例中调用它。 A.foo(1)会产生TypeError,但是A.class_foo(1)工作的很好:

 A.class_foo(1) # executing class_foo(<class '__main__.A'>,1) 

一个人使用的方法是创build可inheritance的替代构造函数 。


使用静态方法self (对象实例)和cls (类)都不会作为第一个参数隐式传递。 除了可以从实例或类调用它们之外,它们的行为与普通函数类似:

 a.static_foo(1) # executing static_foo(1) A.static_foo('hi') # executing static_foo(hi) 

静态方法用于对与类具有某种逻辑连接的函数进行分组。


foo只是一个函数,但是当你调用a.foo你不会得到这个函数,你会得到一个“部分应用”的函数版本,其中的对象实例绑定为函数的第一个参数。 foo需要2个参数,而a.foo只需要1个参数。

a必然是foo 。 以下是“绑定”一词的含义:

 print(a.foo) # <bound method A.foo of <__main__.A object at 0xb7d52f0c>> 

对于a.class_fooa不绑定到class_foo ,而是将类A绑定到class_foo

 print(a.class_foo) # <bound method type.class_foo of <class '__main__.A'>> 

在这里,使用静态方法,即使它是一个方法, a.static_foo只是返回一个没有绑定参数的好的'ole函数。 static_foo需要1个参数,而a.static_foo需要1个参数。

 print(a.static_foo) # <function static_foo at 0xb7d479cc> 

当然,当你用类A调用static_foo时, static_foo发生同样的事情。

 print(A.static_foo) # <function static_foo at 0xb7d479cc> 

静态方法是一种对所调用的类或实例一无所知的方法。 它只是获得通过的参数,没有隐含的第一个参数。 在Python中基本没用 – 你可以使用模块函数而不是静态方法。

另一方面,类方法是一个方法,它通过被调用的类或者被调用的实例的类作为第一个参数。 当你希望这个方法成为这个类的一个工厂的时候,这是非常有用的:因为它获得了作为第一个参数被调用的实际类,所以你可以总是实例化正确的类,即使在涉及子类时也是如此。 例如,观察类方法dict.fromkeys()如何在子类上调用时返回子类的实例:

 >>> class DictSubclass(dict): ... def __repr__(self): ... return "DictSubclass" ... >>> dict.fromkeys("abc") {'a': None, 'c': None, 'b': None} >>> DictSubclass.fromkeys("abc") DictSubclass >>> 

基本上@classmethod创build了一个方法,其第一个参数是从其调用的类(而不是类实例), @staticmethod没有任何隐式参数。

官方的python文档:

@classmethod

类方法将类作为隐式第一个参数接收,就像实例方法接收实例一样。 要声明一个类的方法,使用这个习惯用法:

 class C: @classmethod def f(cls, arg1, arg2, ...): ... 

@classmethodforms是一个函数装饰器 – 有关详细信息,请参阅函数定义中的函数定义的描述。

它可以在类(如Cf() )或实例(如C().f() )上调用。 该实例被忽略,除了它的类。 如果为派生类调用类方法,则派生类对象作为隐含的第一个parameter passing。

类方法不同于C ++或Java静态方法。 如果您需要这些,请参阅本节中的staticmethod()

@staticmethod

静态方法不会收到隐式的第一个参数。 为了声明一个静态方法,使用这个习惯用法:

 class C: @staticmethod def f(arg1, arg2, ...): ... 

@staticmethodforms是一个函数装饰器 – 详细信息请参阅函数定义中函数定义的描述。

它可以在类(如Cf() )或实例(如C().f() )上调用。 该实例被忽略,除了它的类。

Python中的静态方法类似于Java或C ++中的方法。 有关更高级的概念,请参阅本节中的classmethod()

这是关于这个问题的简短文章

@staticmethod函数不过是一个类中定义的函数。 它可以被调用,而不需要先实例化类。 它的定义是通过inheritance而不可变的。

@classmethod函数也可以在没有实例化类的情况下调用,但是其定义如下Sub类,而不是Parent类,通过inheritance。 这是因为@classmethod函数的第一个参数必须始终是cls(class)。

Python中的@staticmethod和@classmethod有什么区别?

您可能已经看到了类似于这种伪代码的Python代码,它演示了各种方法types的签名,并提供了一个文档string来解释每种方法:

 class Foo(object): def a_normal_instance_method(self, arg_1, kwarg_2=None): ''' Return a value that is a function of the instance with its attributes, and other arguments such as arg_1 and kwarg2 ''' @staticmethod def a_static_method(arg_0): ''' Return a value that is a function of arg_0. It does not know the instance or class it is called from. ''' @classmethod def a_class_method(cls, arg1): ''' Return a value that is a function of the class and other arguments. respects subclassing, it is called with the class it is called from. ''' 

正常实例方法

首先我将解释a_normal_instance_method 。 这正是所谓的“ 实例方法 ”。 当使用实例方法时,它被用作部分函数(相对于在源代码中查看所有值时定义的全部函数),即在使用时,第一个参数被预定义为对象,具有所有的给定属性。 它具有绑定到它的对象的实例,它必须从对象的一个​​实例中调用。 通常,它将访问实例的各种属性。

例如,这是一个string的实例:

 ', ' 

如果我们使用实例方法, join这个string,join另一个迭代,它显然是实例的一个函数,除了作为可迭代列表的函数之外, ['a', 'b', 'c']

 >>> ', '.join(['a', 'b', 'c']) 'a, b, c' 

绑定的方法

实例方法可以通过虚线查找来绑定以备后用。

例如,这将str.join方法绑定到':'实例:

 >>> join_with_colons = ':'.join 

后来我们可以使用它作为一个已经绑定了第一个参数的函数。 这样,就像实例上的部分函数一样工作:

 >>> join_with_colons('abcde') 'a:b:c:d:e' >>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF']) 'FF:FF:FF:FF:FF:FF' 

静态方法

静态方法不会将实例作为参数。

这与模块级function非常相似。

但是,模块级function必须存在于模块中,并专门导入到其他使用模块的地方。

但是,如果附加到对象上,它将通过导入和inheritance方便地跟踪对象。

静态方法的一个例子是str.maketrans ,它是从Python 3的string模块中移出来的。它使得转换表适合str.translate 。 从string的实例中使用它似乎相当愚蠢,如下所示,但是从string模块中导入函数相当笨拙,能够从类中调用它是很好的,就像在str.maketrans

 # demonstrate same function whether called from instance or not: >>> ', '.maketrans('ABC', 'abc') {65: 97, 66: 98, 67: 99} >>> str.maketrans('ABC', 'abc') {65: 97, 66: 98, 67: 99} 

在Python 2中,你必须从越来越less用的string模块中导入这个函数:

 >>> import string >>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc')) 'abcDEFG' 

类方法

一个类方法类似于一个实例方法,因为它需要一个隐含的第一个参数,而不是采取实例,它采用类。 通常这些被用作更好的语义用法的替代构造函数,它将支持inheritance。

内置类方法的最典型的例子是dict.fromkeys 。 它被用作字典的替代构造函数,(非常适合当你知道你的键是什么,并希望他们的默认值。)

 >>> dict.fromkeys(['a', 'b', 'c']) {'c': None, 'b': None, 'a': None} 

当我们inheritancedict的时候,我们可以使用相同的构造函数来创build子类的一个实例。

 >>> class MyDict(dict): 'A dict subclass, use to demo classmethods' >>> md = MyDict.fromkeys(['a', 'b', 'c']) >>> md {'a': None, 'c': None, 'b': None} >>> type(md) <class '__main__.MyDict'> 

查看其他类似的替代构造函数的pandas源代码 ,另请参阅classmethodstaticmethod的官方Python文档。

要决定是否使用@staticmethod或@classmethod你必须看看你的方法。 如果你的方法访问你的类中的其他variables/方法,那么使用@classmethod 。 另一方面,如果你的方法不触及类的任何其他部分,那么使用@staticmethod。

 class Apple: _counter = 0 @staticmethod def about_apple(): print('Apple is good for you.') # note you can still access other member of the class # but you have to use the class instance # which is not very nice, because you have repeat yourself # # For example: # @staticmethod # print('Number of apples have been juiced: %s' % Apple._counter) # # @classmethod # print('Number of apples have been juiced: %s' % cls._counter) # # @classmethod is especially useful when you move your function to other class, # you don't have to rename the class reference @classmethod def make_apple_juice(cls, number_of_apples): print('Make juice:') for i in range(number_of_apples): cls._juice_this(i) @classmethod def _juice_this(cls, apple): print('Juicing %d...' % apple) cls._counter += 1 

python 2.4中添加了@decorators如果你使用python <2.4,你可以使用classmethod()和staticmethod()函数。

例如,如果你想创build一个工厂方法(一个函数根据它得到的参数返回一个不同实现类的实例),你可以做如下的事情:

 class Cluster(object): def _is_cluster_for(cls, name): """ see if this class is the cluster with this name this is a classmethod """ return cls.__name__ == name _is_cluster_for = classmethod(_is_cluster_for) #static method def getCluster(name): """ static factory method, should be in Cluster class returns a cluster object for the given name """ for cls in Cluster.__subclasses__(): if cls._is_cluster_for(name): return cls() getCluster = staticmethod(getCluster) 

还要注意,这是使用类方法和静态方法的一个很好的例子,静态方法显然属于类,因为它在内部使用类Cluster。 classmethod只需要关于类的信息,而不需要对象的实例。

使_is_cluster_for方法成为类方法的另一个好处是,子类可以决定改变它的实现,也许因为它是非常通用的,并且可以处理多种types的集群,所以仅仅检查类的名称是不够的。

我认为一个更好的问题是“什么时候使用@classmethod和@staticmethod?”

@classmethod允许您轻松访问与类定义关联的私有成员。 这是一个很好的方式来做singletons,或工厂类,控制创build的对象的实例的数量存在。

@staticmethod提供了边际性能的提升,但是我还没有看到在类中静态方法的有效使用,而这种方法不能作为类之外的独立函数来实现。

静态方法:

  • 简单的function,没有自我的论点。
  • 处理class级属性; 而不是实例属性。
  • 可以通过类和实例调用。
  • 内置函数staticmethod()用于创build它们。

静态方法的好处:

  • 它在classscope中本地化函数名称
  • 它将函数代码更靠近它所使用的位置
  • 导入与模块级函数更方便,因为每种方法都不需要特别导入

     @staticmethod def some_static_method(*args, **kwds): pass 

类方法:

  • 具有第一个参数作为classname的函数。
  • 可以通过类和实例调用。
  • 这些是使用classmethod内置函数创build的。

      @classmethod def some_class_method(cls, *args, **kwds): pass 

@staticmethod只是将方法描述符的默认函数禁用。 classmethod将你的函数封装在一个可调用的容器中,该容器通过对拥有的类的引用作为第一个参数:

 >>> class C(object): ... pass ... >>> def f(): ... pass ... >>> staticmethod(f).__get__(None, C) <function f at 0x5c1cf0> >>> classmethod(f).__get__(None, C) <bound method type.f of <class '__main__.C'>> 

事实上, classmethod具有运行时间开销,但是可以访问拥有的类。 或者,我推荐使用一个元类,并将类方法放在该元类上:

 >>> class CMeta(type): ... def foo(cls): ... print cls ... >>> class C(object): ... __metaclass__ = CMeta ... >>> C.foo() <class '__main__.C'> 

@classmethod的意思是 :当这个方法被调用的时候,我们传递这个类作为第一个参数,而不是那个类的实例(就像我们通常用的方法一样)。 这意味着您可以在该方法内使用该类及其属性,而不是特定的实例。

@staticmethod的意思是:当这个方法被调用的时候,我们不会把类的实例传递给它(就像我们通常用的方法一样)。 这意味着你可以在一个类中放置一个函数,但是你不能访问那个类的实例(当你的方法不使用实例的时候,这是很有用的)。

关于如何在Python中使用静态,类或抽象方法的权威指南是这个主题的一个很好的链接,并将其总结如下。

@staticmethod函数不过是一个类中定义的函数。 它可以被调用,而不需要先实例化类。 它的定义是通过inheritance而不可变的。

  • Python不必为对象实例化绑定方法。
  • 它简化了代码的可读性,并且不依赖于对象本身的状态;

@classmethod函数也可以在没有实例化类的情况下调用,但是它的定义如下Sub类,而不是Parent类,通过inheritance,可以被子类覆盖。 这是因为@classmethod函数的第一个参数必须始终是cls (class)。

  • 工厂方法 ,用于使用例如某种预处理来为类创build实例。
  • 调用静态方法的静态方法 :如果在多个静态方法中分离静态方法,则不应该对类名称进行硬编码,而应使用类方法

关于静态方法和类方法的另一个考虑就是inheritance。 假设你有以下课程:

 class Foo(object): @staticmethod def bar(): return "In Foo" 

然后你想在一个子类中覆盖bar()

 class Foo2(Foo): @staticmethod def bar(): return "In Foo2" 

这是有效的,但是请注意,现在,子类( Foo2 )中的bar()实现不能再利用该类的任何特定的优势。 例如,假设Foo2有一个名为magic()的方法,您可以在bar()Foo2实现中使用它:

 class Foo2(Foo): @staticmethod def bar(): return "In Foo2" @staticmethod def magic(): return "Something useful you'd like to use in bar, but now can't" 

这里的解决方法是在bar()调用Foo2.magic() ,但是你重复自己(如果Foo2的名字改变了,你必须记得更新bar()方法)。

对我来说,这是一个轻微的违反开放/封闭的原则 ,因为在Foo做出的决定影响了你在派生类中重构通用代码的能力(也就是说对扩展的开放性较低)。 如果bar()是一个classmethod我们会很好:

 class Foo(object): @classmethod def bar(cls): return "In Foo" class Foo2(Foo): @classmethod def bar(cls): return "In Foo2 " + cls.magic() @classmethod def magic(cls): return "MAGIC" print Foo2().bar() 

给: In Foo2 MAGIC

在Python中 ,classmethod接收一个类作为隐式的第一个参数。 对象实例的类作为第一个参数隐式传递。 当需要方法作为类的工厂时,这可能是有用的,因为它将实际的类(称为方法)作为第一个参数,可以实例化正确的类,即使子类也是关心的。

静态方法只是一个类中定义的函数。 它不知道被调用的类或实例的任何内容,只获取没有任何隐含的第一个parameter passing的参数。 例:

 class Test(object): def foo(self, a): print "testing (%s,%s)"%(self,a) @classmethod def foo_classmethod(cls, a): print "testing foo_classmethod(%s,%s)"%(cls,a) @staticmethod def foo_staticmethod(a): print "testing foo_staticmethod(%s)"%a test = Test() 

静态方法用于将与类具有某种逻辑连接的函数分组。

我将尝试用一个例子来解释基本的差异。

 class A(object): x = 0 def say_hi(self): pass @staticmethod def say_hi_static(): pass @classmethod def say_hi_class(cls): pass def run_self(self): self.x += 1 print self.x # outputs 1 self.say_hi() self.say_hi_static() self.say_hi_class() @staticmethod def run_static(): print Ax # outputs 0 # A.say_hi() # wrong A.say_hi_static() A.say_hi_class() @classmethod def run_class(cls): print cls.x # outputs 0 # cls.say_hi() # wrong cls.say_hi_static() cls.say_hi_class() 

1 – 我们可以直接调用静态和类方法而不需要初始化

 # A.run_self() # wrong A.run_static() A.run_class() 

2-静态方法不能调用自己的方法,但可以调用其他静态方法和类方法

3-静态方法属于类,根本不使用对象。

4-类方法不绑定到一个对象,而是一个类。

@classmethod:可以用来创build一个共享的全局访问所有创build该类的实例…..就像更新一个logging由多个用户….我特别发现它用于创build单身人士以及..: )

@静态方法:与类或实例关联…无关,但为了可读性可以使用静态方法

我开始用C ++,然后是Java,然后是Python学习编程语言,所以这个问题困扰了我很多,直到我明白每个的简单用法。

类方法:与Java和C ++不同,Python没有构造函数重载。 为了达到这个目的,你可以使用classmethod 。 下面的例子将解释这一点

让我们考虑我们有一个Person类,它带有两个参数first_namelast_name并创buildPerson的实例。

 class Person(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name 

现在,如果需求到了需要使用单一名称创build类的地方,只需要一个first_name 。 你不能在Python中做这样的事情。

当你尝试创build一个对象(实例)时,这会给你一个错误。

 class Person(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name def __init__(self, first_name): self.first_name = first_name 

但是,您可以使用@classmethod来达到同样的@classmethod ,如下所述

 class Person(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name @classmethod def get_person(cls, first_name): return cls(first_name, "") 

静态方法:这是一个相当简单的,它不绑定到实例或类,你可以使用类名简单的调用。

所以我们假设在上面的例子中,你需要validationfirst_name不能超过20个字符,你可以简单的做到这一点。

@staticmethod def validate_name(name):return len(name)<= 20

你可以使用类名简单的调用

 Person.validate_name("Gaurang Shah") 
 #!/usr/bin/python #coding:utf-8 class Demo(object): def __init__(self,x): self.x = x @classmethod def addone(self, x): return x+1 @staticmethod def addtwo(x): return x+2 def addthree(self, x): return x+3 def main(): print Demo.addone(2) print Demo.addtwo(2) #print Demo.addthree(2) #Error demo = Demo(2) print demo.addthree(2) if __name__ == '__main__': main() 

iPython中其他相同方法的快速破解揭示了@staticmethod产生的边际性能增益(以纳秒为单位),否则它似乎不起作用。 另外,在编译期间(在运行脚本之前执行任何代码之前staticmethod() ,通过staticmethod()处理方法的额外工作可能会消除任何性能增益。

为了代码的可读性,我会避免@staticmethod除非你的方法将被用于纳秒计数的工作负载。