1.1. Polymorphism

1.1.1. Switch

Switch moves business logic to the execution place:

watney = 'Astronaut'

if watney == 'Astronaut':
    print('Hello')
elif watney == 'Cosmonaut':
    print('Привет!')
elif watney == 'Taikonaut':
    print('你好')
else:
    print('Default Value')

# Hello
def say_hello(key=None):
    return {
        'Astronaut': 'Hello',
        'Cosmonaut': 'Привет!',
        'Taikonaut': '你好',
    }.get(key, 'Default Value')


watney = 'Astronaut'
ivanovic = 'Cosmonaut'
twardowski = 'Sorcerer'

say_hello(watney)
# Hello

say_hello(ivanovic)
# Привет!

say_hello(twardowski):
# 'Default Value'

1.1.2. Polymorphism in a Function

Polymorphism on Function:

class Sorcerer:
    pass

class Astronaut:
    def say_hello(self):
        return 'Hello'

class Cosmonaut:
    def say_hello(self):
        return 'Привет!'


def say_hello(spaceman):
    if hasattr(spaceman, 'say_hello')
        return spaceman.say_hello()
    else:
        return 'Default Value'


watney = Astronaut()
ivanovic = Cosmonaut()
twardowski = Sorcerer()

say_hello(watney)
# Hello

say_hello(ivanovic)
# Привет!

say_hello(twardowski)
# 'Default Value'

1.1.3. Polymorphism on a Class

Polymorphism on Classes:

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

    def say_hello(self):
        return 'Hello'


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

    def say_hello(self):
        return 'Witaj!'


crew = [
    Astronaut('Mark Watney'),
    Cosmonaut('Иван Иванович'),
    Astronaut('Matt Kowalski'),
    Cosmonaut('Pan Twardowski'),
]

for member in crew:
    print(member.say_hello())
# Hello
# Witaj!
# Hello
# Witaj!

1.1.4. Factory

DATA = [
    ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
    (5.8, 2.7, 5.1, 1.9, 'virginica'),
    (5.1, 3.5, 1.4, 0.2, 'setosa'),
    (5.7, 2.8, 4.1, 1.3, 'versicolor'),
    (6.3, 2.9, 5.6, 1.8, 'virginica'),
    (6.4, 3.2, 4.5, 1.5, 'versicolor'),
    (4.7, 3.2, 1.3, 0.2, 'setosa'),
]


class Iris:
    def __init__(self, sepal_length, sepal_width, petal_length, petal_width):
        self.sepal_length = sepal_length
        self.sepal_width = sepal_width
        self.petal_length = petal_length
        self.petal_width = petal_width

    def __repr__(self):
        name = self.__class__.__name__
        values = tuple(self.__dict__.values())
        return f'\n {name}{values}'


class Setosa(Iris):
    pass

class Virginica(Iris):
    pass

class Versicolor(Iris):
    pass


def factory(species: str):
    if species == 'setosa':
        return Setosa
    if species == 'virginica':
        return Virginica
    if species == 'versicolor':
        return Versicolor


result = []

for *features, species in DATA[1:]:
    iris = factory(species)
    i = iris(*features)
    result.append(i)

print(result)
# [Virginica(5.8, 2.7, 5.1, 1.9),
#  Setosa(5.1, 3.5, 1.4, 0.2),
#  Versicolor(5.7, 2.8, 4.1, 1.3),
#  Virginica(6.3, 2.9, 5.6, 1.8),
#  Versicolor(6.4, 3.2, 4.5, 1.5),
#  Setosa(4.7, 3.2, 1.3, 0.2)]
from dataclasses import dataclass

DATA = [
    ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
    (5.8, 2.7, 5.1, 1.9, 'virginica'),
    (5.1, 3.5, 1.4, 0.2, 'setosa'),
    (5.7, 2.8, 4.1, 1.3, 'versicolor'),
    (6.3, 2.9, 5.6, 1.8, 'virginica'),
    (6.4, 3.2, 4.5, 1.5, 'versicolor'),
    (4.7, 3.2, 1.3, 0.2, 'setosa'),
]


@dataclass
class Iris:
    sepal_length: float
    sepal_width: float
    petal_length: float
    petal_width: float

class Setosa(Iris):
    pass

class Virginica(Iris):
    pass

class Versicolor(Iris):
    pass


def factory(species: str):
    species = species.capitalize()
    classes = globals()
    return classes[species]


result = [
    factory(species)(*features)
    for *features, species in DATA[1:]
]

print(result)
# [Virginica(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9),
#  Setosa(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2),
#  Versicolor(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3),
#  Virginica(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8),
#  Versicolor(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5),
#  Setosa(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2)]