Simple example of __new__ method usage
""" | |
An example of usage of __new__ magic method to implement dynamic computational | |
logic dispatching. | |
For more information see: https://iliazaitsev.me/blog/2018/02/14/python-new | |
""" | |
import abc | |
class TemperatureConverter(metaclass=abc.ABCMeta): | |
""" | |
Base class of temperature converters which supports dynamic substitution | |
of implementations. | |
""" | |
symbol = 'K' | |
def __new__(cls, convert_to='celsius'): | |
if issubclass(cls, TemperatureConverter): | |
if convert_to == 'celsius': | |
cls = _CelsiusConverter | |
elif convert_to == 'fahrenheit': | |
cls = _FahrenheitConverter | |
else: | |
raise ValueError('unexpected converter: %s' % convert_to) | |
return object.__new__(cls) | |
def convert(self, value): | |
""" | |
Converts temperature from Kelvin degrees into format defined by | |
concrete implementation. | |
""" | |
self.check_value(value) | |
return self._convert(value) | |
def format(self, value): | |
return '%.2f (%s)' % (self.convert(value), self.symbol) | |
@staticmethod | |
def check_value(value): | |
if value < 0: | |
raise ValueError('temperature should be provided in Kelvin degrees') | |
@property | |
def name(self): | |
return self.__class__.__name__.strip('_') | |
def _convert(self, value): | |
raise NotImplementedError() | |
class _CelsiusConverter(TemperatureConverter): | |
""" | |
Concrete implementation of temperature converted which converts from Kelvin | |
into Celsius degrees. | |
""" | |
symbol = '°C' | |
def _convert(self, value): | |
return value - 273.15 | |
class _FahrenheitConverter: | |
""" | |
Concrete implementation of temperature converter which converts from Kelvin | |
into Fahrenheit degrees. | |
Note that this class does not directly inherit base class, but is | |
registered as derived class using ABCMeta.register method. Though in this | |
case, there is no a default implementation of `format` method and `name` | |
property. | |
""" | |
symbol = '°F' | |
def _convert(self, value): | |
return value * 9./5 - 459.67 | |
def format(self, value): | |
return '%.2f (%s)' % (self._convert(value), self.symbol) | |
@property | |
def name(self): | |
return 'FahrenheitConverter' | |
TemperatureConverter.register(_FahrenheitConverter) | |
def main(): | |
converters = [ | |
TemperatureConverter(convert_to=name) | |
for name in ('celsius', 'fahrenheit')] | |
temperature = 300 | |
for converter in converters: | |
string = converter.format(temperature) | |
print('%s converted %sK temperature into: %s' % ( | |
converter.name, temperature, string | |
)) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment