Python 中的@staticmethod与@classmethod

@staticmethod vs @classmethod in Python

提问人:Daryl Spitzer 提问时间:9/26/2008 最后编辑:Super Kai - Kazuya ItoDaryl Spitzer 更新时间:10/20/2023 访问量:1057525

问:

@staticmethod装饰的方法和用@classmethod装饰的方法有什么区别?

python oop 静态 python-decorators 类方法

评论

25赞 FistOfFury 12/6/2017
为了简洁起见,静态方法有时最好作为 Python 中的模块级函数。使用模块函数,可以更轻松地只导入您需要的函数,并防止不必要的“.”语法(我正在看着你 Objective-C)。类方法具有更多用途,因为它们可以与多态性结合使用以创建“工厂模式”函数。这是因为类方法将类作为隐式参数接收。
62赞 Mr. Unnormalized Posterior 7/11/2018
tl;DR >> 与普通方法相比,静态方法和类方法也可以使用类访问,但与类方法不同的是,静态方法通过继承是不可变的。
9赞 moooeeeep 9/10/2018
Raymond Hettinger关于该主题的相关演讲:youtube.com/watch?v=HTLu2DFOdTg

答:

951赞 Thomas Wouters 9/26/2008 #1

staticmethod 是一种对调用它的类或实例一无所知的方法。它只是获取传递的参数,而不是隐式的第一个参数。

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

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

评论

858赞 Tony Meyer 9/26/2008
staticmethod 并非毫无用处 - 它是一种将函数放入类中的方法(因为它在逻辑上属于该类),同时指示它不需要访问该类。
155赞 Thomas Wouters 9/26/2008
因此,只有“基本上”无用。这种组织以及依赖注入是静态方法的有效用法,但由于模块(而不是 Java 中的类)是 Python 中代码组织的基本元素,因此它们的使用和有用性很少。
48赞 Ben James 3/12/2010
当方法与类或其实例无关时,在类中定义方法有什么逻辑?
123赞 haridsv 4/8/2010
也许是为了继承?静态方法可以像实例方法和类方法一样被继承和覆盖,并且查找按预期工作(与 Java 不同)。无论是在类上还是在实例上调用静态方法,静态方法都不会真正静态解析,因此类和静态方法之间的唯一区别是隐式的第一个参数。
101赞 Imbrondir 8/22/2011
它们还创建了一个更简洁的命名空间,并使其更容易理解函数与类有关。
194赞 Terence Simpson 9/26/2008 #2

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

评论

0赞 c z 8/17/2023
其他答案给一个简单的答案增加了不必要的混乱。从字面上看,这是唯一改变的想法。
30赞 Armin Ronacher 9/26/2008 #3

@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赞 Russia Must Remove Putin 6/15/2016
我立即想到的元类的一个可能的缺点是,您不能直接在实例上调用类方法。 引发 AttributeError,你必须这样做。这也可能被认为是一项功能 - 我想不出你为什么要这样做。c = C(); c.foo()type(c).foo()
98赞 Tom Neyland 11/4/2009 #4

这是一篇关于这个问题的短文

@staticmethod函数只不过是在类中定义的函数。它是可调用的,无需先实例化类。它的定义通过继承是不可变的。

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

评论

2赞 Mohan Gulati 11/4/2009
那么,这是否意味着通过使用静态方法,我总是绑定到 Parent 类,而使用 classmethod 绑定了我声明类方法的类(在本例中为子类)?
10赞 Matt Anderson 11/4/2009
不。通过使用 static方法,您完全不受约束;没有隐式的第一个参数。通过使用 classmethod,您可以获得调用方法的类(如果直接在类上调用它)或调用方法的实例的类(如果在实例上调用它)作为隐式第一个参数。
9赞 MestreLion 5/3/2012
可以稍微扩展一下,以表明,通过将类作为第一个参数,类方法可以直接访问其他类属性和方法,而静态方法则不能(为此,它们需要对 MyClass.attr 进行硬编码)
2赞 c z 9/17/2019
“它的定义是通过继承不可变的”在 Python 中没有任何意义,你可以很好地覆盖静态方法。
3880赞 unutbu 11/4/2009 #5

也许一些示例代码会有所帮助: 请注意 和 的调用签名的差异:fooclass_foostatic_foo

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({x})")

a = A()

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

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

使用 class方法,对象实例的类将作为第一个参数而不是 .self

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

