Python如何创建包装任何值的类

时间:2019-05-09 01:54:01

标签: python

假设我有一个Entity班:

class Entity(dict):
    pass

    def save(self):
        ...

我可以用Entity(dict_obj)包裹字典对象

但是有可能创建一个可以包装任何类型对象的类,例如。 int,列表等。

我提出了以下解决方法,它不适用于更复杂的对象,但似乎可以与基本对象一起使用,完全不确定是否有陷阱,可能会因创建每个类而受到效率的影响。时间,请让我知道:

class EntityMixin(object):

    def save(self):
        ...

def get_entity(obj):
    class Entity(obj.__class__, EntityMixin):
        pass

    return Entity(obj)

用法:

>>> a = get_entity(1) 
>>> a + 1
2
>>> b = get_entity('b')
>>> b.upper()
'B'
>>> c = get_entity([1,2])
>>> len(c)
2
>>> d = get_entity({'a':1})
>>> d['a']
1
>>> d = get_entity(map(lambda x : x, [1,2]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jlin/projects/django-rest-framework-queryset/rest_framework_queryset/entity.py", line 11, in get_entity
    return Entity(obj)
TypeError: map() must have at least two arguments.

提高效率:

EntityClsCache = {}

class EntityMixin(object):

    def save(self):
        ...

def _get_entity_cls(obj):

    class Entity(obj.__class__, EntityMixin):
        pass

    return Entity

def get_entity(obj)
    cls = None
    try:
       cls = EntityClsCache[obj.__class__]
    except AttributeError:
       cls = _get_entity_cls(obj)
       EntityClsCache[obj.__class__] = cls
    return cls(obj)

1 个答案:

答案 0 :(得分:0)

您提出的解决方案看起来不错,但是它缺乏缓存,因为即使类型相同,每次调用get_entity()时,您都将构造一个唯一的类。

Python具有元类,它们充当类工厂。鉴于元类的方法覆盖了而不是实例的方法,我们可以实现类缓存:

class EntityMixin(object):
    pass


class CachingFactory(type):
    __registry__ = {}

    # Instead of declaring an inner class,
    # we can also return type("Wrapper", (type_, EntityMixin), {}) right away,
    # which, however, looks more obscure
    def __makeclass(cls, type_):
        class Wrapper(type_, EntityMixin):
            pass

        return Wrapper

    # This is the simplest form of caching; for more realistic and less error-prone example,
    # better use a more unique/complex key, for example, tuple of `value`'s ancestors --
    # you can obtain them via type(value).__mro__
    def __call__(cls, value):
        t = type(value)
        typename = t.__name__
        if typename not in cls.__registry__:
            cls.__registry__[typename] = cls.__makeclass(t)
        return cls.__registry__[typename](value)


class Factory(object):
    __metaclass__ = CachingFactory

这样,Factory(1)执行Factory.__call__(1),即CachingFactory.__call__(1)(如果没有元类,那将是一个构造函数调用,这将导致一个类实例-但是我们想要首先创建一个类,然后实例化它。

我们可以确保Factory创建的对象是同一类的实例,这是第一次为它们专门设计的:

>>> type(Factory(map(lambda x: x, [1, 2]))) is type(Factory([1]))
True
>>> type(Factory("a")) is type(Factory("abc"))
True
相关问题