0

Consider the following (silly) MWE:

from abc import ABC, abstractmethod

class CarABC(ABC):
    def __init__(self, name, color):
        self.name = name
        self.color = color

    def __str__(self):
        return "%s %s" % (self.color, self.name)

    @abstractmethod
    def run(self):
        raise NotImplementedError()

    def clone(self, color):
        car = type(self)
        return car(color)

class Ford(CarABC):
    def __init__(self, color):
        super().__init__("Ford", color)


    def run(self):
        print("Ford is running")


class Toyota(CarABC):
    def __init__(self, color):
        super().__init__("Toyota", color)

    def run(self):
        print("Toyota is running")

where the purpose of clone method is to create a new car of the same type but different color.

Obviously since the clone operation is common among all classes inheriting from CarABC it should be a method of the base class. However, the method needs to know the child class that has invoked it to return a car of the correct type.

I was wondering if the way I used in the example is correct (and Pythonic) for figuring out the type of the base class that has invoked the method?

MikeL
  • 2,369
  • 2
  • 24
  • 38
  • `isinstance` should answer you the question: https://stackoverflow.com/questions/1549801/what-are-the-differences-between-type-and-isinstance – evilive Dec 09 '19 at 14:16
  • I am not sure. `isinstance` only checks, for example, if `self` is a `Ford` or a `Toyota`. It does not return the type. – MikeL Dec 09 '19 at 14:18
  • But isn't that exactly what you achieve by printing "yx is running"? Ahh: sorry...no i get it...bu no idea ;) – evilive Dec 09 '19 at 14:20
  • My question was about what I am doing in the `clone` method, not `run` method. – MikeL Dec 09 '19 at 14:21

1 Answers1

0

According to [Python 3.Docs]: Built-in Functions - class type(object) (emphasis is mine):

With one argument, return the type of an object. The return value is a type object and generally the same object as returned by object.__class__.

The isinstance() built-in function is recommended for testing the type of an object, because it takes subclasses into account.

Using isinstance here would obviously be a bad design decision, as the base class would have to know (and iterate over) all its child classes.

A cleaner clone method variant would be:

def clone(self, color):
    cls = self.__class__
    return cls(color)

The drawback would be that if you add some other (non default) arguments to a child class initializer, you won't be able to instantiate it.

As a note, using type in this case would be OK (and it would probably be the only way if using old style classes - but those exist in Python 2. only).

Community
  • 1
  • 1
CristiFati
  • 38,250
  • 9
  • 50
  • 87