1

I have a class that I use as a container for parameters:

class Parameters:
    param1 = {'name': 'color', 'value': 'blue'}
    param2 = {'name': 'distance', 'value': 'far, far away'}
    .
    .
    .

Can I specify what values can be written to a parameter? For example color can be only blue and red, and if I do this:

params.param1['value'] = 'green'

it should fail.

In C++ I can create an enum and use it as a type, I guess similar functionality should be possible in Python too.

corwin
  • 77
  • 10
  • 4
    If you're asking *"does Python have an enum?"*, then did you try [`enum.Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)? In your case, though, a [`namedtuple`](https://docs.python.org/3/library/collections.html#collections.namedtuple) might be a better bet for storing items that will all have the same two attributes. But the short answer is: no, a Python dictionary can contain any arbitrary keys; you'd have to create your own class to implement that. – jonrsharpe Mar 23 '18 at 16:54
  • You can use Enum library Link: https://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python/1695250#1695250 – HAMZA MAJDI Mar 23 '18 at 17:06

2 Answers2

1

You could subclass dict and add your controls there. Then define param1 = ControlDict({...}).

class ControlDict(dict):
    def __init__(self, *args, **kwargs):
        self.permitted_vals = {'onekey': {1, 2, 3},
                               'value': {'blue', 'red'}}

    def update(self, *args, **kwargs):
        other = dict(args[0])
        for key in other:
            if other[key] not in self.permitted_vals[key]:
                raise ValueError("invalid value supplied for {0}".format(key))
            self[key] = other[key]


d = ControlDict({})

d.update({'value': 'blue'})
# works

d.update({'value': 'green'})
# ValueError: invalid value supplied for value
jpp
  • 159,742
  • 34
  • 281
  • 339
1

You could use a descriptor. The descriptor would be an attribute of the class accesible with dot notation.

from weakref import WeakKeyDictionary

class RestrictedColor:
    """A descriptor that restricts values"""
    def __init__(self, default):
        self.value = default
        self.data = WeakKeyDictionary()

    def __get__(self, instance, owner):
        return self.data.get(instance, self.value)

    def __set__(self, instance, value):
        if value.lower() not in ('blue','red'):
            raise ValueError(f'{value} is not allowed')
        self.data[instance] = value                            

class Parameters:
    color = RestrictedColor('blue')

Usage:

>>> p = Parameters()
>>> p.color
'blue'
>>> p.color = 'orange'
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    p.color = 'orange'
  File "foo.py", line 40, in __set__
    raise ValueError(f'{value} is not allowed')
ValueError: orange is not allowed
>>> p.color = 'red'
>>> p.color
'red'
>>> getattr(p, 'color')
'red'
>>> 

If you need Parameters instances to be subscriptable:

class BenignDescriptor:
    """A descriptor that accepts anything"""
    def __init__(self, default):
        self.value = default
        self.data = WeakKeyDictionary()
    def __get__(self, instance, owner):
        return self.data.get(instance, self.value)
    def __set__(self, instance, value):
        self.data[instance] = value                            

class Parameters:
    color = RestrictedColor('blue')
    distance = BenignDescriptor('far, far away')

    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)

Usage

>>> p = Parameters()
>>> p['color']
'blue'
>>> p.distance
'far, far away'
>>> p['distance'] = 'close'
>>> p['distance']
'close'
>>> 
wwii
  • 23,232
  • 7
  • 37
  • 77