静态方法和类方法之间的区别


3579

@staticmethod修饰的功能和用修饰的功能有什么区别@classmethod


11
出于清洁的考虑,静态方法有时最好作为python中的模块级函数使用。使用模块功能,可以更轻松地仅导入所需的功能并避免不必要的“。”。语法(我在看你的Objective-C)。类方法具有更多用途,因为它们可以与多态性结合使用以创建“工厂模式”功能。这是因为类方法将类作为隐式参数接收。
FistOfFury

27
tl; dr >>与常规方法相比,静态方法和类方法也可以使用类进行访问,但是与类方法不同,静态方法通过继承是不可变的。
imsrgadich

4
Raymond Hettinger关于该主题的相关演讲:youtube.com/watch?
v=HTLu2DFOdTg

更精确的youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689您只需要使用classmethod作为替代构造函数。否则,您可以使用staticmethod并通过某种方式提供更多信息的实际“ CLASSNAME”(通过。/点)来访问任何类属性,而不是像classmethod中那样使用cls
Robert Nowak

Answers:


3136

也许有点示例代码将有助于:发现其中的差别在调用签名fooclass_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)" % x    

a = A()

以下是对象实例调用方法的常用方法。对象实例,a作为第一个参数隐式传递。

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

使用classmethods时,对象实例的类作为第一个参数而不是隐式传递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)

人们发现类方法的一种用途是创建可继承的替代构造函数


使用staticmethods时self(对象实例)和 cls(类)都不会隐式传递为第一个参数。它们的行为类似于普通函数,不同之处在于您可以从实例或类中调用它们:

a.static_foo(1)
# executing static_foo(1)

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

静态方法用于对与类之间具有某种逻辑联系的函数进行分组。


foo只是一个函数,但是当您调用a.foo它时,不仅得到函数,还会得到函数的“部分应用”版本,其中对象实例a绑定为函数的第一个参数。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>

当然,当您static_foo使用类进行调用时,也会发生同样的事情A

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

182
我不明白使用staticmethod有什么收获。我们可以只使用一个简单的类外函数。
奥尔科特(Alcott)

372
@Alcott:您可能想将函数移到类中,因为它在逻辑上属于该类。在Python源代码(例如,multiprocessing,turtle,dist-packages)中,它用于从模块名称空间“隐藏”单下划线“ private”函数。但是,它的使用高度集中在仅几个模块中-可能表明它主要是样式问题。尽管我找不到任何示例,但@staticmethod可以被子类覆盖来帮助组织代码。没有它,您将在模块名称空间中浮动该函数的变体。
unutbu 2011年

14
...以及有关在何处以及为什么使用实例,类或静态方法的解释。您没有对此一言不发,但OP也没有询问。
MestreLion'5

106
@Alcott:正如unutbu所说,静态方法是一种组织/风格特征。有时,一个模块有许多类,并且某些辅助函数在逻辑上与一个给定的类相关联,而不与其他类相关联,因此,不要用许多“自由函数”“污染”该模块是有意义的,最好使用静态的该方法要比依靠糟糕的样式将类和函数defs混合在一起只是在代码中表明它们“相关”有关
MestreLion 2012年

4
的另一种用途@staticmethod-您可以用它来清除碎屑。我正在用Python实现编程语言-库定义的函数使用静态execute方法,其中用户定义的函数需要实例参数(即函数主体)。该修饰器消除了PyCharm检查器中的“未使用的参数自我”警告。
tehwrus

798

一个静态方法是一无所知,它被称为上类或实例的方法。它只是获取传递的参数,没有隐式的第一个参数。在Python中基本上没有用-您可以使用模块函数代替静态方法。

类方法,在另一方面,是获取传递的类,它被称为上,或该类的实例,它被称为上的,作为第一个参数的方法。当您希望该方法成为类的工厂时,这很有用:由于它获得了作为第一个参数调用的实际类,因此即使涉及子类,也始终可以实例化正确的类。例如dict.fromkeys(),观察在子类上调用时,类方法如何返回子类的实例:

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

712
静态方法不是没有用的-它是一种将函数放入类的方法(因为它在逻辑上属于该类),同时指示它不需要访问该类。
托尼·迈尔

135
因此,只有“基本”没有用。这种组织以及依赖注入是对静态方法的有效使用,但是由于模块(而不是Java中的类)是Python中代码组织的基本元素,因此它们的使用和实用性很少。
Thomas Wouters

40
当与类或其实例无关的方法在类中定义方法时,逻辑如何?
本·詹姆斯

