使用type和__slots__创建动态类?

时间:2015-04-21 18:35:03

标签: python class python-2.7 dynamic

在类中创建结果对象时,是否可以在此示例中使用__slots__?我以为我可以通过将'__slots__'传递到第type的第三个参数的字典中来实现它:

class GeocodeResult(object):
    """class to handle Reverse Geocode Result"""

    __slots__ = ['geometry', 'response', 'spatialReference',
                'candidates', 'locations', 'address', 'type', 'results']

    def __init__(self, res_dict, geo_type):
        RequestError(res_dict)
        self.response = res_dict
        self.type = 'esri_' + geo_type
        self.spatialReference = None
        self.candidates = []
        self.locations = []
        self.address = []
        if 'spatialReference' in self.response:
            self.spatialReference = self.response['spatialReference']

        # more stuff

    @property
    def results(self):
        results = []
        for result in self.address + self.candidates + self.locations:
            result['__slots__'] = ['address', 'score', 'location', 'attributes']
            results.append(type('GeocodeResult.result', (object,), result))
        return results

    def __len__(self):
        """get length of results"""
        return len(self.results)

对于results属性,我想构建一个包含4个属性的小型轻量级对象列表:['address', 'score', 'location', 'attributes']

创建了生成的对象,我甚至可以访问插槽,但它仍然保留__dict__。由于可能有数百个结果对象,我只想要上面列出的四个属性来节省空间。

示例:

>>> rev = GeocodeResult(r, 'reverseGeocode')
>>> result = rev.results[0]
>>> result.__slots__
['address', 'score', 'location', 'attributes']
>>> hasattr(result, '__dict__')
True
>>> 

有更好的方法吗?或者我是否必须定义一个显式类来处理这个问题?

2 个答案:

答案 0 :(得分:9)

完全可以使用插槽动态创建类:

>>> C = type('C', (), {'__slots__': ('a', 'b')})
>>> C.a
<member 'a' of 'C' objects>
>>> dir(C())
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a', 'b']
>>> vars(C())
Traceback (most recent call last):
  ...
TypeError: vars() argument must have __dict__ attribute

适用于Python 3和2。

您在示例中看到hasattr(result, '__dict__')评估True,因为GecodeResult.results返回的列表是类型列表,而不是实例列表。如果你说result().__dict__,你会得到AttributeError

(另外值得注意的是:该列表中的每个类型都共享名称&#34; GeocodeResult.result&#34;,但它们不是相同的类型!results[0].__class__ == results[1].__class__是{{ 1}}。)

正如jonrsharpe所指出的,最好只定义一次类型并重复使用它,而namedtuple对于这项工作来说是完美的,所以坚持下去:)

答案 1 :(得分:1)

不,我认为没有办法使用type的三参数形式创建__slots__的对象;每the docs

   dict 字典[第三个参数]是包含类体定义的命名空间,并成为__dict__属性。

我不确定你需要,但是;为什么不用__slots__一次定义类,因为它们看起来都是一样的,只返回一个实例列表?也就是说,我倾向于使用collections.namedtuple来做这件事;它们非常轻量级,仍可通过属性访问其内容:

class GeocodeResult(object):
    """class to handle Reverse Geocode Result"""

    ...

    Result = namedtuple(
        'Result', 
        'address score location attributes',
    )

    @property
    def results(self):
        results = []
        for result in self.address + self.candidates + self.locations:
            results.append(self.Result(...))  # pass in the four relevant attributes
        return result

    ...

您也可以使用list comprehension简化属性。