从__init __

时间:2016-07-08 10:58:13

标签: python properties setter getter setattribute

我有一个硬件,我写了一个有多个输入的类。每个输入(通道)都有一个名称,因此我创建了一个列表:

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字典即可。或者,还有更好的方法?

因为我只想读取值,所以我会在这里停下来。但是,有没有办法将它与制定者结合起来呢?

编辑:

我必须澄清:我不想在运行时更改字典或访问字典的值,我想在类创建时使用它来为硬件创建多个属性以读取硬件值。

硬件是带通道的ADC。我可以用

读取每个通道的ADC值
someclass._query("REQUEST STRING i")
# i is the channel number and returns the ADC value (e.g. 10.45 V)

2 个答案:

答案 0 :(得分: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)
jonsharpe的__getattr__解决方案解决了第一点而不是第二点。这里最简单的解决方案是使用类装饰器,它将在类本身上添加getter(作为方法或属性,它取决于你),即(属性版本):

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 :(得分:-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__