2

I stumbled over the term "inheritable alternative constructors" in this answer: https://stackoverflow.com/a/1669524/633961

The link points to a place where classmethod gets explained.

Do other programming languages have this feature, too?

Community
  • 1
  • 1
guettli
  • 25,042
  • 81
  • 346
  • 663

3 Answers3

4

One of the things that you can do with ANY language that has class methods (or similar) is provide alternative constructors. A slightly contrived Python3 example below :

class Color():
     def __init__( self, red, green, blue):
         self._red, self._green, self._blue = red, green, blue
     
     @classmethod
     def by_name( cls_, color_name ):
        color_defs = {'white':(255,255,255), 'red':(255,0,0),
                       'green':(0,255,0),'blue':(0,0,255)}
        return cls_( *color_defs[color_name] )

with this class you can now do :

    red = Color(255,0,0) # Using the normal constructor
    # or
    red = Color.by_name('red') # Using the alternative 

In Python the 'by_name' method would normally be called a factory method, rather than a constructor, but it uses the normal constructor methods.

Because this 'by_name' method is just a classmethod, it means you subclass it, the class method is inherited too - so it can be used on any subclass: i.e. it is inheritable and extensible.

An example in Python of a subclass which extends the Color class above, and extends the constructor and the 'by_name'

class ColorWithAlpha( Color ):
      def __init__(self, red, green, blue, alpha=1.0):
           super().__init__(red,green,blue)
           self._alpha = alpha
      
      @classmethod
      def by_name( cls_, color_name, alpha):
          inst = super().by_name(color_name)
          inst._alpha = alpha
          return inst

red_alpha = ColorWithAlpha(255,0,0,0.5)
red2_alpha = ColorWithAlpha.by_name('red',0.5)

Other languages have similar alternative constructors (for instance C++ allows multiple constructors based on the arguments types), and these methods are all inheritable (i.e. subclasses can use them too (or extend them as necessary). I can't speak of other languages, but I am sure other OOP languages will have similar constructors/factory method capabilities.

Tony Suffolk 66
  • 9,358
  • 3
  • 30
  • 33
  • 1
    This solution returns an object of type Color, not ColorWithAlpha. Thus, if ColorWithAlpha defines some method foo, red_alpha.foo(), would work, but red2_alpha.foo() would not, it would fail saying type Color has no attribute "foo" -- just verified that with an interpreter. – Ivan Krivyakov Oct 18 '20 at 01:51
  • yes - there is a small bug in the by_name method - will correct now. – Tony Suffolk 66 Oct 18 '20 at 08:54
3

A Python class can only have one __init__() method, which many call the class constructor because it's called to initialize instances of the class when they are created by calling the class.

Sometimes it's useful to have other, alternative, ways of creating instances of the class, and functions or methods that do that could collectively be called alternative constructors. Since these will be called before an instance exists, they are often implemented as classmethods rather than standalone functions. This also requires the class name (or instance of the class) to be used to make it more obvious what is being done: i.e. MyClass.created_some_other_way()

Like most methods, they will be inherited by any subclasses derived from the class that defines them, so could collectively be referred to as inheritable alternative constructors to emphasize what would otherwise be implicit.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • @martineua. Thank you for your answer. This reminds me a bit to a question which I had some months ago. If the constructor needs other classes it's sometimes impossible to change the classes which get used by the (alternative) constructor. If you are curious, here is the question: http://stackoverflow.com/questions/27571848/name-of-design-pattern-get-class-from-class-level – guettli Mar 29 '17 at 09:40
  • 1
    @guettli: It's important to realize that any alternative constructors (whether they're standalone functions or —possibly inherited—classmethods) end up calling the "real" class constructor, `__init__()`. This means that if some scheme has been implemented for it (`__init__()`) to use different classes, then the scheme will also be used by instances created by alternative means. In other words, the design pattern shown in your other question would also work with (possibly inherited) alternative constructors. – martineau Mar 29 '17 at 10:36
  • Thank you ... and: Do you have a name for the design pattern of question 275... ? – guettli Mar 29 '17 at 10:55
0

To make clear the inheritable portion of the question, here is the first example rewritten in a way that works if inheritance is not used. Again, this is 'not' what you want to do.

class Color():
     def __init__( self, red, green, blue):
         self._red, self._green, self._blue = red, green, blue
     
     def by_name(color_name ):
        color_defs = {'white':(255,255,255), 'red':(255,0,0),
                       'green':(0,255,0),'blue':(0,0,255)}
        return Color( *color_defs[color_name] )

Note that the @classmethod is gone, as well as the references to cls_. Also the return statement calls out the name of the class, Color, directly.

Using the @classmethod makes it possible to refer to Color with the more general and inheritance friendly cls_.

brocla
  • 123
  • 6
  • I don't understand `by_name()` of above example. Is this method a needs already an instance to be able to create an instance. Maybe you missed to use `@staticmethod`? – guettli Aug 16 '21 at 08:23