2016-07-08 14 views
0

У меня есть аппаратное обеспечение. Я пишу класс, для которого есть несколько входов. Каждый вход (канал) имеет имя, поэтому я создал список:Создать свойства в классе из списка/атрибута в __init__

CHANNELS = {"T0": 0, "Tend": 1, "A": 2, "B": 3, "C" : 4, "D" : 5, 
    "E" : 6, "F" : 7, "G" : 8, "H" : 9} 

Теперь я хотел бы создать свойство для доступа к значению каждого канала:

@property 
def readT0(self) 
    return self._query("REQUEST STRING 0") 
    # the request string expects a number, instead of the real name T0 

@property 
def readTend(self) 
    return self._query("REQUEST STRING 1") 

и т.д.

я предпочел бы сделать что-то вроде этого (меньше исходного кода):

def read(self, channel) 
    return self._query("REQUEST STRING %s" % channel) 

и использовать какое-то перевод, чтобы создать Атрибуты:

def __init__ bla bla: 
    bla bla 

    for x in CHANNELS: 
     setattr(self, "read" + str(x), self.read(CHANNELS[x]) 

так

class.readA # channel A value: 10 
> 10 
class.readT0 # channel T0 value: 0.11 
> 0.11 

Таким образом, если аппаратное обеспечение использует больше каналов, я могу просто добавить к CHANNELS словаря. Или есть лучший способ?

Поскольку я только хочу читать ценности, я бы остановился здесь. Но есть ли способ сочетать это с сеттера?

редактировать:

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

Аппаратное обеспечение - это АЦП с каналами. Я могу прочитать значение АЦП каждого канала с

someclass._query("REQUEST STRING i") 
# i is the channel number and returns the ADC value (e.g. 10.45 V) 
+0

Почему вы хотите «читать» там? Разве 'class.A0' не был бы более аккуратным? Как '_query' соответствует словарю? – jonrsharpe

+0

Привет, 'class.T0' или' class.A' действительно был бы более аккуратным, но каналы имеют разные свойства, поэтому 'read' вполне может измениться на' class.delayT0' и 'class.offsetT0' ... Don ' t знать, что вы подразумеваете под соответствием: '_query()' является функцией com для чтения с аппаратного обеспечения. – Wolf82

+1

не используют свойства для чего-то, что имеет побочный эффект, это ужасно, вместо этого используйте методы. Помните [zen of python] (https://www.python.org/dev/peps/pep-0020/): явное лучше, чем неявное. Свойство предназначено для скрытия небольшой операции над атрибутом, а не для маскировки дорогостоящего метода в простом атрибуте. –

ответ

1

Если вы действительно хотите, чтобы динамически создавать функции и сделать их членами экземпляров класса, вы можете использовать lambda:

CHANNELS = {"T0": 0, "Tend": 1, "A": 2, "B": 3, "C" : 4, "D" : 5, 
    "E" : 6, "F" : 7, "G" : 8, "H" : 9} 

class Foo(object): 
    def __init__(self): 
     for x in CHANNELS: 
      setattr(self, "read{}".format(x), lambda x=x: self.read(CHANNELS[x])) 

    def read(self, channel): 
     return self._query("REQUEST STRING {}".format(channel)) 

    def _query(self, q): 
     print "query {}".format(q) 


f = Foo() 
f.readT0() 
f.readTend() 
f.readA() 
f.readB() 
f.readC() 

Он работает, но есть несколько недостатков:

  • он создает тот же набор функций для каждого и любого экземпляра (без всяких причин в этом случае, поскольку все экземпляры основаны на том же определении CHANNELS)
  • эти функции не документированы и не будут появляться в dir(Foo) или help(Foo)

__getattr__ решение jonsharpe в решает первую точку, но не второй. Самым простым решением здесь является использование класса декоратора, который добавит методы получения (либо методов или свойств, это до вас) на самом классе, то есть (свойства версии):

def with_channel_props(cls): 
    for x in cls.CHANNELS: 
     getter = lambda self, x=x: self.read(self.CHANNELS[x]) 
     setattr(cls, "{}".format(x), property(getter)) 
    return cls 


@with_channel_props 
class Baaz(object): 
    CHANNELS = { 
     "T0": 0, "Tend": 1, "A": 2, "B": 3, "C" : 4, "D" : 5, 
     "E" : 6, "F" : 7, "G" : 8, "H" : 9 
     } 

    def read(self, channel): 
     return self._query("REQUEST STRING {}".format(channel)) 

    def _query(self, q): 
     return "query {}".format(q) 

b = Baaz() 
print b.T0 
print b.Tend 
print b.A 
print b.B 
print b.C 

Теперь вы можете использовать dir(Baaz) и help(Baaz) или любой другой механизм самоанализа.

-1

Я думать это то, что вы хотите:

class Something(object): 

    CHANNELS = {"T0": 0, "Tend": 1, "A": 2, "B": 3, "C": 4, "D": 5, 
       "E": 6, "F": 7, "G": 8, "H": 9} 

    def __getattr__(self, name): 
     """Handle missing attributes.""" 
     if name.startswith('read') and name[4:] in self.CHANNELS: 
      return self.CHANNELS[name[4:]] 
     return super(Something, self).__getattribute__(name) 

При использовании:

>>> thing = Something() 
>>> thing.readA 
2 
>>> thing.readT0 
0 
>>> thing.garbage 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 8, in __getattr__ 
AttributeError: 'Something' object has no attribute 'garbage' 

Вы можете использовать эти каналы атрибуты в вашем вызове _query по мере необходимости. Если вы также хотите назначить словарь, вам нужно будет реализовать __setattr__.

+0

Привет, я не хочу читать из словаря, но с входа АЦП. CHANNELS только для перевода с 'A' до 2, потому что аппаратное обеспечение использует номера в качестве идентификаторов каналов. – Wolf82

+0

@ Wolf82 да, это просто показывает вам, как использовать '__getattr__' для обработки отсутствующих атрибутов и получения соответствующего номера канала из словаря. Интегрировать это в свою базу кода - это ваша работа! – jonrsharpe