Есть ли компактный способ объявить несколько похожих свойств в классе Python? - PullRequest
0 голосов
/ 10 октября 2018

Я работаю в более крупном модуле python, где могут быть сохранены «проекты», содержащие сборки экземпляров класса, похожих на dict, но для сохранения атрибутов класса они должны храниться в определенном словаре в классе.Я могу сделать мои атрибуты доступными в виде простых атрибутов (self.x вместо self.properties['x']), записав их как свойства.Но у меня до 9 таких вещей, и предоставление каждому из них методов получения, установки и удаления кажется такой пустой тратой пространства, тем более что они все такие тривиальные.Есть ли лучший способ?

Слишком долго:

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 голосов
/ 10 октября 2018

Лучшее, что я мог сделать, это перебрать вызов 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)

Это проходит тест:

from var_test import MyClass2 as mc  # If you saved it in var_test.py
a = mc(variable1=1234)
b = mc(variable1='wheee')
assert a.variable1 is not b.variable1
assert a.properties['variable1'] == a.variable1
assert a.variable1 == 1234
0 голосов
/ 10 октября 2018

Да, вы можете динамически создавать объекты свойств и добавлять их в класс:

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

Однако , вероятно, гораздо удобнее использовать настройку доступа к атрибуту перехватывает и передает имена атрибутов в словарь 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...