有没有一种紧凑的方法可以在python类中声明几个类似的属性?

时间:2018-10-09 21:07:18

标签: python properties

我正在一个较大的python模块中工作,可以保存包含类似dict的类实例的程序集的“项目”,但是为了保存类的属性,必须将它们存储在该类的特定字典中。通过将属性写为属性,可以将其作为简单属性(self.x而不是self.properties['x'])进行访问。但是我最多只能处理9件事,并且给它们分别一个getter,setter和Deleter似乎是对空间的浪费,特别是因为它们都很琐碎。有更好的方法吗?

太长:

class MyClass(dict):

    @property
    def variable1(self):
        return self.properties.get('variable1', None)

    @variable1.setter
    def variable1(self, value):
        self.properties['variable1'] = value

    @variable1.deleter
    def variable1(self):
        self.properties['variable1'] = None

    # ... same for variables 2 - 8, so boring

    @property
    def variable9(self):
        return self.properties.get('variable9', None)

    @variable9.setter
    def variable9(self, value):
        self.properties['variable9'] = value

    @variable9.deleter
    def variable9(self):
        self.properties['variable9'] = None

    def __init__(self, variable1='default1', variable9='default9'):
        self.properties = dict(variable1=variable1, variable9=variable9)
        dict.__init__(self)

如何遍历属性声明,这样会更短?

奖金:如果我循环执行,是否有必要在某些变量中包括次要自定义项(可能在字典中声明要列出的内容作为键,而值是次要自定义项的说明) ?

上面示例的测试/用法:

from var_test import MyClass  # If you saved it in var_test.py
a = MyClass(variable1=1234)
b = MyClass(variable1='wheee')
assert a.variable1 is not b.variable1
assert a.properties['variable1'] == a.variable1
assert a.variable1 == 1234

2 个答案:

答案 0 :(得分:2)

是的,您可以动态创建属性对象,并将其添加到类中:

def gen_property(name):
    def getter(self):
        return self.properties.get(name, None)
    def setter(self, value):
        self.properties[name] = value
    def deleter(self):
        self.properties[name] = None

    return property(getter, setter, deleter)

class MyClass(dict):
    def __init__(self, variable1='default1', variable9='default9'):
        self.properties = dict(variable1=variable1, variable9=variable9)
        dict.__init__(self)

for number in range(1, 10):
    name = 'variable{}'.format(number)
    setattr(MyClass, name, gen_property(name))

但是,使用attribute access customisation hooks并将属性名称代理到self.properties字典中可能更清洁:

class MyClass(dict):
    def __init__(self, variable1='default1', variable9='default9'):
        self.properties = dict(variable1=variable1, variable9=variable9)
        dict.__init__(self)

    def __getattr__(self, name):
        if name.startswith('variable') and name[8:].isdigit():
            return self.properties.get(name, None)
        raise AttributeError(name)

    def __setattr__(self, name, value):
        if name.startswith('variable') and name[8:].isdigit():
            self.properties[name] = value
            return
        super().__setattr__(name, value)

    def __delattr__(self, name):
        if name.startswith('variable') and name[8:].isdigit():
            self.properties[name] = None
            return
        super().__delattr__(name)

答案 1 :(得分:0)

我能做的最好的事情是循环调用trafficPolicy.loadBalancer.consistentHash.[httpHeaderName|httpCookie]来分配属性生成函数的结果。它比问题陈述中的内容短,但我觉得必须有更好的方法:

exec

这通过了测试:

import copy


class MyClass2(dict):
    properties = {}  # Temporary declaration of properties during startup
    save_attrs = ['variable{}'.format(i) for i in range(1, 10)]

    @staticmethod
    def _make_property(props, name, init_value=None, doc=None):
        """Creates a property which can be assigned to a variable, with getter, setter, and deleter methods"""
        props[name] = init_value

        def getter1(self):
            return self.properties.get(name, None)

        def setter1(self, value):
            self.properties[name] = value

        def deleter1(self):
            self.properties[name] = None

        getter1.__name__ = name
        setter1.__name__ = name
        deleter1.__name__ = name
        return property(getter1, setter1, deleter1, doc)

    for attr in save_attrs:
        exec("{attr:} = _make_property.__func__(properties, '{attr:}')".format(attr=attr))

    def __init__(self, **kw):
        # Instance-specific reassignment of properties, so instances won't share values
        self.properties = copy.deepcopy(self.properties)
        for attr in self.save_attrs:
            kw_attr = kw.pop(attr, None)
            if kw_attr is not None:
                self.properties[attr] = kw_attr
        dict.__init__(self)