ChatGPT解决这个技术问题 Extra ChatGPT

@staticmethod 和 @classmethod 之间的区别

@staticmethod 装饰的函数和用 @classmethod 装饰的函数有什么区别?

为了简洁起见,静态方法有时最好作为 python 中的模块级函数。使用模块功能可以更轻松地仅导入您需要的功能并防止不必要的“。”语法(我在看你的 Objective-C)。类方法有更多用途,因为它们可以与多态性结合使用以创建“工厂模式”函数。这是因为类方法接收类作为隐式参数。
tl;dr >> 与普通方法相比,静态方法和类方法也可以使用类访问,但与类方法不同,静态方法通过继承是不可变的。
Raymond Hettinger 就该主题发表的相关演讲:youtube.com/watch?v=HTLu2DFOdTg
更精确的 youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 你只需要 classmethod 来替代构造函数。否则,您可以使用 staticmethod 并通过以某种方式提供更多信息的实际“CLASSNAME”而不是像 classmethod 中的 cls 访问任何类属性(通过 ./dot)

M
Mike T

也许一些示例代码会有所帮助:注意 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)

使用类方法,对象实例的类作为第一个参数而不是 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)

人们发现类方法的一种用途是创建 inheritable alternative constructors

使用静态方法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>

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

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

我不明白使用静态方法有什么问题。我们可以只使用一个简单的类外函数。
@Alcott:您可能希望将一个函数移动到一个类中,因为它在逻辑上属于该类。在 Python 源代码(例如 multiprocessing、turtle、dist-packages)中,它用于从模块命名空间中“隐藏”单下划线“私有”函数。然而,它的使用高度集中在几个模块中——也许表明它主要是一种风格。虽然我找不到这方面的任何示例,但 @staticmethod 可以通过被子类覆盖来帮助组织您的代码。没有它,您将在模块命名空间中浮动函数的变体。
@Alcott:正如unutbu所说,静态方法是一种组织/风格特征。有时一个模块有很多类,并且一些辅助函数在逻辑上与一个给定的类绑定,而不是与其他类绑定,因此不要用许多“自由函数”“污染”模块是有意义的,最好使用静态方法比依靠在代码中混合类和函数定义的糟糕风格只是为了表明它们是“相关的”
当然,上述对奥尔科特评论的回应也错过了显而易见的……您可以在子类中覆盖静态方法和类方法。此时classmethod和staticmethod之间的区别就变得极其重要了。如果一个静态方法调用另一个静态方法,它应该是调用cls.some_static_method()的类方法,以便子类可以轻松替换静态。
@Alcott Staticmethod 定义了一个函数,但它还做了一件事——它使函数成为一个类变量。如果您碰巧想要一个作为函数(而不是方法)的类变量,并且不想将该函数用于与类无关的任何事情,那么 staticmethod 是惯用的方法。
B
Brian Burns

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

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

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

静态方法不是无用的——它是一种将函数放入类的方法(因为它在逻辑上属于那里),同时表明它不需要访问类。
因此,只有“基本上”没用。这种组织以及依赖注入是静态方法的有效用途,但由于模块而不是 Java 中的类是 Python 中代码组织的基本元素,因此它们的使用和用处很少。
当与类或其实例无关时,在类中定义方法有什么逻辑?
或许是为了传承?静态方法可以像实例方法和类方法一样被继承和覆盖,并且查找按预期工作(与 Java 不同)。无论是在类还是实例上调用,静态方法都不是真正静态解析的,因此类和静态方法之间的唯一区别是隐式的第一个参数。
它们还创建了一个更干净的命名空间,并且更容易理解函数与类有关。
T
Tadeck

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


m
me_and

官方python文档:

@classmethod

类方法接收类作为隐式第一个参数,就像实例方法接收实例一样。要声明一个类方法,请使用以下习惯用法: class C: @classmethod def f(cls, arg1, arg2, ...): ... @classmethod 形式是一个函数装饰器——参见函数定义中函数定义的描述详情。它可以在类(例如 Cf())或实例(例如 C().f())上调用。该实例被忽略,除了它的类。如果为派生类调用类方法,则派生类对象作为隐含的第一个参数传递。类方法不同于 C++ 或 Java 静态方法。如果您需要这些,请参阅本节中的 staticmethod()。

@staticmethod

