标签归档:class-method

在Python中调用基类的类方法

问题:在Python中调用基类的类方法

考虑以下代码:

class Base(object):

    @classmethod
    def do(cls, a):
        print cls, a

class Derived(Base):

    @classmethod
    def do(cls, a):
        print 'In derived!'
        # Base.do(cls, a) -- can't pass `cls`
        Base.do(a)

if __name__ == '__main__':
    d = Derived()
    d.do('hello')

> $ python play.py  
> In derived! 
> <class '__main__.Base'> msg

从哪里来Derived.do,我怎么打电话Base.do

super如果这是一个普通的对象方法,通常我会直接使用甚至直接使用基类名称,但是显然我找不到在基类中调用类方法的方法。

在上面的示例中,Base.do(a)打印Baseclass而不是Derivedclass。

Consider the following code:

class Base(object):

    @classmethod
    def do(cls, a):
        print cls, a

class Derived(Base):

    @classmethod
    def do(cls, a):
        print 'In derived!'
        # Base.do(cls, a) -- can't pass `cls`
        Base.do(a)

if __name__ == '__main__':
    d = Derived()
    d.do('hello')

> $ python play.py  
> In derived! 
> <class '__main__.Base'> msg

From Derived.do, how do I call Base.do?

I would normally use super or even the base class name directly if this is a normal object method, but apparently I can’t find a way to call the classmethod in the base class.

In the above example, Base.do(a) prints Base class instead of Derived class.


回答 0

如果您使用的是新样式的类(例如,是从objectPython 2 派生的,或者总是在Python 3中派生的),则可以这样操作super()

super(Derived, cls).do(a)

这是您如何在基类版本的方法(即print cls, a)中从派生类调用代码并将cls其设置为派生类的方式。

If you’re using a new-style class (i.e. derives from object in Python 2, or always in Python 3), you can do it with super() like this:

super(Derived, cls).do(a)

This is how you would invoke the code in the base class’s version of the method (i.e. print cls, a), from the derived class, with cls being set to the derived class.


回答 1

这已经有一段时间了,但是我想我可能已经找到了答案。当您装饰一个方法成为类方法时,原始的未绑定方法存储在名为“ im_func”的属性中:

class Base(object):
    @classmethod
    def do(cls, a):
        print cls, a

class Derived(Base):

    @classmethod
    def do(cls, a):
        print 'In derived!'
        # Base.do(cls, a) -- can't pass `cls`
        Base.do.im_func(cls, a)

if __name__ == '__main__':
    d = Derived()
    d.do('hello')

this has been a while, but I think I may have found an answer. When you decorate a method to become a classmethod the original unbound method is stored in a property named ‘im_func’:

class Base(object):
    @classmethod
    def do(cls, a):
        print cls, a

class Derived(Base):

    @classmethod
    def do(cls, a):
        print 'In derived!'
        # Base.do(cls, a) -- can't pass `cls`
        Base.do.im_func(cls, a)

if __name__ == '__main__':
    d = Derived()
    d.do('hello')

回答 2

这对我有用:

Base.do('hi')

This works for me:

Base.do('hi')

类方法的目的是什么?

问题:类方法的目的是什么?

我正在自学Python,最近的类是Python不是Java,因此我花了一段时间将所有Class方法都转换为函数。

我现在意识到我不需要使用Class方法来完成staticJava中的方法,但是现在我不确定何时使用它们。我可以找到的有关Python类方法的所有建议都是像我这样的新手提出的,应该避免使用它们,并且在讨论它们时,标准文档最为模糊。

有没有人有在Python中使用Class方法的好例子,或者至少有人可以告诉我何时可以明智地使用Class方法?

I’m teaching myself Python and my most recent lesson was that Python is not Java, and so I’ve just spent a while turning all my Class methods into functions.

I now realise that I don’t need to use Class methods for what I would done with static methods in Java, but now I’m not sure when I would use them. All the advice I can find about Python Class methods is along the lines of newbies like me should steer clear of them, and the standard documentation is at its most opaque when discussing them.