106
也许是为了继承?静态方法可以像实例方法和类方法一样被继承和覆盖,并且查找按预期进行(与Java不同)。无论是在类还是实例上调用静态方法,都无法真正静态地解决,因此类和静态方法之间的唯一区别是隐式的第一个参数。
haridsv 2010年

80
它们还创建了一个更简洁的名称空间,并使您更容易理解该函数与该类有关。
Imbrondir

149

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


103

官方python文档:

@classmethod

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

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

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

可以在类(如C.f())或实例(如C().f())上调用它。该实例除其类外均被忽略。如果为派生类调用类方法,则派生类对象作为隐式第一个参数传递。

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

@staticmethod

静态方法不会收到隐式的第一个参数。要声明静态方法,请使用以下惯用法:

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

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

可以在类(如C.f())或实例(如C().f())上调用它。该实例除其类外均被忽略。

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


文档中没有错误吗?不应使用staticmethod:“实例及其类都被忽略”。而不是“除了实例之外,实例被忽略”?
mirek

这可能是剪切和粘贴错误,但严格来说,如果您忽略类,则无法在类上调用方法。
亚伦·本特利

76

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

@staticmethod函数不过是在类内部定义的函数。可调用而无需先实例化该类。它的定义通过继承是不可变的。

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


1
那么这是否意味着通过使用静态方法,我总是绑定到Parent类,而通过类方法,我始终绑定在其中声明该类方法的类(在本例中为子类)?
Mohan Gulati,2009年

7
不。通过使用静态方法,您根本不受束缚。没有隐式的第一个参数。通过使用classmethod,您可以将调用方法的类(如果直接在类上直接调用)或调用方法的实例的类(如果在实例上调用)作为隐式第一个参数。
马特·安德森

6
可以扩展一点,以表明,通过将类作为第一个参数,类方法可以直接访问其他类的属性和方法,而静态方法则不能(这需要对MyClass.attr进行硬编码)
MestreLion

“它的定义通过继承是不可变的。” 在Python中没有任何意义,您可以覆盖静态方法就可以了。
cz

68

要决定使用@staticmethod还是@classmethod,您必须查看方法内部。如果您的方法访问类中的其他变量/方法,请使用@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

classmethod和cls._counter相对于staticmethod和Apple._counter的优势是什么
Robert Nowak

1
cls._countercls._counter即使将代码放在不同的类中,或者更改了类名,也将仍然是这样。Apple._counter是针对该Apple班级的;对于其他类,或更改类名时,您将需要更改引用的类。
kiamlaluno

52

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

您可能已经看到了类似此伪代码的Python代码,该代码演示了各种方法类型的签名,并提供了一个文档字符串来说明每种方法:

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。这就是所谓的“ 实例方法 ”。使用实例方法时,它用作部分函数(与总函数相反,在源代码中查看时为所有值定义的总函数),即在使用时,将第一个参数预定义为具有所有给定属性的对象。它具有绑定到其对象的实例,并且必须从该对象的实例调用它。通常,它将访问实例的各种属性。

例如,这是一个字符串的实例:

', '

如果我们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'

静态方法

静态方法并没有把实例作为参数。

它与模块级功能非常相似。

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

但是,如果将其附加到对象上,它也将通过导入和继承方便地跟随对象。

静态方法的一个示例是str.maketransstringPython 3 的模块中移出的。它使转换表适合由占用str.translate。从字符串的实例使用时,看起来确实很愚蠢,如下所示,但是从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中,您必须从越来越少用的字符串模块中导入此函数:

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

类方法

类方法与实例方法类似,因为它采用了隐式的第一个参数,但是它采用了类,而不是采用实例。通常,它们被用作替代构造函数以更好地使用语义,并且它将支持继承。

内建类方法的最典型示例是dict.fromkeys。它用作dict的替代构造函数(非常适合当您知道键是什么并且想要它们的默认值时)。

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

当我们对dict进行子类化时,可以使用相同的构造函数,该构造函数创建子类的实例。

>>> 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'>

看到熊猫的源代码的替代构造其它类似的例子,同时也看到了官方的Python文档classmethodstaticmethod


42

我开始使用C ++,Java和Python学习编程语言,所以这个问题也困扰着我,直到我理解了每种语言的简单用法。

类方法:与Java和C ++不同,Python没有构造函数重载。因此,可以使用实现此目的classmethod。以下示例将对此进行解释

让我们考虑,我们有一个Person类,它有两个参数first_name,并last_name与创建的实例Person

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

现在,如果您只需要使用一个名称创建一个类(仅使用一个名称)first_name,那么您将无法在Python中执行类似的操作。