静态方法不接收隐式的第一个参数。要声明一个静态方法,请使用这个习惯用法: class C: @staticmethod def f(arg1, arg2, ...): ... @staticmethod 形式是一个函数装饰器——详见函数定义中函数定义的描述.它可以在类(例如 Cf())或实例(例如 C().f())上调用。该实例被忽略,除了它的类。 Python 中的静态方法类似于 Java 或 C++ 中的静态方法。有关更高级的概念,请参阅本节中的 classmethod()。


文档中没有错误吗?不应该在 staticmethod:“实例及其类都被忽略。”而不是“实例被忽略,除了它的类。”?
这可能是一个剪切和粘贴错误,但严格来说,如果您忽略该类,您将无法调用该类的方法。
D
Du D.

要决定是使用 @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

classmethod 和 cls._counter 与 staticmethod 和 Apple._counter 的优势是什么
即使将代码放在不同的类中或更改了类名,cls._counter 仍然是 cls._counterApple._counter 特定于 Apple 类;对于不同的类,或者当类名更改时,您需要更改引用的类。
C
Community

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

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


那么这是否意味着通过使用静态方法我总是绑定到父类并且使用类方法我绑定了我在其中声明类方法的类(在这种情况下是子类)?
不。通过使用静态方法,您根本不受约束。没有隐式的第一个参数。通过使用 classmethod,您可以将调用方法的类(如果直接在类上调用)或调用方法的实例的类(如果在实例上调用)作为隐式第一个参数。
可以扩展一点以表明,通过将类作为第一个参数,类方法可以直接访问其他类属性和方法,而静态方法则不能(他们需要为此硬编码 MyClass.attr)
“它的定义通过继承是不可变的。”在 Python 中没有任何意义,您可以很好地覆盖静态方法。
C
Community

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.maketrans,它是从 Python 3 中的 string 模块移来的。它使转换表适合 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'>

有关替代构造函数的其他类似示例,请参阅 pandas source code,另请参阅有关 classmethodstaticmethod 的官方 Python 文档。


T
Tsingfeng Yang

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

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

假设我们有一个 Person 类,它接受两个参数 first_namelast_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")

这是一篇旧文章,但实现构造函数接受一个或两个参数的更 Pythonic 方法是使用 def __init__(self, first_name, last_name="") 而不是类方法 get_person。在这种情况下,结果也将完全相同。
b
blue_note

只有第一个参数不同:

普通方法:当前对象自动作为(附加)第一个参数传递

classmethod:当前对象的类自动作为(附加)第一个参数传递

staticmethod:不会自动传递额外的参数。你传递给函数的就是你得到的。

更详细...

正常方法

“标准”方法,就像在每个面向对象的语言中一样。当一个对象的方法被调用时,它会自动获得一个额外的参数 self 作为它的第一个参数。也就是方法

def f(self, x, y)

必须用 2 个参数调用。 self 是自动传递的,它是对象本身。类似于神奇地出现在例如 this 中。 java/c++,只有在 python 中才会显式显示。

实际上,第一个参数不必称为 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)


这是上面一堆中最好的答案。简洁明了的例子很清楚。其他示例省略了一些细节。
这是唯一可以很好地解释这个概念的答案。我在 python 上为 apis 学习 graphql,有些使用 classmethod,而其他人在他们的类突变下使用 staticmethod。这让我明白了为什么它们被这样使用,而这确实为我的好奇心提供了答案。谢谢你
j
joel

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

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

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


这个问题提到了私有类成员的访问。我想强调一下(取决于您对私有的定义),@staticmethod@classmethod 具有不同的访问级别。前者不应该访问类私有类成员
J
Jonathan B.

静态方法:

没有 self 参数的简单函数。

处理类属性;不在实例属性上。

可以通过类和实例调用。

内置函数 staticmethod() 用于创建它们。

静态方法的好处:

它在类作用域中本地化函数名称

它将功能代码移到更靠近使用它的位置

与模块级函数相比,导入更方便,因为不必专门导入每个方法 @staticmethod def some_static_method(*args, **kwds): pass

类方法:

具有第一个参数作为类名的函数。

可以通过类和实例调用。

这些是使用 classmethod 内置函数创建的。 @classmethod def some_class_method(cls, *args, **kwds): 通过


J
Jens Timmerman

在 python 2.4 中添加了@decorators 如果你使用的是 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。 classmethod 只需要关于类的信息,不需要对象的实例。

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


P
Pang

让我先说说用@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',)

