我正在自学Python,我最近的一课是Python不是Java,所以我刚刚花了一段时间把我所有的Class方法变成了函数。

我现在意识到,我不需要使用Class方法来做我在Java中使用静态方法所做的事情,但现在我不确定什么时候我会使用它们。我能找到的所有关于Python类方法的建议都是,像我这样的新手应该避开它们,而标准文档在讨论它们时是最不透明的。

谁有一个在Python中使用类方法的好例子,或者至少有人能告诉我什么时候可以合理地使用类方法吗?


当前回答

我从Ruby学到的是,所谓的类方法和所谓的实例方法只是一个函数,它的第一个参数具有语义意义,当函数作为对象的方法(即obj.meth())调用时,该参数会被无声地传递。

通常,该对象必须是一个实例,但@classmethod方法装饰器更改规则以传递一个类。你可以在一个实例上调用一个类方法(它只是一个函数)-第一个参数将是它的类。

因为它只是一个函数,所以在任何给定的作用域(即类定义)中只能声明一次。因此,对于ruby开发者来说,你不能让一个类方法和一个实例方法同名。

考虑一下:

class Foo():
  def foo(x):
    print(x)

你可以在实例上调用foo

Foo().foo()
<__main__.Foo instance at 0x7f4dd3e3bc20>

但不是在课堂上:

Foo.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)

现在添加@classmethod:

class Foo():
  @classmethod
  def foo(x):
    print(x)

调用一个实例现在会通过它的类:

Foo().foo()
__main__.Foo

调用类也一样:

Foo.foo()
__main__.Foo

按照惯例,我们在实例方法中使用self作为第一个参数,在类方法中使用cls。我在这里用这两个都不是来说明它只是一个论点。在Ruby中,self是一个关键字。

与Ruby的对比:

class Foo
  def foo()
    puts "instance method #{self}"
  end
  def self.foo()
    puts "class method #{self}"
  end
end

Foo.foo()
class method Foo

Foo.new.foo()
instance method #<Foo:0x000000020fe018>

Python类方法只是一个装饰函数,您可以使用相同的技术来创建自己的装饰器。修饰过的方法包装了真正的方法(在@classmethod的情况下,它传递了额外的类参数)。底层的方法仍然存在,隐藏但仍然可以访问。


脚注:在类和实例方法之间的名称冲突引起了我的好奇心之后,我写了这篇文章。我不是Python专家,如果有任何错误,我希望得到评论。

其他回答

工厂方法(替代构造函数)确实是类方法的经典例子。

基本上,类方法适用于任何您希望有一个方法自然地适合类的名称空间,但不与类的特定实例相关联的时候。

例如,在excellent unipath模块中:

当前目录

Path.cwd () 返回实际的当前目录;例如,路径(“/ tmp / my_temp_dir”)。这是一个类方法。 .chdir () 使self为当前目录。

由于当前目录是进程范围的,cwd方法没有应该与之关联的特定实例。但是,将cwd更改为给定Path实例的目录确实应该是一个实例方法。

嗯…因为Path.cwd()确实返回了一个Path实例,我猜它可以被认为是一个工厂方法…

诚实?我从未发现staticmethod或classmethod的用途。我还没有看到一个操作不能使用全局函数或实例方法来完成。

如果python像Java那样使用private和protected成员,情况将有所不同。在Java中,我需要一个静态方法来访问实例的私有成员来做一些事情。在Python中,很少需要这样做。

通常,我看到人们使用静态方法和类方法,而他们真正需要做的只是更好地使用python的模块级名称空间。

我以前用过PHP,最近我在问自己,这个类方法是怎么回事?Python手册是非常技术性的,非常简短的文字,所以它不会帮助理解这个功能。我一直在谷歌上搜索,然后我找到了答案——> http://code.anjanesh.net/2007/12/python-classmethods.html。

如果你懒得点击它。我的解释很简短。:)

在PHP中(也许不是所有人都知道PHP,但是这个语言非常直接,每个人都应该明白我在说什么),我们有这样的静态变量:


class A
{

    static protected $inner_var = null;

    static public function echoInnerVar()
    {
        echo self::$inner_var."\n";
    }

    static public function setInnerVar($v)
    {
        self::$inner_var = $v;
    }

}

class B extends A
{
}

A::setInnerVar(10);
B::setInnerVar(20);

A::echoInnerVar();
B::echoInnerVar();

在这两种情况下,输出都是20。

但是在python中,我们可以添加@classmethod装饰器,这样就可以分别输出10和20。例子:


class A(object):
    inner_var = 0

    @classmethod
    def setInnerVar(cls, value):
        cls.inner_var = value

    @classmethod
    def echoInnerVar(cls):
        print cls.inner_var


class B(A):
    pass


A.setInnerVar(10)
B.setInnerVar(20)

A.echoInnerVar()
B.echoInnerVar()

聪明,不是吗?

类方法提供了“语义糖”(不知道这个术语是否被广泛使用)——或者“语义便利”。

例如:你有一组表示对象的类。你可能想让类方法all()或find()写User.all()或User.find(firstname='Guido')。当然,这可以使用模块级函数来实现……

这是一个有趣的话题。我对它的理解是,python的classmethod操作起来像一个单例而不是一个工厂(它返回一个类的生成实例)。它是单例的原因是存在一个公共对象(字典),但只为类生成一次,但由所有实例共享。

为了说明这一点,这里有一个例子。注意,所有实例都有一个对单个字典的引用。这不是我理解的工厂模式。这可能是python独有的。

class M():
 @classmethod
 def m(cls, arg):
     print "arg was",  getattr(cls, "arg" , None),
     cls.arg = arg
     print "arg is" , cls.arg

 M.m(1)   # prints arg was None arg is 1
 M.m(2)   # prints arg was 1 arg is 2
 m1 = M()
 m2 = M() 
 m1.m(3)  # prints arg was 2 arg is 3  
 m2.m(4)  # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
 M.m(5)   # prints arg was 4 arg is 5