Un ‘functor’ es u objeto el cual puede ser llamado como si fuera una función. En Python simplemente hay que implementar el método ‘__call__’. En este ejemplo, vamos a tomar a los candidatos presidenciales del articulo anterior y vamos a escribir una pequeña clase (llamada SortKey) la cual nos va a permitir ordenar por cualquier attributo o combinación de attributos (en nuestro ejemplo vamos a ordendar por sexo y luego por edad):
#!/usr/bin/env python3
# A little fun with the candidates for the US presidency for 2016 election year
# Revisited versions with functors, slots, abstract classes
# @author josevnz@kodegeek.com
#
import abc
class SortKey:
def __init__(self, *attributes):
self.attributes = attributes
def __call__(self, instance):
return [getattr(instance, attribute) for attribute in self.attributes]
class Candidate(metaclass=abc.ABCMeta):
__slots__ = ("__name", "__sex", "__age")
@abc.abstractmethod
def __init__(self, name, sex="F", age=21):
self.__name = name
self.__sex = "F" if sex not in ["M", "F"] else sex
self.__age = 21 if not (21 < = age <= 120) else age
@property
def name(self):
return self.__name
def get_party(self):
raise NotImplemented()
party = abc.abstractproperty(get_party)
@property
def sex(self):
return self.__sex
@property
def age(self):
return self.__age
def __hash__(self):
return hash(id(self))
def __str__(self):
return "{}=[name={}, sex={}, age={}]".format(self.__class__.__name__,
self.__name,
self.__sex,
self.__age
)
def __repr__(self):
return "{}({}{}{}{})".format(
self.__class__.__name__,
self.__name,
self.__sex,
self.__age
)
@abc.abstractmethod
def __bool__(self):
raise NotImplemented()
def __lt__(self, other):
return self.__age < other.__age
def __gt__(self, other):
return self.__age > other.__age
def __eq__(self, other):
return (
self.__age == other.__age and
self.__name == other.__name and
self.__sex == other.__sex
)
class Republican(Candidate):
def __init__(self, name, sex="F", age=21):
return super().__init__(name, sex, age)
party = "Replublican Party (GOP)"
def __bool__(self):
return False
class Democrat(Candidate):
def __init__(self, name, sex="F", age=21):
return super().__init__(name, sex, age)
party = "Democratic Party"
def __bool__(self):
return True
if __name__ == "__main__":
candidates = []
candidates.append(Democrat("Hillary Clinton", "F", 68))
candidates.append(Republican("Donald Trump", "M", 70))
candidates.append(Democrat("Bernie Sanders", "M", 75))
candidates.append(Republican("Marco Rubio", "M", 45))
candidates.sort(key=SortKey("sex", "age"), reverse=False)
for candidate in candidates:
isDemocrat = "yes" if candidate else "no"
print("Candidate: {}, democrat? {}".format(candidate, isDemocrat))
Y la salida (fíjense que definimos el orden en la linea 103, ademas de que ordenamos la lista primero para después utilizarla):
Candidate: Democrat=[name=Hillary Clinton, sex=F, age=68], democrat? yes
Candidate: Republican=[name=Marco Rubio, sex=M, age=45], democrat? no
Candidate: Republican=[name=Donald Trump, sex=M, age=70], democrat? no
Candidate: Democrat=[name=Bernie Sanders, sex=M, age=75], democrat? yes