您也可以使用类进行调用。事实上,如果你将某物定义为 一个类方法,这可能是因为你打算从类而不是从类实例调用它。 会引发 TypeError,但工作正常:class_fooA.foo(1)A.class_foo(1)

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

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


使用 staticmethods,(对象实例)和(类)都不会作为第一个参数隐式传递。它们的行为类似于普通函数,只是您可以从实例或类调用它们:selfcls

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

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

静态方法用于将与类与类具有某种逻辑联系的函数分组到该类。


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

a绑定到 。这就是下面“绑定”一词的含义:foo

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

with ,不绑定到 ,而是类绑定到 。a.class_fooaclass_fooAclass_foo

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

在这里,使用 staticmethod,即使它是一个方法,也只是返回 一个好的 'ole 函数,没有绑定参数。 期望 1 个参数,也期望 1 个参数。a.static_foostatic_fooa.static_foo

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

当然,当你和班级一起打电话时,也会发生同样的事情。static_fooA

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

评论

249赞 Alcott 9/19/2011
我不明白使用静态方法有什么问题。我们可以使用一个简单的类外函数。
500赞 unutbu 9/19/2011
@Alcott:您可能希望将函数移动到类中,因为它在逻辑上属于该类。在 Python 源代码(例如 multiprocessing,turtle,dist-packages)中,它用于从模块命名空间中“隐藏”单下划线“私有”函数。然而,它的使用高度集中在几个模块中——也许这表明它主要是一种风格的东西。虽然我找不到任何这方面的例子,但可以通过子类覆盖来帮助组织您的代码。如果没有它,您将在模块命名空间中浮动函数的变体。@staticmethod
154赞 MestreLion 5/3/2012
@Alcott:正如 Unutbu 所说,静态方法是一种组织/风格特征。有时一个模块有很多类,并且一些辅助函数在逻辑上与给定的类绑定,而不是与其他类绑定,因此不用许多“自由函数”来“污染”模块是有道理的,并且最好使用静态方法,而不是依靠在代码中将类和函数定义混合在一起的糟糕风格,只是为了表明它们是“相关的”
1赞 Philip Couling 1/21/2022
当然,上述对奥尔科特评论的回应也遗漏了显而易见的......可以重写子类中的静态方法和类方法。在这一点上,classmethod 和 staticmethod 之间的区别变得极其重要。如果一个静态方法调用另一个静态方法,它实际上应该是一个类方法调用,以便子类可以很容易地替换静态方法。cls.some_static_method()
1赞 saolof 3/24/2022
@Alcott Static方法定义了一个函数,但它又做了一件事——它使函数成为类变量。如果你碰巧想要一个作为函数(而不是方法)的类变量,并且不想将该函数用于与该类无关的任何内容,那么 staticmethod 是这样做的惯用方法。
128赞 Chris B. 11/4/2009 #6

官方 python 文档:

@classmethod

类方法将类接收为 隐式第一个参数,就像 instance 方法接收实例。 要声明类方法,请使用以下命令 成语:

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

表单是一个函数装饰器 – 参见 函数中的函数定义 定义了解详细信息。@classmethod

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

类方法不同于 C++ 或 Java 静态方法。如果需要帮助, 这些,请参阅此处的 staticmethod() 部分。

@staticmethod

静态方法不接收 隐式第一个参数。要声明 static 方法,使用这个成语:

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

表单是一个函数装饰器 – 参见 函数中的函数定义 定义了解详细信息。@staticmethod

它可以在类上调用 (例如 )或在实例上 (如)。实例是 忽略,但其类除外。C.f()C().f()

Python 中的静态方法与此类似 到 Java 或 C++ 中的那些。对于一个 更高级的概念,请参阅本节中的 ClassMethod()。

评论

0赞 mirek 3/2/2020
文档中没有错误吗?不应该是 staticmethod: “实例及其类都被忽略”,而不是“实例被忽略,除了它的类。
0赞 Aaron Bentley 5/15/2020
这可能是一个剪切和粘贴错误,但严格来说,如果你忽略一个类,你就不能在类上调用方法。
0赞 Hamidreza Hosseinkhani 12/8/2022
你错了!Java 中的“静态成员”等同于 Python 中的“类成员”(区别在于,在 Java 中,您可以从对象访问公共静态属性,但在 Python 中,则不能)。Java 中的“静态方法”等价于 Python 中的“classmethod”,可以访问类状态(类成员),Python 中的“staticmethod”是一种相对无用的方法,既不访问类状态,也不访问实例状态,就像类定义之外的纯函数。
37赞 Jens Timmerman 2/24/2012 #7

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

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