Does anyone have a good example of using a Class method in Python or at least can someone tell me when Class methods can be sensibly used?


回答 0

类方法用于需要不特定于任何特定实例但仍以某种方式涉及到类的方法。关于它们的最有趣的事情是它们可以被子类覆盖,这在Java的静态方法或Python的模块级函数中根本是不可能的。

如果您有一个类MyClass,以及一个在MyClass上运行的模块级函数(工厂,依赖项注入存根等),请将其设置为classmethod。然后它将对子类可用。

Class methods are for when you need to have methods that aren’t specific to any particular instance, but still involve the class in some way. The most interesting thing about them is that they can be overridden by subclasses, something that’s simply not possible in Java’s static methods or Python’s module-level functions.

If you have a class MyClass, and a module-level function that operates on MyClass (factory, dependency injection stub, etc), make it a classmethod. Then it’ll be available to subclasses.


回答 1

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

基本上,类方法适合任何您想自然地适合于类的命名空间但不与类的特定实例相关联的方法。

例如,在出色的unipath模块中:

当前目录

  • Path.cwd()
    • 返回实际的当前目录;例如Path("/tmp/my_temp_dir")。这是一个类方法。
  • .chdir()
    • 使自己成为当前目录。

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

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

Factory methods (alternative constructors) are indeed a classic example of class methods.

Basically, class methods are suitable anytime you would like to have a method which naturally fits into the namespace of the class, but is not associated with a particular instance of the class.

As an example, in the excellent unipath module:

Current directory

  • Path.cwd()
    • Return the actual current directory; e.g., Path("/tmp/my_temp_dir"). This is a class method.
  • .chdir()
    • Make self the current directory.

As the current directory is process wide, the cwd method has no particular instance with which it should be associated. However, changing the cwd to the directory of a given Path instance should indeed be an instance method.

Hmmm… as Path.cwd() does indeed return a Path instance, I guess it could be considered to be a factory method…


回答 2

这样考虑:普通方法对隐藏调度细节很有用:您可以键入myobj.foo()而不必担心该foo()方法是由myobj对象的类或其父类之一实现的。类方法与此完全类似,但是使用类对象:它们使您可以调用,MyClass.foo()而不必担心是否由于需要它自己的专用版本foo()而特别实现MyClass,或者是否让其父类处理该调用。

当您在创建实际实例之前进行设置或计算时,类方法必不可少,因为在该实例存在之前,您显然不能将实例用作方法调用的分配点。可以在SQLAlchemy源代码中查看一个很好的示例。dbapi()在以下链接中查看类方法:

https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47

您可以看到,dbapi()数据库后端用来按需导入特定于供应商的数据库库的方法是一个类方法,因为它需要开始创建特定数据库连接的实例之前运行但是它不能运行是一个简单的函数或静态函数,因为他们希望它能够调用其他支持的方法,这些方法可能类似地需要在子类中而不是在其父类中更具体地编写。而且,如果您分派给一个函数或静态类,那么您将“忘记”并失去有关哪个类正在执行初始化的知识。

Think about it this way: normal methods are useful to hide the details of dispatch: you can type myobj.foo() without worrying about whether the foo() method is implemented by the myobj object’s class or one of its parent classes. Class methods are exactly analogous to this, but with the class object instead: they let you call MyClass.foo() without having to worry about whether foo() is implemented specially by MyClass because it needed its own specialized version, or whether it is letting its parent class handle the call.

Class methods are essential when you are doing set-up or computation that precedes the creation of an actual instance, because until the instance exists you obviously cannot use the instance as the dispatch point for your method calls. A good example can be viewed in the SQLAlchemy source code; take a look at the dbapi() class method at the following link:

https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47

You can see that the dbapi() method, which a database backend uses to import the vendor-specific database library it needs on-demand, is a class method because it needs to run before instances of a particular database connection start getting created — but that it cannot be a simple function or static function, because they want it to be able to call other, supporting methods that might similarly need to be written more specifically in subclasses than in their parent class. And if you dispatch to a function or static class, then you “forget” and lose the knowledge about which class is doing the initializing.