当您尝试创建对象(实例)时,这将给您一个错误。

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以下方法实现相同的目的

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, "")

静态方法:这很简单,它不受实例或类的约束,您可以使用类名简单地调用它。

因此,假设在上面的示例中,您需要一个first_name不超过20个字符的验证,您只需执行此操作即可。

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

你可以简单地使用 class name

Person.validate_name("Gaurang Shah")

2
这是一个古老的文章,但是要使用构造函数来接受一个或两个参数的更多pythonic方式将def __init__(self, first_name, last_name="")代替classmethod get_person。在这种情况下,结果也将完全相同。
akarilimano

31

我认为一个更好的问题是“何时使用@classmethod与@staticmethod?”

@classmethod允许您轻松访问与类定义关联的私有成员。这是完成单例或工厂类的一种好方法,该类控制已创建对象的实例数量。

@staticmethod可以提供少量的性能提升,但是我还没有看到在类中有效地使用静态方法,而该方法不能作为类外的独立函数来实现。


31

@decorators是在python 2.4中添加的。如果您使用的是python <2.4,则可以使用classmethod()和staticmethod()函数。

例如,如果您想创建一个工厂方法(一个函数根据得到的参数返回一个类的不同实现的实例),您可以执行以下操作:

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。类方法仅需要有关类的信息,而无需对象的实例。

_is_cluster_for方法设为类方法的另一个好处是,子类可以决定更改其实现,这可能是因为它非常通用并且可以处理多种类型的集群,因此仅检查类的名称是不够的。


28

静态方法:

  • 没有自变量的简单函数。
  • 处理类属性;不在实例属性上。
  • 可以通过类和实例调用。
  • 内置函数staticmethod()用于创建它们。

静态方法的好处:

  • 它在类范围内本地化函数名称
  • 它将功能代码移近使用位置
  • 与模块级函数相比,导入更方便,因为不必专门导入每种方法

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

类方法:

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

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

22

@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'>

1
我马上想到的一个元类的缺点可能是,您不能直接在实例上调用类方法。c = C(); c.foo()引发AttributeError,您必须这样做type(c).foo()。这也可能被认为是一项功能-我想不出为什么要这么做。
亚伦·霍尔

20

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

@staticmethod函数不过是在类内部定义的函数。可调用而无需先实例化该类。它的定义通过继承是不可变的。

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

@classmethod函数也可以在不实例化该类的情况下调用,但是其定义遵循子类,而不是父类,通过继承可以被子类覆盖。这是因为@classmethodfunction 的第一个参数必须始终为cls(类)。

  • 工厂方法,用于使用例如某种预处理为类创建实例。
  • 静态方法调用静态方法:如果将静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法

感谢@zangw-静态函数的继承不变性是它的主要区别
-hard_working_ant

18

只有第一个参数不同

  • 普通方法:当前对象(如果自动作为(附加)第一个参数传递)
  • classmethod:当前对象的类作为(附加)fist参数自动传递
  • 静态方法:不会自动传递其他参数。您传递给该函数的就是所得到的。

更详细地...

普通方法

调用对象的方法时,它会自动获得一个额外的参数self作为其第一个参数。即方法

def f(self, x, y)

必须使用2个参数调用。self是自动传递的,它是对象本身

类方法

装饰方法时

@classmethod
def f(cls, x, y)

自动提供的参数不是 self,而是的类 self

静态方法

装饰方法时

@staticmethod
def f(x, y)

该方法根本没有任何自动参数。仅提供调用它的参数。

用法

  • classmethod 主要用于替代构造函数。
  • staticmethod不使用对象的状态。它可能是类外部的函数。它仅放在类中以将具有相似功能的功能分组(例如,类似于Java的Math类静态方法)
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

16

让我先说一下用@classmethod装饰的方法与@staticmethod装饰的方法之间的相似性。

相似:两者都可以在本身上调用,而不仅仅是类的实例。因此,从某种意义上来说,它们都是Class的方法

区别:类方法将接收类本身作为第一个参数,而静态方法则不接收。

因此,从某种意义上说,静态方法并不绑定于Class本身,而只是因为它可能具有相关的功能而挂在这里。

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

11

关于静态方法与类方法的另一个考虑是继承。假设您有以下课程:

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

然后,您想覆盖bar()一个子类:

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

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

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" 

这里的解决办法是打电话Foo2.magic()bar(),但此时你重复自己(如果名称Foo2的改变,你必须记住要更新bar()方法)。

对我来说,这有点违反开放式/封闭式原则,因为做出的决定Foo会影响您在派生类中重构通用代码的能力(即扩展性较小)。如果bar()是a,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