-5赞 Cathal Garvey 12/18/2012 #8

对 iPython 中其他相同方法的快速破解表明,它产生了边际性能提升(以纳秒为单位),但除此之外,它似乎没有任何作用。此外,在编译期间处理方法的额外工作可能会抵消任何性能提升(在运行脚本时发生任何代码执行之前)。@staticmethodstaticmethod()

为了代码的可读性,我会避免使用,除非您的方法将用于纳秒数的大量工作。@staticmethod

评论

8赞 Keith Pinson 12/18/2012
“否则似乎没有任何作用”:严格来说不是真的。请参阅上面的讨论。
75赞 Russia Must Remove Putin 1/24/2015 #9

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'

Static 方法

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

它与模块级函数非常相似。

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

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

静态方法的一个例子是 ,从 Python 3 中的模块中移出。它使转换表适合 使用 。当从字符串的实例中使用时,它看起来确实很愚蠢,如下所示,但是从模块导入函数相当笨拙,并且能够从类中调用它很好,如下所示str.maketransstringstr.translatestringstr.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'

Class 方法

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

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

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

当我们子类化字典时,我们可以使用相同的构造函数,它创建子类的实例。

>>> 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 文档。

47赞 Nathan Tregillus 5/19/2015 #10

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

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

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

评论

0赞 joel 8/14/2020
这个问题提到了私有班级成员的访问权。我想强调的是(取决于您对私有的定义),s 对 s 具有不同的访问级别。前者不应访问类私有类成员@staticmethod@classmethod
25赞 zangw 11/16/2015 #11

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

@staticmethod函数只不过是在类中定义的函数。它是可调用的,无需先实例化类。它的定义通过继承是不可变的。

  • Python 不必实例化 object 的 bound-method。
  • 它简化了代码的可读性,并且不依赖于对象本身的状态;

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

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

评论

0赞 Ulysses 5/2/2018
多亏了@zangw - 静态函数的继承不变性似乎是关键区别
139赞 Du D. 4/22/2016 #12

要决定是使用 @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 another class,
        #       you don't have to rename the referenced class 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Making juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing apple %d...' % apple)
        cls._counter += 1

评论

0赞 Robert Nowak 10/11/2019
ClassMethod 和 cls._counter 与 StaticMethod 和 Apple._counter 相比有什么优势
7赞 apaderno 11/13/2019
cls._counter即使代码被放在不同的类中,或者类名被更改,仍然会如此。 特定于类;对于其他类,或者更改类名时,需要更改引用的类。cls._counterApple._counterApple
0赞 Zachiah 11/24/2022
我希望“移动”的意思不是“复制”哈哈
0赞 Mehdi 3/9/2023
为什么在这种情况下还要打扰装饰器,您的描述并没有真正澄清
11赞 Rizwan Mumtaz 9/20/2016 #13

我将尝试用一个例子来解释基本区别。

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 - 我们可以直接调用 static 和 classmethods 而无需初始化

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

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

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

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

26赞 Adam Parkin 9/30/2016 #14

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

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

然后,您希望在子类中覆盖:bar()

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

这是可行的,但请注意,现在子类 () 中的实现不能再利用特定于该类的任何内容。例如,假设有一个调用的方法,你想在实现中使用:bar()Foo2Foo2magic()Foo2bar()

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()Foo2bar()

对我来说,这略微违反了开放/封闭原则,因为做出的决定会影响您在派生类中重构公共代码的能力(即它对扩展的开放程度较低)。如果是的话,我们会没事的:Foobar()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

另外:历史说明:Guido Van Rossum(Python 的创造者)曾经将 's 称为“意外”: https://mail.python.org/pipermail/python-ideas/2012-May/014969.htmlstaticmethod

