Assume I have performance-oriented type mylib.color.Hardware
, and its user-friendly counterparts mylib.color.RGB
and mylib.color.HSB
. When user-friendly color passed into library functions, it gets converted into color.Hardware
. Now it is implemented by examining type of passed arg. But in future I want to accept and auto-convert from any type, which provides corresponding conversion feature. For example, third-party library that implements 'otherlib.color.LAB'.
Right now I am playing with prototype, something like this:
class somelib:
class A(object):
def __init__(self, value):
assert isinstance(value, int)
self._value = value
def get(self):
return self._value
class userlib:
class B(object):
def __init__(self, value):
self._value = value
def __toA(self):
try: value = int(self._value)
except: value = 0
return somelib.A(value)
__typecasts__ = {somelib.A: __toA}
def autocast(obj, cast_type):
if isinstance(obj, cast_type): return obj
try: casts = getattr(obj, '__typecasts__')
except AttributeError: raise TypeError, 'type cast protocol not implemented at all in', obj
try: fn = casts[cast_type]
except KeyError: raise TypeError, 'type cast to {0} not implemented in {1}'.format(cast_type, obj)
return fn(obj)
def printValueOfA(a):
a = autocast(a, somelib.A)
print 'value of a is', a.get()
printValueOfA(userlib.B(42.42)) # value of a is 42
printValueOfA(userlib.B('derp')) # value of a is 0
And here is my second prototype, less intrusive but more verbose:
# typecast.py
_casts = dict()
def registerTypeCast(from_type, to_type, cast_fn = None):
if cast_fn is None:
cast_fn = to_type
key = (from_type, to_type)
global _casts
_casts[key] = cast_fn
def typeCast(obj, to_type):
if isinstance(obj, to_type): return obj
from_type = type(obj)
key = (from_type, to_type)
fn = _casts.get(key)
if (fn is None) or (fn is NotImplemented):
raise TypeError, "type cast from {0} to {1} not provided".format(from_type, to_type)
return fn(obj)
# test.py
from typecast import *
registerTypeCast(int, str)
v = typeCast(42, str)
print "result:", type(v), repr(v)
Questions. Is there exists library with same functional? (I don't want to reinvent the wheel, but my google-fu yields None.) Or may be You can suggest better (perhaps more pythonic) approach?
EDIT: Added 2nd prototype.
You are looking for a component architecture and adaptation. The Zope Component Architecture lets you register interfaces and adapters; a central registry to look up converters from one type to another. As long as an adapter exists to convert a value to the target type, you can pass any object into your API.
You start by defining what interface the target type requires:
from zope.interface import Interface, Attribute
class IHardware(Interface):
"""A hardware colour"""
bar = Attribute("The bar value for the frobnar")
def foo():
"""Foo the bar for spam and eggs"""
Any class can then implement that interface (instances of such a class would provide the interface).
from zope.interface import implements
class Hardware(object):
implements(IHardware)
def __init__(self, bar):
self.bar = bar
def foo(self):
return self.bar + ' and spam and eggs'
For your RGB class, you then register an adapter; it helps if you have an IRGB
interface, but it is not required:
from zope.interface import implements
from zope.component import adapts
from zope.component import getGlobalSiteManager
class RGB(object):
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
class RGBtoHardwareAdapter(object):
implements(IHardware)
adapts(RGB)
def __init__(self, rgb_instance):
self._rgb = rgb_instance
self.bar = '{},{},{}'.format(rgb_instance.r, rgb_instance.g, rgb_instance.b)
def foo(self):
return self.bar + ' in rgb and spam and eggs'
gsm = getGlobalSiteManager()
gsm.registerAdapter(RGBtoHardwareAdapter)
Now your API just needs to 'cast' your values to IHardware
:
def some_api_call(hardware):
hardware = IHardware(hardware)
That's it. If the hardware
value directly provides the IHardware
interface, it is returned unchanged. If it is a RGB
instance, an adapter is found in the registry; the adapter is created ( RGBtoHardwareAdapter(hardware)
is called) and will act just like a IHardware
object.
You can also have your adapter return an actual Hardware()
object; the above example returns a proxy object instead, but the principle is the same.
Python's Abstract Base Classes approach interfaces from a different direction, namely ducktyping. You can test if a given object conforms to the ABCs methods and attributes. It does not offer adaptation however.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.