回答 3

我最近想要一个非常轻量级的日志记录类,该类将根据可以通过编程设置的日志记录级别输出不同数量的输出。但是我不想每次想输出调试消息,错误或警告时都实例化该类。但是我还想封装该日志记录工具的功能,并在不声明任何全局变量的情况下使其可重用。

因此,我使用了类变量和@classmethod装饰器来实现这一点。

使用简单的Logging类,可以执行以下操作:

Logger._level = Logger.DEBUG

然后,在我的代码中,如果我想吐出一堆调试信息,我只需要编写代码

Logger.debug( "this is some annoying message I only want to see while debugging" )

错误可能会被解决

Logger.error( "Wow, something really awful happened." )

在“生产”环境中,我可以指定

Logger._level = Logger.ERROR

现在,将仅输出错误消息。调试消息将不会打印。

这是我的课:

class Logger :
    ''' Handles logging of debugging and error messages. '''

    DEBUG = 5
    INFO  = 4
    WARN  = 3
    ERROR = 2
    FATAL = 1
    _level = DEBUG

    def __init__( self ) :
        Logger._level = Logger.DEBUG

    @classmethod
    def isLevel( cls, level ) :
        return cls._level >= level

    @classmethod
    def debug( cls, message ) :
        if cls.isLevel( Logger.DEBUG ) :
            print "DEBUG:  " + message

    @classmethod
    def info( cls, message ) :
        if cls.isLevel( Logger.INFO ) :
            print "INFO :  " + message

    @classmethod
    def warn( cls, message ) :
        if cls.isLevel( Logger.WARN ) :
            print "WARN :  " + message

    @classmethod
    def error( cls, message ) :
        if cls.isLevel( Logger.ERROR ) :
            print "ERROR:  " + message

    @classmethod
    def fatal( cls, message ) :
        if cls.isLevel( Logger.FATAL ) :
            print "FATAL:  " + message

和一些测试它的代码:

def logAll() :
    Logger.debug( "This is a Debug message." )
    Logger.info ( "This is a Info  message." )
    Logger.warn ( "This is a Warn  message." )
    Logger.error( "This is a Error message." )
    Logger.fatal( "This is a Fatal message." )

if __name__ == '__main__' :

    print "Should see all DEBUG and higher"
    Logger._level = Logger.DEBUG
    logAll()

    print "Should see all ERROR and higher"
    Logger._level = Logger.ERROR
    logAll()

I recently wanted a very light-weight logging class that would output varying amounts of output depending on the logging level that could be programmatically set. But I didn’t want to instantiate the class every time I wanted to output a debugging message or error or warning. But I also wanted to encapsulate the functioning of this logging facility and make it reusable without the declaration of any globals.

So I used class variables and the @classmethod decorator to achieve this.

With my simple Logging class, I could do the following:

Logger._level = Logger.DEBUG

Then, in my code, if I wanted to spit out a bunch of debugging information, I simply had to code

Logger.debug( "this is some annoying message I only want to see while debugging" )

Errors could be out put with

Logger.error( "Wow, something really awful happened." )

In the “production” environment, I can specify

Logger._level = Logger.ERROR

and now, only the error message will be output. The debug message will not be printed.

Here’s my class:

class Logger :
    ''' Handles logging of debugging and error messages. '''

    DEBUG = 5
    INFO  = 4
    WARN  = 3
    ERROR = 2
    FATAL = 1
    _level = DEBUG

    def __init__( self ) :
        Logger._level = Logger.DEBUG

    @classmethod
    def isLevel( cls, level ) :
        return cls._level >= level

    @classmethod
    def debug( cls, message ) :
        if cls.isLevel( Logger.DEBUG ) :
            print "DEBUG:  " + message

    @classmethod
    def info( cls, message ) :
        if cls.isLevel( Logger.INFO ) :
            print "INFO :  " + message

    @classmethod
    def warn( cls, message ) :
        if cls.isLevel( Logger.WARN ) :
            print "WARN :  " + message

    @classmethod
    def error( cls, message ) :
        if cls.isLevel( Logger.ERROR ) :
            print "ERROR:  " + message

    @classmethod
    def fatal( cls, message ) :
        if cls.isLevel( Logger.FATAL ) :
            print "FATAL:  " + message

