What's an example use case for a Python classmethod?
Asked Answered
T

7

86

I've read What are Class methods in Python for? but the examples in that post are complex. I am looking for a clear, simple, bare-bones example of a particular use case for classmethods in Python.

Can you name a small, specific example use case where a Python classmethod would be the right tool for the job?

Tailgate answered 21/4, 2011 at 1:19 Comment(2)
Possible duplicate of What are Class methods in Python for?.Sorcha
I've edited the question to explain the distinction between the two questions. I am looking for a Hello World-type bare bones example use case.Tailgate
B
63

Helper methods for initialization:

class MyStream(object):

    @classmethod
    def from_file(cls, filepath, ignore_comments=False):    
        with open(filepath, 'r') as fileobj:
            for obj in cls(fileobj, ignore_comments):
                yield obj

    @classmethod
    def from_socket(cls, socket, ignore_comments=False):
        raise NotImplemented # Placeholder until implemented

    def __init__(self, iterable, ignore_comments=False):
       ...
Brine answered 21/4, 2011 at 1:40 Comment(1)
Indeed, providing alternate constructors is the classic use case for classmethod. Unlike their staticmethod equivalents, they play nicely with subclasses.Guttersnipe
B
34

Well __new__ is a pretty important classmethod. It's where instances usually come from

so dict() calls dict.__new__ of course, but there is another handy way to make dicts sometimes which is the classmethod dict.fromkeys()

eg.

>>> dict.fromkeys("12345")
{'1': None, '3': None, '2': None, '5': None, '4': None}
Bowen answered 21/4, 2011 at 1:48 Comment(3)
+1 for a good real-world example. Though you leave implicit the fact that fromkeys is also a classmethod.Cooperative
Technically, __new__ is implicitly turned into a static method. There is other machinery in the instance creation process that passes in the appropriate class object as the first argument. dict.fromkeys is an excellent example of an actual class method, though.Guttersnipe
And, of course, __new__ still relies on the same concept as classmethod even if it isn't actually implemented that way.Guttersnipe
H
22

The biggest reason for using a @classmethod is in an alternate constructor that is intended to be inherited. This can be very useful in polymorphism. An example:

class Shape(object):
    # this is an abstract class that is primarily used for inheritance defaults
    # here is where you would define classmethods that can be overridden by inherited classes
    @classmethod
    def from_square(cls, square):
        # return a default instance of cls
        return cls()

Notice that Shape is an abstract class that defines a classmethod from_square, since Shape is not really defined, it does not really know how to derive itself from a Square so it simply returns a default instance of the class.

Inherited classes are then allowed to define their own versions of this method:

class Square(Shape):
    def __init__(self, side=10):
        self.side = side

    @classmethod
    def from_square(cls, square):
        return cls(side=square.side)


class Rectangle(Shape):
    def __init__(self, length=10, width=10):
        self.length = length
        self.width = width

    @classmethod
    def from_square(cls, square):
        return cls(length=square.side, width=square.side)


class RightTriangle(Shape):
    def __init__(self, a=10, b=10):
        self.a = a
        self.b = b
        self.c = ((a*a) + (b*b))**(.5)

    @classmethod
    def from_square(cls, square):
        return cls(a=square.length, b=square.width)


class Circle(Shape):
    def __init__(self, radius=10):
        self.radius = radius

    @classmethod
    def from_square(cls, square):
        return cls(radius=square.length/2)

The usage allows you to treat all of these uninstantiated classes polymorphically

square = Square(3)
for polymorphic_class in (Square, Rectangle, RightTriangle, Circle):
    this_shape = polymorphic_class.from_square(square)

This is all fine and dandy you might say, but why couldn't I just use as @staticmethod to accomplish this same polymorphic behavior:

class Circle(Shape):
    def __init__(self, radius=10):
        self.radius = radius

    @staticmethod
    def from_square(square):
        return Circle(radius=square.length/2)

The answer is that you could, but you do not get the benefits of inheritance because Circle has to be called out explicitly in the method. Meaning if I call it from an inherited class without overriding, I would still get Circle every time.

Notice what is gained when I define another shape class that does not really have any custom from_square logic:

class Hexagon(Shape):
    def __init__(self, side=10):
        self.side = side

    # note the absence of classmethod here, this will use from_square it inherits from shape

Here you can leave the @classmethod undefined and it will use the logic from Shape.from_square while retaining who cls is and return the appropriate shape.

