Как я могу создать @property с динамическими податрибутами?

1

У меня есть класс, Tariff, с количеством и некоторыми ценами:

class Tariff():
    quantity = 0
    item_1_price = 250
    item_1_selected = False
    item_2_price = 350
    item_2_selected = False
    item_3_price = 165
    item_3_selected = False

    @property
    def total(self):
        return sum([
            self.item_1_price * self.item_1_selected,
            self.item_2_price * self.item_2_selected,
            self.item_3_price * self.item_3_selected,
            ]) * self.quantity

Я могу использовать его как таковой:

purchase = Tariff()
purchase.quantity = 2
purchase.item_1_selected = True
purchase.item_2_selected = True
print(purchase.total)
> 1200

Мне нужна функция, которая сообщит мне общее количество отдельного элемента. Например:

print(purchase.totals.item_1)
> 500

Каков наилучший способ обойти это? Это возможно/легко?

  • 2
    Один раз , когда вы писали total ( в единственном числе), другой раз , когда вы написали totals (множественное число). Предполагается, что это одно и то же свойство или два отдельных свойства?
  • 0
    @Aran-Fey Аран-Фей один - это общая сумма, один - атрибут со всеми промежуточными итогами в качестве податрибутов.
Показать ещё 5 комментариев
Теги:

2 ответа

1
Лучший ответ

Вы можете создать класс дескриптора, который возвращает подкласс int с __getattr__ методом __getattr__ чтобы получить общее количество данного отдельного элемента "на лету":

class Tariff:
    quantity = 0
    item_1_price = 250
    item_1_selected = False
    item_2_price = 350
    item_2_selected = False
    item_3_price = 165
    item_3_selected = False

    class TotalProperty:
        def __get__(self, obj, objtype):
            class Total(int):
                def __getattr__(self, item):
                    return getattr(obj, item + '_price') * getattr(obj, item + '_selected') * obj.quantity

            return Total(sum([
                obj.item_1_price * obj.item_1_selected,
                obj.item_2_price * obj.item_2_selected,
                obj.item_3_price * obj.item_3_selected,
                ]) * obj.quantity)

    total = TotalProperty()

чтобы:

purchase = Tariff()
purchase.quantity = 2
purchase.item_1_selected = True
purchase.item_2_selected = True
print(purchase.total)
print(purchase.total.item_1)
print(purchase.total.item_2)
print(purchase.total.item_3)

будет выводить:

1200
500
700
0
1

Прямым способом сделать это было бы:

@property
def total(self):
    return dict(
        sum=sum([ self.item_1_price * self.item_1_selected,
                  self.item_2_price * self.item_2_selected,
                  self.item_3_price * self.item_3_selected,
                ]) * self.quantity,
        item_1=item_1_price,
        item_2=item_2_price,
        item_3=item_3_price)

Тогда это вернет словарь, содержащий всю необходимую информацию. Чтобы получить доступ к элементам в dict, вам нужно будет использовать синтаксис индексации:

print(purchase.totals['item_1'])
> 500

Если вы можете жить с этим, это то, за что я бы пошел.

Если вам нужны атрибуты, вам нужно создать объект, который содержит эти атрибуты (или, по крайней мере, подделывает их). Посмотрите на collections.namedtuple для этого:

Totals = collections.namedtuple('total',
                                ['item_1', 'item_2', 'item_3', 'sum'])
return Totals(sum=sum([ self.item_1_price * self.item_1_selected,
                        self.item_2_price * self.item_2_selected,
                        self.item_3_price * self.item_3_selected,
                     ]) * self.quantity,
              item_1=item_1_price,
              item_2=item_2_price,
              item_3=item_3_price)

Тогда вы можете использовать:

print(purchase.totals.item_1)
> 500

Ещё вопросы

Сообщество Overcoder
Наверх
Меню