And some code that tests it just a bit:

def logAll() :
    Logger.debug( "This is a Debug message." )
    Logger.info ( "This is a Info  message." )
    Logger.warn ( "This is a Warn  message." )
    Logger.error( "This is a Error message." )
    Logger.fatal( "This is a Fatal message." )

if __name__ == '__main__' :

    print "Should see all DEBUG and higher"
    Logger._level = Logger.DEBUG
    logAll()

    print "Should see all ERROR and higher"
    Logger._level = Logger.ERROR
    logAll()

回答 4

替代构造函数是经典示例。

Alternative constructors are the classic example.


回答 5

当用户登录我的网站时,将从用户名和密码实例化User()对象。

如果我需要一个用户对象而不需要用户在那里登录(例如,管理员用户可能要删除另一个用户帐户,那么我需要实例化该用户并调用其delete方法):

我有获取用户对象的类方法。

class User():
    #lots of code
    #...
    # more code

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username).get()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_id == auth_id).get()

When a user logs in on my website, a User() object is instantiated from the username and password.

If I need a user object without the user being there to log in (e.g. an admin user might want to delete another users account, so i need to instantiate that user and call its delete method):

I have class methods to grab the user object.

class User():
    #lots of code
    #...
    # more code

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username).get()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_id == auth_id).get()

回答 6

我认为最明确的答案是AmanKow的答案。归结为您要如何组织代码。您可以将所有内容编写为模块级函数,并包装在模块的命名空间中,即

module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass


usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass 
def f5():pass

usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()

上面的过程代码没有很好的组织,正如您看到的只有3个模块感到困惑之后,每种方法有什么作用?您可以为函数使用长的描述性名称(例如Java中的名称),但您的代码仍然很快变得难以管理。

面向对象的方式是将代码分解为可管理的块,即类和对象以及函数可以与对象实例或类相关联。

与模块级别的函数相比,使用类函数可以在代码中获得更高的划分级别。因此,您可以在一个类中对相关功能进行分组,以使它们对您分配给该类的任务更加特定。例如,您可以创建一个文件实用程序类:

class FileUtil ():
  def copy(source,dest):pass
  def move(source,dest):pass
  def copyDir(source,dest):pass
  def moveDir(source,dest):pass

//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")

这种方式更灵活,更可维护,您可以将功能分组在一起,并且每个功能的作用更加明显。另外,还可以防止名称冲突,例如,函数副本可能存在于您在代码中使用的另一个导入模块(例如,网络副本)中,因此,当您使用全名FileUtil.copy()时,您就可以解决此问题并同时复制两个函数可以并排使用。

I think the most clear answer is AmanKow’s one. It boils down to how u want to organize your code. You can write everything as module level functions which are wrapped in the namespace of the module i.e

module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass


usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass 
def f5():pass

usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()

The above procedural code is not well organized, as you can see after only 3 modules it gets confusing, what is each method do ? You can use long descriptive names for functions(like in java) but still your code gets unmanageable very quick.

The object oriented way is to break down your code into manageable blocks i.e Classes & objects and functions can be associated with objects instances or with classes.

With class functions you gain another level of division in your code compared with module level functions. So you can group related functions within a class to make them more specific to a task that you assigned to that class. For example you can create a file utility class :

class FileUtil ():
  def copy(source,dest):pass
  def move(source,dest):pass
  def copyDir(source,dest):pass
  def moveDir(source,dest):pass

//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")