square = Square(3)
for polymorphic_class in (Square, Rectangle, RightTriangle, Circle, Hexagon):
    this_shape = polymorphic_class.from_square(square)
Hackett answered 17/7, 2017 at 18:4 Comment(4)
you have a typo in class RightTriangle. the init method should be "init" but you write it as "__init"Molal
shouldn't the Initiation be like : square = Square(3) rect = Rectangle(15,5) rt = RightTriangle(5,15) cir = Circle(50) for polymorphic_class in (rect,square,cir,rt): this_shape = polymorphic_class.from_square(polymorphic_class) print("Initating Class ",type(this_shape).__name__) Erse
No @AlferdNobel, in your example you are instantiating the class prior to iterating on them. When you instantiate a class it becomes an instance of that class and so polymorphic_class used as the iteration variable becomes a misnomer. It is true that you can call a classmethod from an instance, but your code will be creating a total of 8 instances of different variations of Shape inherited classes, once prior to the for loop and then once again after where this_shape = polymorphic_class.from_square(...) instantiates an entirely new instance from attributes of the iterated instance.Hackett
Furthermore, @AlferdNobel, it will fail for many of those instances because in passing varying shapes as the arguments to from_square they will very likely not have the attributes that are expected, since this method expects the argument to be an instance of the Square class and your iterations will also include Rectangles, Circles and RightTrianglesHackett
D
20

I don't know, something like named constructor methods?

class UniqueIdentifier(object):

    value = 0

    def __init__(self, name):
        self.name = name

    @classmethod
    def produce(cls):
        instance = cls(cls.value)
        cls.value += 1
        return instance

class FunkyUniqueIdentifier(UniqueIdentifier):

    @classmethod
    def produce(cls):
        instance = super(FunkyUniqueIdentifier, cls).produce()
        instance.name = "Funky %s" % instance.name
        return instance

Usage:

>>> x = UniqueIdentifier.produce()
>>> y = FunkyUniqueIdentifier.produce()
>>> x.name
0
>>> y.name
Funky 1
Dempstor answered 21/4, 2011 at 1:33 Comment(0)
P
9

I find that I most often use @classmethod to associate a piece of code with a class, to avoid creating a global function, for cases where I don't require an instance of the class to use the code.

For example, I might have a data structure which only considers a key valid if it conforms to some pattern. I may want to use this from inside and outside of the class. However, I don't want to create yet another global function:

def foo_key_is_valid(key):
    # code for determining validity here
    return valid

I'd much rather group this code with the class it's associated with:

class Foo(object):

    @classmethod
    def is_valid(cls, key):
        # code for determining validity here
        return valid

    def add_key(self, key, val):
        if not Foo.is_valid(key):
            raise ValueError()
        ..

# lets me reuse that method without an instance, and signals that
# the code is closely-associated with the Foo class
Foo.is_valid('my key')
Pinchhit answered 21/4, 2011 at 1:39 Comment(2)
class methods can be called with either a class or an instance, so for example in add_key Foo.is_valid(key) could also be self.is_valid(key) docs.python.org/library/functions.html#classmethodHenning
This use case sounds more like a static method than a class method. If your method isn't using the cls parameter than you should probably use @staticmethod instead.Matadi
P
2

Another useful example of classmethod is in extending enumerated types. A classic Enum provides symbolic names which can be used later in the code for readability, grouping, type-safety, etc. This can be extended to add useful features using a classmethod. In the example below, Weekday is an enuerated type for the days of the week. It has been extended using classmethod so that instead of keeping track of the weekday ourselves, the enumerated type can extract the date and return the related enum member.

from enum import Enum
from datetime import date


class Weekday(Enum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 7
    #
    @classmethod
    def from_date(cls, date):
        return cls(date.isoweekday())
Weekday.from_date(date.today())     
<Weekday.TUESDAY: 2>

Source: https://docs.python.org/3/howto/enum.html

Pimple answered 20/12, 2022 at 14:38 Comment(0)
O
-5
in class MyClass(object):
    '''
    classdocs
    '''
    obj=0
    x=classmethod
    def __init__(self):
        '''
        Constructor
        '''
        self.nom='lamaizi'
        self.prenom='anas'
        self.age=21
        self.ville='Casablanca'
if __name__:
    ob=MyClass()
    print(ob.nom)
    print(ob.prenom)
    print(ob.age)
    print(ob.ville)
Otoplasty answered 9/2, 2015 at 19:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.