The Python Oracle

What is the purpose of class methods?

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Hypnotic Puzzle3

--

Chapters
00:00 What Is The Purpose Of Class Methods?
00:37 Answer 1 Score 31
00:46 Accepted Answer Score 208
01:19 Answer 3 Score 74
02:12 Answer 4 Score 31
03:47 Thank you

--

Full question
https://stackoverflow.com/questions/3823...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #classmethod

#avk47



ACCEPTED ANSWER

Score 208


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.




ANSWER 2

Score 74


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




ANSWER 3

Score 31


Alternative constructors are the classic example.




ANSWER 4

Score 31


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