This way is more flexible and more maintainable, you group functions together and its more obvious to what each function do. Also you prevent name conflicts, for example the function copy may exist in another imported module(for example network copy) that you use in your code, so when you use the full name FileUtil.copy() you remove the problem and both copy functions can be used side by side.


回答 7

老实说 我从未找到用于staticmethod或classmethod的方法。我还没有看到使用全局函数或实例方法无法完成的操作。

如果python更像Java那样使用私有成员和受保护成员,那将是不同的。在Java中,我需要一个静态方法来访问实例的私有成员以执行操作。在Python中,这几乎没有必要。

通常,当人们真正需要做的就是更好地使用python的模块级命名空间时,我会看到人们使用staticmethod和classmethod。

Honestly? I’ve never found a use for staticmethod or classmethod. I’ve yet to see an operation that can’t be done using a global function or an instance method.

It would be different if python used private and protected members more like Java does. In Java, I need a static method to be able to access an instance’s private members to do stuff. In Python, that’s rarely necessary.

Usually, I see people using staticmethods and classmethods when all they really need to do is use python’s module-level namespaces better.


回答 8

它允许您编写可与任何兼容类一起使用的通用类方法。

例如:

@classmethod
def get_name(cls):
    print cls.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C.get_name()

如果您不使用@classmethod它,可以使用self关键字来实现,但是它需要一个Class的实例:

def get_name(self):
    print self.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C().get_name() #<-note the its an instance of class C

It allows you to write generic class methods that you can use with any compatible class.

For example:

@classmethod
def get_name(cls):
    print cls.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C.get_name()

If you don’t use @classmethod you can do it with self keyword but it needs an instance of Class:

def get_name(self):
    print self.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C().get_name() #<-note the its an instance of class C

回答 9

我曾经使用过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()

聪明吧?

I used to work with PHP and recently I was asking myself, whats going on with this classmethod? Python manual is very technical and very short in words so it wont help with understanding that feature. I was googling and googling and I found answer -> http://code.anjanesh.net/2007/12/python-classmethods.html.

If you are lazy to click it. My explanation is shorter and below. :)

in PHP (maybe not all of you know PHP, but this language is so straight forward that everybody should understand what I’m talking about) we have static variables like this:


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

The output will be in both cases 20.

However in python we can add @classmethod decorator and thus it is possible to have output 10 and 20 respectively. Example:


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

Smart, ain’t?


回答 10

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

示例:您获得了一组表示对象的类。您可能需要类方法all()find()编写User.all()or User.find(firstname='Guido')。当然可以使用模块级别的功能来完成…

Class methods provide a “semantic sugar” (don’t know if this term is widely used) – or “semantic convenience”.

Example: you got a set of classes representing objects. You might want to have the class method all() or find() to write User.all() or User.find(firstname='Guido'). That could be done using module level functions of course…


回答 11

刚从Ruby袭来的我是,所谓的方法和所谓的实例方法只是一个函数,其第一个参数具有语义含义,当该函数被称为一个对象(即obj.meth())。

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

因为它只是一个函数,所以只能在任何给定范围(即class定义)中声明一次。因此,如果遵循的话,令Rubyist感到惊讶的是,您不能拥有同名的类方法和实例方法

考虑一下:

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专家还很远,如果其中任何一个错误,我想发表评论。

What just hit me, coming from Ruby, is that a so-called class method and a so-called instance method is just a function with semantic meaning applied to its first parameter, which is silently passed when the function is called as a method of an object (i.e. obj.meth()).

Normally that object must be an instance but the @classmethod method decorator changes the rules to pass a class. You can call a class method on an instance (it’s just a function) – the first argument will be its class.

Because it’s just a function, it can only be declared once in any given scope (i.e. class definition). If follows therefore, as a surprise to a Rubyist, that you can’t have a class method and an instance method with the same name.

Consider this:

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

You can call foo on an instance

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

But not on a class:

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)

Now add @classmethod:

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

Calling on an instance now passes its class:

Foo().foo()
__main__.Foo

as does calling on a class:

Foo.foo()
__main__.Foo

