【问题标题】:When should I use @classmethod and when def method(self)?我应该什么时候使用@classmethod,什么时候使用def method(self)?
【发布时间】:2012-05-22 03:23:42
【问题描述】:

在集成一个我以前没有使用过的 Django 应用程序时,我发现了两种不同的方法来定义类中的函数。作者似乎既独特又有意地使用它们。第一个是我自己经常使用的:

class Dummy(object):

    def some_function(self, *args, **kwargs):
        # do something here
        # self is the class instance

另一个是我从不使用的,主要是因为我不明白什么时候使用它:

class Dummy(object):

    @classmethod
    def some_function(cls, *args, **kwargs):
        # do something here
        # cls refers to what?

python 文档中的 classmethod 装饰器说:

类方法接收类作为隐含的第一个参数,只是 就像一个实例方法接收实例一样。

所以我猜cls 指的是Dummy 本身(class,而不是实例)。我不完全明白为什么会这样,因为我总是可以这样做:

type(self).do_something_with_the_class

这只是为了清楚起见,还是我错过了最重要的部分:没有它就无法完成的怪异和迷人的事情?

【问题讨论】:

    标签: python language-lawyer class-method method-dispatch language-details


    【解决方案1】:

    您的猜测是正确的 - 您了解 classmethods 的工作原理。

    为什么这些方法既可以在实例上也可以在类上调用(在这两种情况下,类对象都将作为第一个参数传递):

    class Dummy(object):
    
        @classmethod
        def some_function(cls,*args,**kwargs):
            print cls
    
    #both of these will have exactly the same effect
    Dummy.some_function()
    Dummy().some_function()
    

    关于在实例上使用这些:在实例上调用类方法至少有两个主要用途:

    1. self.some_function() 将在self 的实际类型上调用some_function 的版本,而不是恰好出现该调用的类(如果类被重命名,则不需要注意);和
    2. 如果some_function 是实现某些协议所必需的,但对于单独调用类对象很有用。

    staticmethod的区别:还有另一种定义不访问实例数据的方法的方法,称为staticmethod。这会创建一个根本不接收隐式第一个参数的方法;因此,它不会传递有关调用它的实例或类的任何信息。

    In [6]: class Foo(object): some_static = staticmethod(lambda x: x+1)
    
    In [7]: Foo.some_static(1)
    Out[7]: 2
    
    In [8]: Foo().some_static(1)
    Out[8]: 2
    
    In [9]: class Bar(Foo): some_static = staticmethod(lambda x: x*2)
    
    In [10]: Bar.some_static(1)
    Out[10]: 2
    
    In [11]: Bar().some_static(1)
    Out[11]: 2
    

    我发现它的主要用途是将现有函数(不希望收到self)改编为类(或对象)上的方法。

    【讨论】:

    • 不错的一个:我喜欢将答案分为如何 - 为什么。据我所知,@classmethod 允许在不需要实例的情况下访问该函数。这正是我想要的,谢谢。
    • @marcin 真正的鸭式打字确实给了它一些其他维度。更多的是不写self.foo()之类的东西,而应该是Bar.foo()
    • @Voo 实际上,self.foo() 更可取,因为self 可能是实现自己的foo 的子类的实例。
    • @Marcin 我认为这取决于您想要实现的目标。但我明白你的意思。
    • 您应该为 Bar 使用不同的 lambda,如 1+1 == 2 == 1*2,因此无法从显示的结果中判断出实际调用了 Bar().static_method
    【解决方案2】:

    classmethod 在 Python 中最常见的用途之一是工厂,这是构建对象的最有效方法之一。因为classmethods 和staticmethods 一样,不需要构造类实例。 (但是如果我们使用staticmethod,我们将不得不在函数中硬编码实例类名)

    这个博客很好地解释了它: https://iscinumpy.gitlab.io/post/factory-classmethods-in-python/

    【讨论】:

      【解决方案3】:

      基本上,当您意识到方法的定义不会被更改或覆盖时,您应该使用@classmethod。

      另外:理论上,类方法比对象方法更快,因为不需要实例化并且需要更少的内存。

      【讨论】:

        【解决方案4】:

        如果你添加装饰器@classmethod,这意味着你将把该方法作为java或C++的静态方法。 (静态方法是一个通用术语我猜;)) Python 也有@staticmethod。而classmethod和staticmethod的区别在于是否可以 使用参数或类名本身访问类或静态变量。

        class TestMethod(object):
            cls_var = 1
            @classmethod
            def class_method(cls):
                cls.cls_var += 1
                print cls.cls_var
        
            @staticmethod
            def static_method():
                TestMethod.cls_var += 1
                print TestMethod.cls_var
        #call each method from class itself.
        TestMethod.class_method()
        TestMethod.static_method()
        
        #construct instances
        testMethodInst1 = TestMethod()    
        testMethodInst2 = TestMethod()   
        
        #call each method from instances
        testMethodInst1.class_method()
        testMethodInst2.static_method()
        

        所有这些类都将 cls.cls_var 加 1 并打印出来。

        并且每个在相同范围内使用相同名称的类或使用这些类构造的实例都将共享这些方法。 只有一个 TestMethod.cls_var 而且只有一个 TestMethod.class_method() , TestMethod.static_method()

        还有一个重要的问题。为什么需要这些方法。

        classmethod 或 staticmethod 在您将该类作为工厂时很有用 或者当你只需要初始化你的类一次时。就像打开文件一次,并使用feed方法逐行读取文件。

        【讨论】:

        • (a) 静态方法是另外一回事; (b) 类方法并非由每个类共享。
        • @Marcin 感谢您让我知道我不清楚的定义等等。
        猜你喜欢
        • 2021-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-22
        • 1970-01-01
        • 2023-04-02
        相关资源
        最近更新 更多