Запрашивайте аппаратные средства во время выполнения и динамически создавайте атрибуты / свойства и установщики в `__init__`

#python #python-3.x

#python #python-3.x

Вопрос:

У меня есть функция со следующей подписью:

 def analog_out(device: str, analog_output: str, voltage: (int, float)):
    # does some hardware manipulation here, this part is working just fine,
    # but i will put a print here just to have something
    print(device, analog_output, voltage)
 

Короче говоря, для этого требуется три параметра.

У меня есть возможность опрашивать подключенное оборудование во время выполнения и определять «имена» analog_output подключенных устройств. Например, если я опрашиваю конкретное аппаратное обеспечение, я мог бы получить список строк ['ao0', 'ao1'] , каждую из которых было бы уместно поместить в analog_output функцию.

У меня определен класс, который обеспечит несколько удобный доступ, но я хотел бы сделать его более удобным. На данный момент использование класса удобства выглядит следующим образом:

 hw = Hardware()
hw.analog_out(analog_output='ai0', voltage=3.3)
 

Опять же, все вышесказанное работает просто отлично, но что-то меня беспокоит в интерфейсе. В идеале я хотел бы иметь что-то вроде:

 hw.ai0 = 3.3
 

Я могу опросить аппаратное обеспечение и настроить это в конструкторе Hardware класса, но, похоже, я не могу определить синтаксис, который работает. Я уверен, что __setattr__ это связано с чем-то или возможно self.__dict__ , но, похоже, я никак не могу сдвинуть дело с мертвой точки. Например, одна из моих осечек, предполагающая, что я пытаюсь записать в ao0 :

 self.__dict__['ao0'] = lambda v, d=device: analog_out(d, 'ao0', v)
 

что позволяет мне делать что-то вроде:

 hw.ao0(3.3)
 

но это не совсем то, что я ищу.

Ответ №1:

Это действительно пример использования для __setattr__ .

 class Hardware:
    def __setattr__(self, attr, value):
        self.__dict__[attr] = value


hw = Hardware()
hw.ai0 = 3.3
 

Это, вероятно, слишком общее, так как позволяет генерировать выходные данные «на лету». Вы можете исправить это, сверившись attr с разрешенным списком.

 class Hardware:
    def __init__(self, allowed_outputs):
        self.outputs = allowed_outputs

    def __setattr__(self, attr, value):
        if attr not in self.outputs:
            raise ValueError(f'Non-existent output {attr}')

        self.__dict__[attr] = value

hw = Hardware(["ao0", "ao1"])
 

Комментарии:

1. Вы могли бы добавить if hasattr(self, key): settatr(self, key, value) для защиты атрибутов текущего объекта

2. Спасибо за ответ. Кажется, что это правильно, но когда я запускаю его в python 3.7, кажется, что __setattr__ он запускается перед конструктором, поэтому я получаю сообщение об ошибке, поскольку self.outputs он еще не существует.

3. Хорошо, итак self.outputs , я отодвинулся от первой регистрации, __setattr__ и это, похоже, решило проблему. У меня есть другие переменные класса, которые устанавливались до self.outputs , так что они тоже мешали. Еще раз спасибо вам!