-1

I have a class with a lot of attributes to be set. In order to do so, I do:

opts.a = 1
opts.b = 2
  # ...
opts.xyz = 0

After repeatedly writing opts. at the beginning of variables I am wondering: Is it possible to wrap this into a function or context so that the namespace is set to the class attributes so that I don't have to write opts all the time? I.e. that I only have to do something like:

with opts:
   a = 1
   b = 2
     # ...
   xyz = 3

I thought of moving the code inside a function of the class, but that doesn't make things easier to read or write, since then I'd need to write self instead of opts everytime.

One side condition for my case: the __setattr__ of the class should be called, since I did override that with a custom function to store the order in which attributes are set.

Honeybear
  • 2,928
  • 2
  • 28
  • 47
  • You can define a context manager to do that, but it is bad design. – Willem Van Onsem Apr 10 '18 at 12:39
  • 3
    If you have too many attributes, you might have a design issue rather perhaps a dict/list is more adapted? – Adonis Apr 10 '18 at 12:40
  • I don't get it. Why are `a`, `b`, ... not set in `__init__`? If you allow default arguments there, you won't have to supply all the attributes to the contructor. – timgeb Apr 10 '18 at 12:43
  • @hiroprotagonist no, I want `__setattr__` to be called. it stores the attr key in an ordered list so that I can call attributes in the order they were stored – Honeybear Apr 10 '18 at 12:46
  • @Adonis: Hm, maybe... I admit I am new to Python. I'm using the class as a "struct" in C, which was recommended somewhere on SO and worked fine so far `class opts: a = 1; b = 2 [ ... ] pass` and access via `opts.a`. However, now I want to store the order in which they are set, which is why I override `__setattr__` to keep a list of keys when they are set. But then I need to call `opts.a = 1` so that `__setattr__` is executed. – Honeybear Apr 10 '18 at 12:51
  • 1
    @Honeybear Maybe you should consider abandoning the class-as-enum approach in favour of an `OrderedDict`. – BoarGules Apr 10 '18 at 12:58
  • @BoarGules: Ye, I guess that is the way to go and what I'm asking here is probably not pythonic. I just like the `.key`-access much better than the `['key']`-access, but you can't have everything. – Honeybear Apr 10 '18 at 13:03
  • will delete this question after some time – Honeybear Apr 10 '18 at 13:04
  • Actually based on [this](https://stackoverflow.com/a/3387975/4121573) you just have to add in the `__setitem__` call `setattr(self,key,value)` and you should have a dict, and the possibility to call `my_transformed_dict.key` – Adonis Apr 10 '18 at 13:17

1 Answers1

2

If I take your question and the comments together, you want to use the class-as-enum approach, but with mutable values, you want a quick way to update multiple attributes, and you want to store the order in which the attributes are set.

Here is a partial implementation that does what I think you want:

class MyClass:
    def __init__(self):
        self.__dict__['values'] = collections.OrderedDict()
    def __setattr__ (self,a,v):
        self.__dict__['values'][a]=v
    def __getattr__ (self,a):
        return self.__dict__['values'][a]
    def __repr__(self):
        return repr(self.__dict__['values'])

Given this definition you can do:

>>> m = MyClass()
>>> m.z = 56
>>> m.y = 22
>>> m.b = 34
>>> m.c = 12
>>> m
OrderedDict([('z', 56), ('y', 22), ('b', 34), ('c', 12)])

Quick update:

>>> m.values.update(f=2, g=3, h=5)
>>> m
OrderedDict([('z', 56), ('y', 22), ('b', 34), ('c', 12), ('h', 5), ('g', 3), ('f', 2)])

It is true that the relative order of f-g is a bit surprising, but since the update happens in a single statement, there is an argument for saying that the updates are simultaneous and so the relative order is arbitrary.

BoarGules
  • 16,440
  • 2
  • 27
  • 44