This pattern can be found here:
github.com/faif/python-patterns/blob/master/creational/abstract_factory.py
This pattern initialises a class (factory) that is able to create other classes. In this case, the factory is PetShop that will receive another class (Cat, Dog) as an argument and create instances of it.
This implementation has some interesting things to considere.
Considere what is said in the explanation about the "interface":
This works because both Dog/Cat and random_animal respect a common
interface (callable for creation and .speak()).
This means that all the objects you pass as an argument, have to be "initialisable", i.e. create an instance using the ObjectName() and have a "method" .speak(). Now remember, in Python, that doesn't mean they need to be classes and they must have a method. You can simulate this behavior in a variety of ways (keep that in mind specially when coming from other programming languages. Python most elegant solutions like decorators, can be implemented as classes AND functions)
So you pass the object (in this case a class), you assign it to a property:
self.pet_factory = animal_factory
and then initialise it (pay attention to the round brackets) in show_pet method.
pet = self.pet_factory()
If you're concerned that trusting the interface will be respected, i.e. the structure of the class passed, you can use abstract classes to enforce a "contract". Check them here
https://docs.python.org/3/library/abc.html
Another little thing here is the __str__ "dunder" (double underscore) magic method. This method allows to return the "name" of the class when you call it. To call it you can do str(class_instance), or as done in this example, with format:
print("We have a lovely {}".format(pet))
If you didn't define str, you would have got the address in memory. For example I got:
<__main__.Dog object at 0x000001B3E8911860>
It wouldn't really work with lovely really...
OK but what if you aren't building a pet shop generator? If you think about it, you could just initialise Dogs and Cats yourself without the factory, but the factory will enforce the use of the interface and it will mean that if you need to add Birds, Rabbits and Squirrels down the line, you keep using the same factory and the same interface.
You can use this pattern every time you need to generate "similar" instances of a class. For example, in game development, you could have a factory for enemies of a variety of types. All enemies need to be able to be damaged and attack your hero somehow. Same concept can be use for programatically generated levels, you can create lights, doors, etc with factories.
github.com/faif/python-patterns/blob/master/creational/abstract_factory.py
This pattern initialises a class (factory) that is able to create other classes. In this case, the factory is PetShop that will receive another class (Cat, Dog) as an argument and create instances of it.
This implementation has some interesting things to considere.
Considere what is said in the explanation about the "interface":
This works because both Dog/Cat and random_animal respect a common
interface (callable for creation and .speak()).
This means that all the objects you pass as an argument, have to be "initialisable", i.e. create an instance using the ObjectName() and have a "method" .speak(). Now remember, in Python, that doesn't mean they need to be classes and they must have a method. You can simulate this behavior in a variety of ways (keep that in mind specially when coming from other programming languages. Python most elegant solutions like decorators, can be implemented as classes AND functions)
So you pass the object (in this case a class), you assign it to a property:
self.pet_factory = animal_factory
and then initialise it (pay attention to the round brackets) in show_pet method.
pet = self.pet_factory()
If you're concerned that trusting the interface will be respected, i.e. the structure of the class passed, you can use abstract classes to enforce a "contract". Check them here
https://docs.python.org/3/library/abc.html
Another little thing here is the __str__ "dunder" (double underscore) magic method. This method allows to return the "name" of the class when you call it. To call it you can do str(class_instance), or as done in this example, with format:
print("We have a lovely {}".format(pet))
If you didn't define str, you would have got the address in memory. For example I got:
<__main__.Dog object at 0x000001B3E8911860>
It wouldn't really work with lovely really...
OK but what if you aren't building a pet shop generator? If you think about it, you could just initialise Dogs and Cats yourself without the factory, but the factory will enforce the use of the interface and it will mean that if you need to add Birds, Rabbits and Squirrels down the line, you keep using the same factory and the same interface.
You can use this pattern every time you need to generate "similar" instances of a class. For example, in game development, you could have a factory for enemies of a variety of types. All enemies need to be able to be damaged and attack your hero somehow. Same concept can be use for programatically generated levels, you can create lights, doors, etc with factories.