7

我将尝试通过一个示例来说明基本区别。

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 A.x  # 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-静态方法不能调用self方法,但可以调用其他static和classmethod

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

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


广告2:确定吗?静态方法如何调用类方法?它没有对它的引用(对它的类)。
mirek

7

@classmethod:可用于创建对该类创建的所有实例的共享全局访问......例如由多个用户更新记录....我特别发现创建单例时它也很有效..: )

@static方法:与与...相关联的类或实例无关,但出于可读性考虑,可以使用static方法


5

您可能需要考虑以下两者之间的区别:

Class A:
    def foo():  # no self parameter, no decorator
        pass

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

这在python2和python3之间发生了变化:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

因此@staticmethod,仅在类中直接使用 for方法已成为python3中的可选方法。如果要从类和实例中调用它们,则仍需要使用@staticmethod装饰器。

unutbus的答案很好地涵盖了其他情况。


4

我的贡献演示之间的差异@classmethod@staticmethod以及实例方法,包括如何实例可以间接调用@staticmethod。但是@staticmethod与其从实例中间接调用a ,不如将其设为私有可能更像是“ pythonic”。这里没有演示从私有方法获取某些东西,但是基本上是相同的概念。

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

4

类方法将类作为隐式第一个参数接收,就像实例方法接收实例一样。它是绑定到类而不是类对象的方法,因为它使用指向类而不是对象实例的类参数,所以可以访问类的状态。它可以修改适用于该类所有实例的类状态。例如,它可以修改将适用于所有实例的类变量。

另一方面,与类方法或实例方法相比,静态方法不接收隐式的第一个参数。并且无法访问或修改类状态。它仅属于该类,因为从设计的角度来看这是正确的方法。但是就功能而言,在运行时未绑定到该类。

作为准则,请将静态方法用作实用程序,将类方法用作例如factory。或定义一个单例。并使用实例方法对实例的状态和行为进行建模。

希望我很清楚!


2

顾名思义,类方法用于更改类而不是对象。为了更改类,他们将修改类属性(而不是对象属性),因为这是更新类的方式。这就是类方法将类(通常用“ cls”表示)作为第一个参数的原因。

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

另一方面,静态方法用于执行未绑定到类的功能,即它们不会读取或写入类变量。因此,静态方法不将类作为参数。使用它们是为了使类执行与该类目的不直接相关的功能。

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

2

从字面上分析@staticmethod可以提供不同的见解。

类的常规方法是隐式动态方法,该方法将实例作为第一个参数。
相反,静态方法不将实例作为第一个参数,因此称为“静态”

静态方法确实是一种正常的功能,与类定义之外的功能相同。
幸运的是,将它分组在类中只是为了靠近它的应用位置,或者您可以滚动查找它。


2

我认为给出一个纯Python版本的staticmethodclassmethod将有助于在语言级别上理解它们之间的区别。

它们都是非数据描述符(如果您先熟悉描述符,会更容易理解它们)。

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner

1

静态方法无法访问继承层次结构中的对象,类或父类的服装。可以直接在类上调用它(无需创建对象)。

classmethod无法访问该对象的属性。但是,它可以访问继承层次结构中的类和父类的属性。可以直接在类上调用它(无需创建对象)。如果在该对象上调用,则它与普通方法相同,后者不会访问self.<attribute(s)>并且self.__class__.<attribute(s)>只能访问。

认为我们有一个带有的类b=2,我们将创建一个对象并将其重新设置为b=4其中。静态方法无法访问以前的任何内容。Classmethod .b==2只能通过进行访问cls.b。:普通方法可以同时访问.b==4通过self.b.b==2通过self.__class__.b

我们可以遵循KISS风格(保持简单,愚蠢):不要使用静态方法和类方法,不要在未实例化它们的情况下使用类,仅访问对象的属性self.attribute(s)。在某些语言中,以这种方式实现了OOP,我认为这不是一个坏主意。:)


关于类方法的另一重要事项:如果在类方法中修改属性,则该类中所有未显式设置此属性的现有对象将具有修改后的值。
mirek

-4

在iPython中对其他相同方法的快速分析表明,该方法会@staticmethod产生少量的性能提升(以纳秒为单位),但否则似乎无济于事。另外,staticmethod()在编译过程中(通过运行脚本执行任何代码之前),通过处理该方法的其他工作可能会消除所有性能提升。

出于代码可读性的考虑,@staticmethod除非您的方法用于纳秒级的工作负载,否则我将避免使用。


7
“否则似乎起不到任何作用”:严格来说不是正确的。参见上面的讨论。
基思·品森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.