Last active
January 5, 2022 17:43
-
-
Save devforfu/1de5cecb96f92bd99ed595de7cdb7907 to your computer and use it in GitHub Desktop.
Simple example of __new__ method usage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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
@MalikRumi thank you so much for appreciation! I'm sorry for the late response, didn't see that a comment was left on this gist. Yes, you're right, it is not a "real" Python code but a pseudocode. Or more exactly, it was inspired by Swift. (Actually, I think that it may be even a real-working Swift code if I didn't do a typo there...) I played around with that language back then, and really liked it. So decided to include some abstract example there using some statically typed object-oriented language that I knew. Hope that helps!