从 __init__ 中的列表/属性在 class 中创建属性
Create properties in class from list / attribute in __init__
我有一个硬件,我写了一个 class,它有多个输入。每个输入(通道)都有一个名称,所以我创建了一个列表:
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
字典中。或者,还有更好的方法?
因为我只想读取数值,所以我就到此为止了。但是有没有办法将它与 setter 结合起来呢?
编辑:
我必须澄清:我不想在运行时更改字典或访问字典的值,我想在 class 创建时使用它来创建多个属性供硬件读取硬件值。
硬件是带通道的ADC。我可以用
读取每个通道的 ADC 值
someclass._query("REQUEST STRING i")
# i is the channel number and returns the ADC value (e.g. 10.45 V)
我认为这就是你想要的:
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__
.
如果您真的想动态创建函数并使它们成为您的 class 实例的成员,您可以使用 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__
解决方案解决了第一点但没有解决第二点。这里最简单的解决方案是使用 class 装饰器,它将在 class 本身上添加吸气剂(作为方法或属性,由您决定),即(属性版本):
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)
或任何其他内省机制。
我有一个硬件,我写了一个 class,它有多个输入。每个输入(通道)都有一个名称,所以我创建了一个列表:
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
字典中。或者,还有更好的方法?
因为我只想读取数值,所以我就到此为止了。但是有没有办法将它与 setter 结合起来呢?
编辑:
我必须澄清:我不想在运行时更改字典或访问字典的值,我想在 class 创建时使用它来创建多个属性供硬件读取硬件值。
硬件是带通道的ADC。我可以用
读取每个通道的 ADC 值someclass._query("REQUEST STRING i")
# i is the channel number and returns the ADC value (e.g. 10.45 V)
我认为这就是你想要的:
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__
.
如果您真的想动态创建函数并使它们成为您的 class 实例的成员,您可以使用 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__
解决方案解决了第一点但没有解决第二点。这里最简单的解决方案是使用 class 装饰器,它将在 class 本身上添加吸气剂(作为方法或属性,由您决定),即(属性版本):
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)
或任何其他内省机制。