Code

Coding, Programming & Algorithms, Tips, Tweaks & Hacks
Search

Sponsored Ads

Python's Classmethods

Lets say I have an abstract class called Vehicle and 3 classes that descend from it are Bike, Car & Truck.
I use a static variable in Vehicle called total to keep track of the total number of Vehicles. But I really don't want to keep track of the total number general Vehicles. What I really want is to keep track of the total number of Bikes, Cars & Trucks individually. This is easy - just declare total in Bike, Car & Truck classes.

Now arises a situation where we need a function println in Vehicle that accesses total. We'll also include a function called set in Vehicle to explicitly set the value of total (instead of creating 10 instances to prove a point).

There are two scenarios to this, both of which are not possible :

  1. Declare a static variable total in Vehicle but different values persist in Bike::total, Car::total & Truck::total. This is impossible because total is Vehicle's static variable which is common to all. The following will output 2 2 2.
    PHP
    <?php
    abstract class Vehicle
     {
            protected static $total;
    
            public static function println()
             {
                    echo self::$total."\n";
             }
    
            public static function set($value)
             {
                    self::$total = $value;
             }
     }
    
    class Bike extends Vehicle
     {
     }
    
    class Car extends Vehicle
     {
     }
    
    class Truck extends Vehicle
     {
     }
    
    Bike::set(3);
    Car::set(5);
    Truck::set(2);
    
    $b = new Bike();
    $c = new Car();
    $t = new Truck();
    
    Bike::println()
    Car::println()
    Truck::println()
    
    // Is there any way for Bike's static variable to hold 3 and Car's static to hold 5 & Truck's static to hold 2 ?
    ?>
    PHP 5.2.5
  2. Declare the static variable total in each of Vehicle's subclasses, Bike, Car & Truck. But in this case, Vehicle's println needs to access the descendant class static variable like child::total which is not possible in most(all) languages.
    PHP
    <?php
    abstract class Vehicle
     {
            public static function println()
             {
                    echo child::$total."\n";
             }
    
            public static function set($value)
             {
                    child::$total = $value;
             }
     }
    
    class Bike extends Vehicle
     {
            public static $total; # Should've been protected, but then parent wouldn't able to access
     }
    
    class Car extends Vehicle
     {
            public static $total;
     }
    
    class Truck extends Vehicle
     {
            public static $total;
     }
    
    Bike::set(3);
    Car::set(5);
    Truck::set(2);
    
    $b = new Bike();
    $c = new Car();
    $t = new Truck();
    
    Bike::println();
    Car::println();
    Truck::println();
    ?>
    PHP 5.2.5

    It is possible to overcome this problem by setting and getting the value in the subclasses and use $this->childMethod() in the parent class, Vehicle.
    PHP
    <?php
    abstract class Vehicle
     {
            public function set($value)
             {
                    $this->setChildValue($value);
             }
    
            public function println()
             {
                    echo $this->getChildValue()."\n";
             }
     }
    
    class Bike extends Vehicle
     {
            protected static $total;
    
            public function getChildValue()
             {
                    return self::$total;
             }
    
            public function setChildValue($value)
             {
                    self::$total = $value;
             }
     }
    
    class Car extends Vehicle
     {
            protected static $total;
    
            public function getChildValue()
             {
                    return self::$total;
             }
    
            public function setChildValue($value)
             {
                    self::$total = $value;
             }
     }
    
    class Truck extends Vehicle
     {
            protected static $total;
    
            public function getChildValue()
             {
                    return self::$total;
             }
    
            public function setChildValue($value)
             {
                    self::$total = $value;
             }
     }
    
    $b = new Bike();  $b->set(3);
    $c = new Car();   $c->set(5);
    $t = new Truck(); $t->set(2);
    
    $b->println();
    $c->println();
    $t->println();
    ?>
    PHP 5.2.5

    But the two methods, getChildValue and setChildValue must be defined properly in all subclasses.
    __CLASS__ returns the class in which its called from and get_class($this) returns the class of the current instance.
    Example : echo __CLASS__; in a method in Vehicle will always output Vehicle, but get_class($this) will output the classname of the object (In this case, Bike, Car or Truck).
    If we could do get_class($this)::$total (Bike::$total), then it could've been easily solved.

    PHP
    <?php
    abstract class Vehicle
     {
            public function set($value)
             {
                    get_class($this)::$total = $value;
             }
    
            public function println()
             {
                    echo get_class($this)::$total."\n";
             }
     }
    
    class Bike extends Vehicle
     {
            public static $total;
     }
    
    class Car extends Vehicle
     {
            public static $total;
     }
    
    class Truck extends Vehicle
     {
            public static $total;
     }
    
    $b = new Bike();  $b->set(3);
    $c = new Car();   $c->set(5);
    $t = new Truck(); $t->set(2);
    
    $b->println();
    $c->println();
    $t->println();
    ?>
    PHP 5.2.5

The last method is possible in Python in two ways - self.__class__.total & classmethod. Python has a way to access class member of the caller's class, not just the class members in which its being accessed. Here each of the subclasses have a static member called total which gets created and assigned in its parent, Vehicle

Python : Using __class__
class Vehicle:

      def println(self):
          print self.__class__.total

      def set(self, value):
          self.__class__.total = value

class Bike(Vehicle):
          pass

class Car(Vehicle):
          pass

class Truck(Vehicle):
          pass

Bike().set(3)   # Bike.total = 3
Car().set(5)    # Car.total = 5
Truck().set(2)  # Truck.total = 2

b = Bike()
c = Car()
t = Truck()

b.println()
c.println()
t.println()
Python 2.5.1
Python : Using classmethod
class Vehicle:

      @classmethod
      def println(cls):
          print cls.total

      @classmethod
      def set(cls, value):
          cls.total = value

class Bike(Vehicle):
          pass

class Car(Vehicle):
          pass

class Truck(Vehicle):
          pass

Bike.set(3)   # Bike.total = 3
Car.set(5)    # Car.total = 5
Truck.set(2)  # Truck.total = 2

b = Bike()
c = Car()
t = Truck()

b.println()
c.println()
t.println()

Bike.println()
Car.println()
Truck.println()
Python 2.5.1

self.__class__.total points to the static member total of the object's (this) class and not of the class Vehicle.
cls.total references the same thing as the first argument is actually the classname which is not passed in the parentheses, but by using the classname preceding the dot.

Vanakkam !

0 comments: