0

假设我有面向性能的类型mylib.color.Hardware,它的用户友好型对应物mylib.color.RGBmylib.color.HSB. 当用户友好的颜色传递到库函数时,它被转换为color.Hardware. 现在它是通过检查传递的 arg 的类型来实现的。但是将来我想接受任何类型并自动转换,它提供了相应的转换功能。例如,实现“otherlib.color.LAB”的第三方库。

现在我正在玩原型,像这样:

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

这是我的第二个原型,不那么打扰但更冗长:

# 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)

问题。是否存在具有相同功能的库?(我不想重新发明轮子,但我的 google-fu 没有产生任何结果。)或者可能是您可以建议更好(也许更 Pythonic)的方法?

编辑:添加了第二个原型。

4

1 回答 1

1

您正在寻找组件架构和适配。Zope 组件架构允许您注册接口和适配器;一个中央注册表,用于查找从一种类型到另一种类型的转换器。只要存在将值转换为目标类型的适配器,您就可以将任何对象传递到您的 API。

首先定义目标类型需要的接口:

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"""

然后任何类都可以实现该接口(此类的实例将提供该接口)。

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'

然后为您的 RGB 类注册一个适配器;如果您有IRGB接口,它会有所帮助,但不是必需的:

 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)

现在您的 API 只需要将您的值“转换”为IHardware

def some_api_call(hardware):
    hardware = IHardware(hardware)

而已。如果hardware直接提供IHardware接口,则原样返回。如果是RGB实例,则在注册表中找到适配器;适配器被创建(RGBtoHardwareAdapter(hardware)被调用)并将一个IHardware对象一样工作。

你也可以让你的适配器返回一个实际的Hardware()对象;上面的例子改为返回一个代理对象,但原理是一样的。

Python 的抽象基类从不同的方向处理接口,即鸭式。您可以测试给定对象是否符合 ABCs 方法和属性。但是,它不提供适应。

于 2013-10-02T07:30:32.207 回答