I have a situation where an object has 15+ parameters where some are mandatory/optional/have defaults/don't have defaults. Let's say the class is Foo
and the params are Bar1
, Bar2
, ..., BarN
.
I could program the class like:
the traditional, tedious way. Easy to define defaults and verbose. However, code will be long and have lots of repetition.
class Foo(object): def __init__(self, bar1, bar2,...barN='somedefault' ): self.bar1 = bar1 self.bar2 = bar2 ... self.barN = barN
abusing(?) the
kwargs
. Does not support defaults.class Foo(object): fields = [ 'bar1', 'bar2', ...n 'barN'] def __init__(self, **kwargs ): for key in kwargs: if key in self.fields: setattr(self, key, kwargs[key]) else: raise Exception('invalid attribute: {0}'.format(key))
For number 2, I could probably change the fields
class attribute into a map { 'attribute_name': 'default_value' }
and then assign the default_value
if that key was not in kwargs
.
The context where I'm working is that I'm creating objects that correspond to servers, firewall rules, storage disks, etc., that have a lot of attributes. In a lot of cases I get the attributes from the API so I can do number 2 (even without the validation). However, sometimes a user will create a firewall rule, attach it to a server and send it to an API (POST with JSON body).
Is there some standard/pythonic way to handle objects with a large amount of attributes? Does the approach in #2 seem good or should I really go with the verbose #1 way?
EDIT: one concrete example is a firewall rule object that has the following parameters:
action
destination_address_end
destination_address_start
destination_port_end
destination_port_start
direction
family
icmp_type
position
protocol
source_address_end
source_address_start
source_port_end
source_port_start
Right now I'm leaning towards something like:
class FirewallRule(object):
attributes = {
'action': 'default_value',
'destination_address_end': 'default_value',
'destination_address_start': 'default_value',
'destination_port_end': 'default_value',
'destination_port_start': 'default_value',
'direction': 'default_value',
'family': 'default_value',
'icmp_type': 'default_value',
'position': 'default_value',
'protocol': 'default_value',
'source_address_end': 'default_value',
'source_address_start': 'default_value',
'source_port_end': 'default_value',
'source_port_start': 'default_value'
}
def __init__(self, **kwargs ):
for key in kwargs:
valur_or_default = kwargs.get(key, attributes[key])
setattr(self, key, valur_or_default)
(In production code I'd obviously add better error handling and validation)