A
Armin Ronacher

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

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

The definitive guide on how to use static, class or abstract methods in Python 是该主题的一个很好的链接,并将其总结如下。

@staticmethod 函数只不过是在类中定义的函数。它可以在不先实例化类的情况下调用。它的定义通过继承是不可变的。

Python 不必为对象实例化绑定方法。

它简化了代码的可读性,并且不依赖于对象本身的状态;

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

工厂方法,用于使用例如某种预处理为类创建实例。

静态方法调用静态方法:如果将一个静态方法拆分为多个静态方法,则不应硬编码类名,而应使用类方法


谢谢@zangw - 静态函数的继承不变性似乎是关键区别
A
Adam Parkin

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

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() 方法)。

对我来说,这稍微违反了 open/closed principle,因为在 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

另外:历史记录:Guido Van Rossum(Python 的创建者)曾将 staticmethod 称为“意外”:https://mail.python.org/pipermail/python-ideas/2012-May/014969.html

我们都知道静态方法有多么有限。 (它们基本上是一个意外——在 Python 2.2 的日子里,当我发明新型类和描述符时,我打算实现类方法,但一开始我不理解它们,不小心先实现了静态方法。然后它删除它们为时已晚,只提供类方法。

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

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


R
Rizwan Mumtaz

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

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

- 我们可以直接调用静态和类方法,无需初始化

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

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

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

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


i
illuminato

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' reference to the class instead of an instance of the class
        return x*2

    @staticmethod
    def static_doubler(x): # no need adding 'self' here; static_doubler() could be just a function not inside the class
        return x*2

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

decor = DecoratorTest()

print(decor.doubler(5))
# 10

print(decor.class_doubler(5)) # a call with an instance of a class
# 10
print(DecoratorTest.class_doubler(5)) # a direct call by the class itself
# 10

# staticmethod could be called in the same way as classmethod.
print(decor.static_doubler(5)) # as an instance of the class
# 10
print(DecoratorTest.static_doubler(5)) # or as a direct call 
# 10

Here您可以看到这些方法的一些用例。

奖励:您可以阅读@property装饰器here


M
Milovan Tomašević

实例方法:

+ 可以修改对象实例状态

+ 可以修改类状态

上课方法:

- 无法修改对象实例状态

+ 可以修改类状态

静态方法:

- 无法修改对象实例状态

- 不能修改类状态

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

我们实际上可以访问对象实例的实例方法,所以这是我的类对象之外的一个实例,而使用类方法我们可以访问类本身。但不是任何对象,因为类方法并不真正关心对象是否存在。但是,您可以在对象实例上调用类方法和静态方法。这会起作用,它并没有真正的区别,所以当你在这里调用静态方法时,它会再次起作用,它会知道你想调用哪个方法。

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

最后,一个简短的例子可以更好地理解:

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

H
H.H

存在继承时会发生差异。

假设有两个类——Parent 和 Child。如果要使用@staticmethod,print_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()

但是,对于@classmethod,不需要编写两次 print_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()

非常喜欢这个答案的简单性。 Wish本可以给它100票。
v
vijay

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

@static 方法:与关联的类或实例无关...但是为了可读性可以使用静态方法


M
Michael Swartz

我的贡献展示了 @classmethod@staticmethod 和实例方法之间的区别,包括实例如何间接调用 @staticmethod。但是,与其从实例间接调用 @staticmethod,不如将其设为私有可能更“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)
'''
"""

N
Nicolae Petridean

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

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

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

希望我很清楚!


D
David Schumann

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

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

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

unutbus answer很好地涵盖了其他情况。


T
Tushar Vazirani

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

方法并不总是做出改变
J
Jacky1205

我认为提供 staticmethodclassmethod 的纯 Python 版本将有助于理解它们在语言级别上的区别。

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

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

A
AbstProcDo

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

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

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


G
Giorgos Myrianthous

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

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

类方法

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

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

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

'Andrew'
85000

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

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)

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

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

静态方法

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

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

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

参考

Python中的静态方法和类方法有什么区别?


A
Ale

子类化时会出现一个非常重要的实际差异。如果你不介意,我会劫持@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_foo 中,无法确定是在 A 还是 B 上调用它:

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

请注意,这并不意味着您不能在 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)

S
Sia

tldr;

staticmethod 本质上是一个绑定到一个类(以及它的实例)的函数

classmethod 本质上是可继承的 staticmethod

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