问题
What is the cleanest way to define a class with a large number of properties, each of which calls the same setter function with a different parameter?
Below is what I'm trying to achieve. Each of the three properties (red_LED etc) call the same TalkToHardware() function when they are set, but with a different address. As you can see, this works, but the class definition is long and unwieldy. It is also error prone.
class Hardware_Controller(object):
def __init__(self):
self.red_LED_address = 0
self.blue_LED_address = 1
self.green_LED_address = 2
@property
def red_LED(self):
return self._red_LED_status
@red_LED.setter
def red_LED(self,value):
self.TalkToHardware(self.red_LED_address,value)
self._red_LED_status = value
@property
def blue_LED(self):
return self._red_LED_status
@blue_LED.setter
def blue_LED(self,value):
self.TalkToHardware(self.blue_LED_address,value)
self._blue_LED_status = value
@property
def green_LED(self):
return self._red_LED_status
@green_LED.setter
def green_LED(self,value):
self.TalkToHardware(self.green_LED_address,value)
self._green_LED_status = value
def TalkToHardware(self,address,value):
print('Sending %i to address %i' % (value,address))
if __name__ == "__main__":
a = Hardware_Controller()
a.red_LED = 1
a.green_LED = 0
print(a.red_LED)
Output:
Sending 1 to address 0
Sending 0 to address 2
1
I would like the class definition to look more like this:
class Hardware_Controller(object):
def __init__(self):
self.red_LED_address = 0
self.blue_LED_address = 1
self.green_LED_address = 2
self.red_LED = some_magic(self.red_LED_address)
self.blue_LED = some_magic(self.blue_LED_address)
self.green_LED = some_magic(self.green_LED_address)
def some_magic(address):
# Do magic things
pass
Is there a clean way to accomplish this, still being able to access each of the LEDs directly as shown in the main function of the first example?
回答1:
You could create a function that simply defines and returns the needed property
class instance and then use it to define the class, thus getting rid of all that repetitive code ✶.
(In case it's not obvious, the LED_property()
function in the code below corresponds to the one named some_magic()
in your question.)
class Hardware_Controller(object):
def LED_property(color_name):
""" Create and return a property for the given color_name. """
address_name = color_name + '_LED_address'
storage_name = '_' + color_name + '_LED_status'
@property
def prop(self):
return getattr(self, storage_name)
@prop.setter
def prop(self, value):
address = getattr(self, address_name)
self.TalkToHardware(address, value)
setattr(self, storage_name, value)
return prop
red_LED = LED_property('red')
blue_LED = LED_property('blue')
green_LED = LED_property('green')
def __init__(self):
self.red_LED_address = 0
self.blue_LED_address = 1
self.green_LED_address = 2
def TalkToHardware(self, address, value):
print('Sending %i to address %i' % (value, address))
del LED_property # Function isn't needed outside class definition.
if __name__ == "__main__":
a = Hardware_Controller()
a.red_LED = 1 # -> Sending 1 to address 0
a.green_LED = 0 # -> Sending 0 to address 2
print(a.red_LED) # -> 1
✶ Similar to recipe "9.21 Avoiding Repetitive Property Methods" in the Python Cookbook, Third Edition by David Beazley & Brian Jones (2013). I found a PDF of the entire book (8.8 Mb), the recipe's on page 382.
回答2:
Perhaps you can do this:
def setter(self, address, value):
if address == self.red_LED_address:
attr = '_red_LED_status'
if address == self.blue_LED_address:
attr = '_blue_LED_status'
if address == self.green_LED_address:
attr = '_green_LED_status'
self.TalkToHardware(address, value)
setattr(self, attr, value) ## does this: self._XXX_LED_address = value
This is a catch-all function for all of your LED setter functions.
I hope this was helpful!
来源:https://stackoverflow.com/questions/49888437/multiple-properties-with-similar-getter-setter-functions