It’s only convention that dictates that we use self for that first argument on an instance method and cls on a class method. I used neither here to illustrate that it’s just an argument. In Ruby, self is a keyword.

Contrast with 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>

The Python class method is just a decorated function and you can use the same techniques to create your own decorators. A decorated method wraps the real method (in the case of @classmethod it passes the additional class argument). The underlying method is still there, hidden but still accessible.


footnote: I wrote this after a name clash between a class and instance method piqued my curiosity. I am far from a Python expert and would like comments if any of this is wrong.


回答 12

这是一个有趣的话题。我认为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

This is an interesting topic. My take on it is that python classmethod operates like a singleton rather than a factory (which returns a produced an instance of a class). The reason it is a singleton is that there is a common object that is produced (the dictionary) but only once for the class but shared by all instances.

To illustrate this here is an example. Note that all instances have a reference to the single dictionary. This is not Factory pattern as I understand it. This is probably very unique to 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

回答 13

我几次问自己这个问题。即使这里的人们努力解释,恕我直言,我发现最好的答案(也是最简单的)是描述 Python文档中有关Class方法。

也有引用静态方法。如果有人已经知道实例方法(我假设),那么这个答案可能是将所有内容放在一起的最后一部分…

也可以在文档中找到有关此主题的更多详细说明: 标准类型层次结构(向下滚动至“ 实例方法”部分)

I was asking myself the same question few times. And even though the guys here tried hard to explain it, IMHO the best answer (and simplest) answer I have found is the description of the Class method in the Python Documentation.

There is also reference to the Static method. And in case someone already know instance methods (which I assume), this answer might be the final piece to put it all together…

Further and deeper elaboration on this topic can be found also in the documentation: The standard type hierarchy (scroll down to Instance methods section)


回答 14

@classmethod从外部资源轻松实例化该类的对象很有用。考虑以下:

import settings

class SomeClass:
    @classmethod
    def from_settings(cls):
        return cls(settings=settings)

    def __init__(self, settings=None):
        if settings is not None:
            self.x = settings['x']
            self.y = settings['y']

然后在另一个文件中:

from some_package import SomeClass

inst = SomeClass.from_settings()

访问inst.x将提供与settings [‘x’]相同的值。

@classmethod can be useful for easily instantiating objects of that class from outside resources. Consider the following:

import settings

class SomeClass:
    @classmethod
    def from_settings(cls):
        return cls(settings=settings)

    def __init__(self, settings=None):
        if settings is not None:
            self.x = settings['x']
            self.y = settings['y']

Then in another file:

from some_package import SomeClass

inst = SomeClass.from_settings()

Accessing inst.x will give the same value as settings[‘x’].


回答 15

当然,一个类定义了一组实例。一类的方法适用于各个实例。类方法(和变量)用于挂起与所有实例集相关的其他信息的位置。

例如,如果您的Class定义了一组学生,则您可能需要Class变量或方法来定义诸如学生可以成为其成员的年级组之类的东西。

您还可以使用类方法来定义用于处理整个集合的工具。例如,Student.all_of_em()可能会返回所有已知的学生。显然,如果您的实例集具有比一组实例更多的结构,则可以提供类方法来了解该结构。Students.all_of_em(grade =’juniors’)

诸如此类的技术往往导致将实例集的成员存储到以类变量为根的数据结构中。您需要注意避免破坏垃圾收集。

A class defines a set of instances, of course. And the methods of a class work on the individual instances. The class methods (and variables) a place to hang other information that is related to the set of instances over all.

For example if your class defines a the set of students you might want class variables or methods which define things like the set of grade the students can be members of.

You can also use class methods to define tools for working on the entire set. For example Student.all_of_em() might return all the known students. Obviously if your set of instances have more structure than just a set you can provide class methods to know about that structure. Students.all_of_em(grade=’juniors’)

Techniques like this tend to lead to storing members of the set of instances into data structures that are rooted in class variables. You need to take care to avoid frustrating the garbage collection then.