Skip to content

Instantly share code, notes, and snippets.

@devforfu
Last active Jan 5, 2022
Embed
What would you like to do?
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()
@MalikRumi
Copy link

MalikRumi commented Oct 21, 2021

I just discovered your blog and instantly became a fan. I did not see a subscription / notification option :-(. I hope you keep enlightening us with your experience. One question: I am looking at the snippet that precedes this code in your blog post, and all I can think is: "This looks like Java". But I did not see this object replicated in the actual code, perhaps because of my relative inexperience and unfamiliarity with Java - or whatever this is in Python. Can you explain a little more, and why that choice, particularly? Thanks!

@devforfu
Copy link
Author

devforfu commented Jan 5, 2022

@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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment