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

2024-11-21 08:33:00
admin
原创
5
摘要:问题描述:用装饰的@staticmethod方法和用 装饰的方法有什么区别@classmethod?解决方案 1:也许一些示例代码会有所帮助:请注意和 的foo调用签名之间的区别:class_foo`static_foo`class A(object): def foo(self, x): ...

问题描述:

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


解决方案 1:

也许一些示例代码会有所帮助:请注意和 的foo调用签名之间的区别:class_foo`static_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)

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


使用 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>

解决方案 2:

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

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

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

解决方案 3:

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

解决方案 4:

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

解决方案 5:

官方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()本节。

解决方案 6:

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

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

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

解决方案 7:

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.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 源代码,另请参阅classmethod和的官方 Python 文档staticmethod

解决方案 8:

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

类方法:与 Java 和 C++ 不同,Python 没有构造函数重载。因此,要实现这一点,您可以使用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")

解决方案 9:

只有第一个参数不同

  • 正常方法:当前对象自动作为(附加的)第一个参数传递

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

解决方案 10:

我认为更好的问题是“你什么时候使用@classmethodvs @staticmethod?”

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

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

解决方案 11:

静态方法:

  • 没有自身参数的简单函数。

  • 针对类属性进行操作;而不是针对实例属性。

  • 可以通过类和实例调用。

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

静态方法的好处:

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

  • 它将函数代码移到更靠近使用位置的地方

  • 与模块级函数相比,导入更方便,因为每个方法不需要专门导入

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

类方法:

  • 以类名作为第一个参数的函数。

  • 可以通过类和实例调用。

  • 这些是用 classmethod 内置函数创建的。

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

解决方案 12:

@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是,子类可以决定更改它的实现,可能是因为它非常通用并且可以处理多种类型的集群,所以仅检查类的名称是不够的。

解决方案 13:

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

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

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

因此,从某种意义上说,静态方法并不与类本身绑定,而只是因为它可能具有相关功能而存在。

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

解决方案 14:

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

解决方案 15:

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

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

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

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

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

  • 工厂方法,用于通过某种预处理等方式为类创建实例。

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

解决方案 16:

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

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()是的话,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's 称为“意外”: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 类方法的事情,但它发布后,我发现真正需要的是 classmethod。但摆脱 staticmethod 已经太晚了。

解决方案 17:

当存在继承时就会出现差异。

假设有两个类——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()

但是,对于@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()

解决方案 18:

实例方法

+ 可以修改对象实例状态

+ 可以修改班级状态

类方法

- 无法修改对象实例状态

+ 可以修改班级状态

静态方法

- 无法修改对象实例状态

- 无法修改班级状态

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

解决方案 19:

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

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-静态方法不能调用自身方法,但可以调用其他静态方法和类方法

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

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

解决方案 20:

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阅读有关装饰器的内容

解决方案 21:

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

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

解决方案 22:

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

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

作为指导原则,使用静态方法作为实用程序,使用类方法(例如工厂)。或者定义一个单例。并使用实例方法来模拟实例的状态和行为。

希望我说清楚了!

解决方案 23:

@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() + '
')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '
')
''' 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() + '
')
''' 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() + '
')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '
')
'''  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() + '
')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

解决方案 24:

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

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

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

其他情况已在unutbus的回答中详尽阐述。

解决方案 25:

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

解决方案 26:

我认为给出纯 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

解决方案 27:

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

类的普通方法是一种隐式动态方法,其第一个参数是实例。

相反,静态方法不以实例为第一个参数,因此被称为“静态”

静态方法确实是一种普通函数,与类定义之外的函数相同。

幸运的是,它被分组到类中只是为了更接近它被应用的地方,否则您可能需要滚动鼠标才能找到它。

解决方案 28:

首先,让我们从一个示例代码开始,以理解这两个概念:

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_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 中的静态方法和类方法有什么区别?

解决方案 29:

@classmethod比 更强大@staticmethod

@classmethod

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

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

  • 需要cls第一个参数,否则@classmethod无法调用,并且的名称cls是按照惯例使用的,因此其他名称cls仍然可以使用。

@staticmethod

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

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

  • 不需要selfcls

*详细地,我在回答Python 中的“实例方法”是什么?时也解释了实例方法。

@classmethod

例如,可以通过类名或直接通过类名@classmethod调用类变量、实例、类和静态方法,也可以通过对象或直接通过类名调用,如下所示:cls`@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

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

# ...
    
    @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’

并且,如果@classmethod没有cls

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

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

# Or

Person.test1()

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

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

并且, 的名称cls是按照惯例使用的,因此其他名称代替cls仍然可以使用,如下所示:

# ...

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

并且,@staticmethod不需要selfcls所以如果@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'

解决方案 30:

一个非常重要的实际区别发生在子类化时。如果你不介意的话,我会劫持@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)
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用