我们都知道静态方法的局限性。(它们基本上是一个意外——回到 Python 2.2 时代,当我发明新式类和描述符时,我打算实现类方法,但起初我不理解它们,不小心先实现了静态方法。然后删除它们并仅提供类方法为时已晚。

另外: https://mail.python.org/pipermail/python-ideas/2016-July/041189.html

老实说,staticmethod 是一个错误——我试图做一些类似 Java 类方法的事情,但一旦它发布,我发现真正需要的是类方法。但是要摆脱staticmethod为时已晚。

39赞 Laxmi 10/3/2016 #15

静态方法:

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

静态方法的优点:

  • 它将函数名称本地化为类范围
  • 它将函数代码移动到更靠近其使用位置的位置
  • 与模块级函数相比,导入更方便,因为每个方法都不必专门导入

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

类方法:

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

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    
7赞 vijay 9/21/2017 #16

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

@static方法:与与...但为了可读性,可以使用静态方法

63赞 Gaurang Shah 10/10/2017 #17

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

类方法:与 Java 和 C++ 不同,Python 没有构造函数重载。因此,要实现这一点,您可以使用 .下面的例子将解释这一点classmethod

让我们假设我们有一个类,它接受两个参数并创建 的实例。Personfirst_namelast_namePerson

class Person(object):

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

现在,如果要求是你只需要使用一个名称创建一个类,只是一个 ,你不能在 Python 中做这样的事情。first_name

当您尝试创建对象(实例)时,这将为您提供错误。

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

静态方法:这很简单,它不绑定到实例或类,您可以简单地使用类名调用它。

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

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

您可以简单地使用class name

Person.validate_name("Gaurang Shah")

评论

11赞 akarilimano 9/18/2019
这是一篇旧文章,但更多的pythonic方法来实现构造函数接受一个或两个参数将使用而不是classmethod。在这种情况下,结果也将完全相同。def __init__(self, first_name, last_name="")get_person
4赞 Tushar Vazirani 12/1/2017 #18

顾名思义,类方法用于对类而不是对象进行更改。要对类进行更改,它们将修改类属性(而不是对象属性),因为这是您更新类的方式。 这就是类方法将类(通常用“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."

评论

0赞 joel 8/14/2020
方法并不总是进行更改
3赞 Wizard 12/12/2017 #19

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

类的普通方法是将实例作为第一个参数的隐式动态方法。
相比之下,static方法不将实例作为第一个参数,因此称为“static”。

staticmethod 确实是一个普通函数,与类定义之外的函数相同。
幸运的是,它被分组到类中,只是为了更接近应用它的位置,或者你可以滚动找到它。

33赞 Selva 1/14/2018 #20

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

相似:它们都可以在类本身上调用,而不仅仅是实例。所以,从某种意义上说,它们都是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',)

评论

0赞 sliders_alpha 9/21/2022
但是,为什么要使用一个而不是另一个呢?什么是真实世界的用例?
7赞 Michael Swartz 6/25/2018 #21

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

#!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)
'''
"""
7赞 David Schumann 1/24/2019 #22

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

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

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

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

蟒蛇2:

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

蟒蛇3:

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

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

其他情况已由unutbus回答很好地涵盖。

61赞 blue_note 5/21/2019 #23

只有第一个参数不同

  • Normal 方法:当前对象自动作为(附加的)第一个参数传递
  • ClassMethod:当前对象的类自动作为(附加的)拳头参数传递
  • staticmethod:不会自动传递任何额外的参数。您传递给函数的内容就是您得到的内容。

更详细地说...

普通方法

“标准”方法,就像在每种面向对象语言中一样。当调用对象的方法时,会自动为其指定一个额外的参数作为其第一个参数。即方法self

def f(self, x, y)

必须使用 2 个参数调用。 是自动传递的,它是对象本身。类似于神奇地出现在例如。java/c++,仅在 Python 中显式显示。selfthis

实际上,第一个参数不必调用,但它是标准约定,因此请保留它self

Class 方法

当方法被装饰时

@classmethod
def f(cls, x, y)

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

静态方法

当方法被装饰时

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

评论

0赞 Rich Lysakowski PhD 3/2/2022
这是上面一堆中最好的答案。通过良好而简洁的例子可以清楚地看出这一点。其他示例省略了一些细节。
0赞 Abayomi Olowu 7/18/2022
这是唯一能很好地解释这个概念的答案。我正在学习 Python 上 API 的 GraphQL,有些人使用 ClassMethod,而另一些人则在他们的类突变下使用 StaticMethod。这让我想到了为什么它们被这样使用的原因,这确实为我的好奇心提供了答案。谢谢
4赞 Jacky Wang 11/20/2019 #24

我认为给出一个纯粹的 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
7赞 Nicolae Petridean 11/20/2019 #25

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

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

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

希望我是清楚的!

0赞 mirek 3/3/2020 #26

StaticMethod 无权访问继承层次结构中的对象、类或父类的属性。 它可以直接在类中调用(无需创建对象)。

ClassMethod 无权访问对象的属性。但是,它可以访问继承层次结构中类和父类的属性。 它可以直接在类中调用(无需创建对象)。如果在对象处调用,则它与不访问和仅访问的普通方法相同。self.<attribute(s)>self.__class__.<attribute(s)>

假设我们有一个类,我们将创建一个对象并将其重新设置为其中。 Static方法不能访问上一个中的任何内容。 Class方法只能通过 访问。 普通方法可以同时访问:via 和 via。b=2b=4.b==2cls.b.b==4self.b.b==2self.__class__.b

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

评论

0赞 mirek 3/3/2020
类方法还有一件重要的事情:如果修改类方法中的属性,则此类中所有未显式设置此属性的现有对象都将具有修改后的值。
0赞 joel 8/14/2020
第一句话并不完全正确。StaticMethod 可以通过指定类名来访问公共类成员,就像任何调用方一样
2赞 Ale 8/1/2020 #27

在子类化时会出现一个非常重要的实际差异。如果你不介意,我会劫持@unutbu的例子:

class A: 
    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)

class B(A):
    pass

在 中,该方法知道它被调用到哪个类上:class_foo

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

在 中,无法确定它是被调用还是:static_fooAB

A.static_foo(1)
# => executing static_foo(1)
B.static_foo(1)
# => executing static_foo(1)

请注意,这并不意味着你不能在 中使用其他方法,你只需要直接引用该类,这意味着子类的 staticmethods 仍将引用父类:staticmethod

class A:
    @classmethod
    def class_qux(cls, x):
        print(f"executing class_qux({cls}, {x})")
    
    @classmethod
    def class_bar(cls, x):
        cls.class_qux(x)

    @staticmethod
    def static_bar(x):
        A.class_qux(x)

class B(A):
    pass

A.class_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
B.class_bar(1)
# => executing class_qux(<class '__main__.B'>, 1)
A.static_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
B.static_bar(1)
# => executing class_qux(<class '__main__.A'>, 1)
10赞 Milovan Tomašević 11/8/2020 #28

实例方法

+ 可以修改对象实例状态

+ 可以修改类状态

类方法

- 无法修改对象实例状态

+ 可以修改类状态

静态方法

- 无法修改对象实例状态

- 无法修改类状态

class MyClass:
    ''' 
    Instance method has a mandatory first attribute self which represent the instance itself. 
    Instance method must be called by a instantiated instance.
    '''
    def method(self):
        return 'instance method called', self
    
    '''
    Class method has a mandatory first attribute cls which represent the class itself. 
    Class method can be called by an instance or by the class directly. 
    Its most common using scenario is to define a factory method.
    '''
    @classmethod
    def class_method(cls):
        return 'class method called', cls
    
    '''
    Static method doesn’t have any attributes of instances or the class. 
    It also can be called by an instance or by the class directly. 
    Its most common using scenario is to define some helper or utility functions which are closely relative to the class.
    '''
    @staticmethod
    def static_method():
        return 'static method called'


obj = MyClass()
print(obj.method())
print(obj.class_method()) # MyClass.class_method()
print(obj.static_method()) # MyClass.static_method()

输出:

('instance method called', <__main__.MyClass object at 0x100fb3940>)
('class method called', <class '__main__.MyClass'>)
static method called

实例方法我们实际上可以访问对象实例,所以这是 my 类对象的实例,而使用 class 方法我们可以访问类本身。但不是任何对象,因为类方法并不真正关心对象的存在。但是,可以在对象实例上调用类方法和静态方法。这将起作用,它并没有真正产生影响,所以当你在这里调用静态方法时,它会起作用,它会知道你想调用哪个方法。

静态方法用于执行一些实用工具任务,类方法用于工厂方法。工厂方法可以返回不同用例的类对象。

最后,为了更好地理解,举一个简短的例子:

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

    @classmethod
    def get_from_string(cls, name_string: str):
        first_name, last_name = name_string.split()
        if Student.validate_name(first_name) and Student.validate_name(last_name):
            return cls(first_name, last_name)
        else:
            print('Invalid Names')

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


stackoverflow_student = Student.get_from_string('Name Surname')
print(stackoverflow_student.first_name) # Name
print(stackoverflow_student.last_name) # Surname
11赞 illuminato 1/17/2021 #29

Python 带有几个内置的装饰器。三巨头是:

@classmethod
@staticmethod
@property

首先,让我们注意,类的任何函数都可以用这个类的实例调用(在我们初始化这个类之后)。

@classmethod调用函数的方法,不仅可以作为类的实例,还可以直接由类本身作为其第一个参数。

@staticmethod 是一种将函数放入类中的方法(因为它在逻辑上属于类),同时指示它不需要访问类(因此我们不需要在函数定义中使用 self)。

让我们考虑以下类:

class DecoratorTest(object):

    def __init__(self):
        pass

    def doubler(self, x):
        return x*2

    @classmethod
    def class_doubler(cls, x): 
        """
        We need to use 'cls' instead of 'self'; 
        'cls' references the class instead of 
        an instance of the class
        """
        return x*2

    @staticmethod
    def static_doubler(x): 
        """
        No need to add 'self' here;
        static_doubler() could just be 
        a function outside the class.
        """
        return x*2

让我们看看它是如何工作的:

decor = DecoratorTest()

print(decor.doubler(5))
# 10

# a call with an instance of a class
print(decor.class_doubler(5)) 
# 10

# a direct call by the class itself
print(DecoratorTest.class_doubler(5)) 
# 10

# staticmethod can be called the same as classmethod.

# as an instance of the class
print(decor.static_doubler(5))
# 10

# or as a direct call 
print(DecoratorTest.static_doubler(5))
# 10

在这里,您可以看到这些方法的一些用例。

奖励:您可以在此处阅读有关装饰器的信息@property

2赞 Sia 2/3/2021 #30

顶级域名;

A 本质上是绑定到类(以及其实例)的函数staticmethod

A 本质上是可继承的。classmethodstaticmethod

有关详细信息,请参阅其他人的优秀答案。

2赞 Giorgos Myrianthous 2/5/2021 #31

首先,让我们从一个示例代码开始,我们将使用它来理解这两个概念:

class Employee:

    NO_OF_EMPLOYEES = 0
  
    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary
        self.increment_employees()

    def give_raise(self, amount):
        self.salary += amount

    @classmethod
    def employee_from_full_name(cls, full_name, salary):
        split_name = full_name.split(' ')
        first_name = split_name[0]
        last_name = split_name[1]
        return cls(first_name, last_name, salary)

    @classmethod
    def increment_employees(cls):
        cls.NO_OF_EMPLOYEES += 1

    @staticmethod
    def get_employee_legal_obligations_txt():
        legal_obligations = """
        1. An employee must complete 8 hours per working day
        2. ...
        """
        return legal_obligations

Class 方法

类方法接受类本身作为隐式参数,并(可选)接受定义中指定的任何其他参数。重要的是要了解类方法无权访问对象实例(如实例方法)。因此,类方法不能用于更改实例化对象的状态,而是能够更改在该类的所有实例之间共享的类状态。 当我们需要访问类本身时,类方法通常很有用——例如,当我们想要创建一个工厂方法时,该方法是一个创建类实例的方法。换句话说,类方法可以用作替代构造函数。

在我们的示例代码中,可以通过提供三个参数来构造一个实例; 和。Employeefirst_namelast_namesalary

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.first_name)
print(employee_1.salary)

'Andrew'
85000

现在,让我们假设有可能在单个字段中提供员工的姓名,其中名字和姓氏用空格分隔。在这种情况下,我们可以使用总共接受三个参数的类方法。第一个是类本身,它是一个隐式参数,这意味着在调用方法时不会提供它——Python 会自动为我们执行此操作:employee_from_full_name

employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(employee_2.first_name)
print(employee_2.salary)

'John'
95000

请注意,也可以从对象实例调用,尽管在此上下文中它没有多大意义:employee_from_full_name

employee_1 = Employee('Andrew', 'Brown', 85000)
employee_2 = employee_1.employee_from_full_name('John Black', 95000)

我们可能想要创建一个类方法的另一个原因是,当我们需要更改类的状态时。在我们的示例中,class 变量跟踪当前为公司工作的员工数量。每次创建 Employee 的新实例时都会调用此方法,并相应地更新计数:NO_OF_EMPLOYEES

employee_1 = Employee('Andrew', 'Brown', 85000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')
employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')

Number of employees: 1
Number of employees: 2

静态方法

另一方面,在静态方法中,实例(即 )和类本身(即 )都不会作为隐式参数传递。这意味着此类方法无法访问类本身或其实例。 现在有人可能会争辩说,静态方法在类的上下文中没有用,因为它们也可以放在帮助程序模块中,而不是将它们添加为类的成员。在面向对象编程中,将类结构化为逻辑块非常重要,因此,当我们需要在类下添加方法时,静态方法非常有用,因为它在逻辑上属于该类。 在我们的示例中,名为的静态方法仅返回一个字符串,其中包含公司每个员工的法律义务。此函数不与类本身交互,也不与任何实例交互。它可以被放置在不同的帮助程序模块中,但是,它只与这个类相关,因此我们必须把它放在 Employee 类下。selfclsget_employee_legal_obligations_txt

静态方法可以直接从类本身访问

print(Employee.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

或者从类的实例:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

引用

13赞 H.H 3/17/2021 #32

当有继承时,就会出现差异。

假设有两个类 -- 和 。如果要使用 ,方法应该写两次,因为类的名称应该写在打印行中。ParentChild@staticmethodprint_name

class Parent:
   _class_name = "Parent"

   @staticmethod
   def print_name():
       print(Parent._class_name)


class Child(Parent):
   _class_name = "Child"

   @staticmethod
   def print_name():
       print(Child._class_name)


Parent.print_name()
Child.print_name()

但是,对于 ,不需要编写两次方法。@classmethodprint_name

class Parent:
    _class_name = "Parent"

    @classmethod
    def print_name(cls):
        print(cls._class_name)


class Child(Parent):
    _class_name = "Child"


Parent.print_name()
Child.print_name()

评论

0赞 masih 7/29/2021
非常喜欢这个答案的简单性。Wish 本可以给它 100 票。
0赞 Arpan Saini 7/21/2021 #33

静态方法和类方法的定义来自它的文档。以及何时使用静态方法以及何时使用类方法。

  • 静态方法就像 java 和 C# 中的静态方法一样,它不会使用类的任何初始化值,它只需要从外部就可以正常工作。

  • class 方法:一般用于继承重写,当我们重写一个方法时,然后我们使用 CLS 实例来判断我们是要调用子类还是父类的方法。 如果您想同时使用具有相同名称和不同签名的方法。

staticmethod(function) -> 方法

Convert a function to be a static method.

A static method does not receive an implicit first argument.
To declare a static method, use this idiom:

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

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()).  The instance is ignored except for its class.

Static methods in Python are similar to those found in Java or C++.
For a more advanced concept, see the classmethod builtin.
"""

classmethod(function) -> 方法

Convert a function to be a class method.

A class method receives the class as implicit first argument,
just like an instance method receives the instance.
To declare a class method, use this idiom:

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

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()).  The instance is ignored except for its class.
If a class method is called for a derived class, the derived class
object is passed as the implied first argument.

Class methods are different than C++ or Java static methods.
If you want those, see the staticmethod builtin.
0赞 David Mendes 9/26/2022 #34

我想在之前的所有答案之上添加以下内容,这不是官方的,但符合标准。

首先,您可以考虑始终提供最少的必要权限。因此,如果您不需要特定于实例的内容,请将其设为类方法。如果不需要特定于类的内容,请将其设置为静态方法。

第二件事是考虑你可以通过你制作的方法类型来传达什么。静态方法 - 辅助函数,用于类本身之外。类函数 - 可以在不实例化的情况下调用,但只能与该类一起使用 - 否则将是一个静态方法!实例方法 - 仅供实例使用。

这可以帮助您传达模式以及应如何使用代码。

class Foo:
    @classmethod
    def bar(cls, id: int = None):
        query = session.query(
            a.id,
            a.name,
            a.address,
        )

        if id is not None:
            query = query.filter(a.id == id)

        return query

例如上面的 -- 没有理由说明方法栏不能是静态的。但是,通过使其成为类方法,您可以传达它应该由类本身使用,而不是将其用作其他地方的辅助函数!

请记住,以上不是官方的,而是我个人的喜好

2赞 Super Kai - Kazuya Ito 11/17/2022 #35

@classmethod比 更强大。@staticmethod

@classmethod:

  • 可以通过类名调用类变量和实例、类和静态方法,但不能通过实例变量调用。cls

  • 既可以按对象调用,也可以直接按类名调用。

  • 否则无法调用第一个参数的需要,并且 的名称在约定中使用,因此其他名称而不是仍然有效。cls@classmethodclscls

@staticmethod:

  • 既可以由对象调用,也可以直接通过类名调用。

  • 可以直接通过类名调用类变量和实例、类和静态方法,但不能通过实例变量调用。

  • 不需要或.selfcls

*详细来说,我还在回答 Python 中的“实例方法”是什么?

@classmethod:

例如,可以通过类名和直接通过类名调用类变量和实例、类和静态方法,也可以通过对象和直接类名调用,如下所示:@classmethodcls@classmethod

class Person:
    x = "Hello"
    def __init__(self, name):
        self.name = name
    
    @classmethod # Here
    def test1(cls):
        print(cls.x)   # Class variable by `cls`
        cls.test2(cls) # Instance method by `cls`
        cls.test3()    # Class method by `cls`
        cls.test4()    # Static method by `cls`
        print()
        print(Person.x)       # Class variable by class name
        Person.test2("Test2") # Instance method by class name
        Person.test3()        # Class method by class name
        Person.test4()        # Static method by class name
    
    def test2(self):
        print("Test2")
        
    @classmethod
    def test3(cls):
        print("Test3")
        
    @staticmethod
    def test4():
        print("Test4")

obj = Person("John")
obj.test1() # By object

# Or

Person.test1() # By class name

输出:

Hello
Test2
Test3
Test4

Hello
Test2
Test3
Test4

并且,不能通过类名和直接类名调用实例变量,因此,如果尝试通过类名和直接类名调用实例变量,如下所示:@classmethodcls@classmethodcls

# ...
    
    @classmethod
    def test1(cls):
        print(cls.name) # Instance variable by `cls`
        
        # Or

        print(Person.name) # Instance variable by class name
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

出现以下错误:

AttributeError:类型对象“Person”没有属性“name”

而且,如果没有:@classmethodcls

# ...
    
    @classmethod
    def test1(): # Without "cls"
        print("Test1")
  
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

@classmethod无法调用,则出现以下错误,如下所示:

TypeError:test1() 接受 0 个位置参数,但给出了 1 个

并且,名称在约定中使用,因此其他名称而不是仍然有效,如下所示:clscls

# ...

    @classmethod
    def test1(orange):
        print(orange.x)      # Class variable
        orange.test2(orange) # Instance method
        orange.test3()       # Class method
        orange.test4()       # Static method

# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

输出:

Hello
Test2
Test3
Test4

@staticmethod:

例如,既可以按对象调用,也可以直接按类名调用,如下所示:@staticmethod

class Person:
    x = "Hello"
    def __init__(self, name):
        self.name = name

    @staticmethod # Here
    def test1():
        print("Test1")
    
    def test2(self):
        print("Test2")
        
    @classmethod
    def test3(cls):
        print("Test3")
        
    @staticmethod
    def test4():
        print("Test4")

obj = Person("John")
obj.test1() # By object

# Or

Person.test1() # By class name

输出:

Test1

并且,可以直接通过类名调用类变量和实例、类和静态方法,而不是实例变量,如下所示:@staticmethod

# ...
    
    @staticmethod
    def test1():
        print(Person.x)       # Class variable
        Person.test2("Test2") # Instance method
        Person.test3()        # Class method
        Person.test4()        # Static method
            
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

输出:

Hello
Test2
Test3
Test4

并且,如果尝试调用实例变量,如下所示:@staticmethod

# ...
    
    @staticmethod
    def test1():
        print(Person.name) # Instance variable
            
# ...

obj = Person("John")
obj.test1()

# Or

Person.test1()

出现以下错误:

AttributeError:类型对象“Person”没有属性“name”

并且,如果 has 或 ,则不需要 or so ,您需要传递一个参数,如下所示:@staticmethodselfcls@staticmethodselfcls

# ...
    
    @staticmethod
    def test1(self): # With "self"
        print(self)

    # Or

    @staticmethod
    def test1(cls): # With "cls"
        print(cls)

# ...

obj = Person("John")
obj.test1("Test1") # With an argument

# Or

Person.test1("Test1") # With an argument

输出:

Test1

否则,如果您不传递如下所示的参数:

# ...
    
    @staticmethod
    def test1(self): # With "self"
        print("Test1")

    # Or

    @staticmethod
    def test1(cls): # With "cls"
        print("Test1")

# ...

obj = Person("John")
obj.test1() # Without an argument

# Or

Person.test1() # Without an argument

出现以下错误:

TypeError:test1() 缺少 1 个必需的位置参数:“self”

TypeError:test1() 缺少 1 个必需的位置参数:“cls”

1赞 Thrastylon 5/17/2023 #36

没有特别的上行空间。 如果您要从头开始编写代码库,并希望倾向于简单性和一致性,请坚持使用 .@staticmethod@classmethod@classmethod

如果你不信任我,那么 Python 的创造者呢?

老实说,静态方法是一个错误——我试图 做一些类似 Java 类方法的事情,但一旦它发布,我发现 真正需要的是类方法。但为时已晚 的 static方法。

— Guido van Rossum,RE:静态方法的良好用途