Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I don't think you want inheritance here. You end up with two separate <code>dict</code> objects which you have to create on load and then paste back together on save…</p> <p>If that's acceptable, you don't even need to bother with the prefixing during normal operations; just do the prefixing while saving, like this:</p> <pre><code>class Config(object): def save(self): merged = {'variable_{}'.format(key): value for key, value in self.variable_dict.items()} merged.update({'routine_{}'.format(key): value for key, value in self.routine_dict.items()} # now save merged </code></pre> <p>If you want that <code>merged</code> object to be visible at all times, but don't expect to be called on that very often, make it a <code>@property</code>.</p> <p>If you want to access the <code>merged</code> dictionary regularly, at the same time you're accessing the two sub-dictionaries, then yes, you want a view:</p> <blockquote> <p>I suppose my ideal solution would be do dispense with the subclassed dict and have have the global and routine properties somehow present a "view" of the plain dict object underneath without the prefixes.</p> </blockquote> <p>This is going to be very hard to do with inheritance. Certainly not with inheritance from <code>dict</code>; inheritance from <code>builtins.dict_items</code> might work if you're using Python 3, but it still seems like a stretch.</p> <p>But with delegation, it's easy. Each sub-dictionary just holds a reference to the parent <code>dict</code>:</p> <pre><code>class PrefixedDict(object): def __init__(self, prefix, d): self.prefix, self.d = prefix, d def prefixify(self, key): return '{}_{}'.format(self.prefix, key) def __getitem__(self, key): return self.d.__getitem__(self.prefixify(key)) def __setitem__(self, key, value): return self.d.__setitem__(self.prefixify(key), value) def __delitem__(self, key): return self.d.__delitem__(self.prefixify(key)) def __iter__(self): return (key[len(self.prefix):] for key in self.d if key.startswith(self.prefix)]) </code></pre> <p>You don't get any of the <code>dict</code> methods for free that way—but that's a good thing, because they were mostly incorrect anyway, right? Explicitly delegate the ones you want. (If you do have some you want to pass through as-is, use <code>__getattr__</code> for that.)</p> <p>Besides being conceptually simpler and harder to screw up through accidentally forgetting to override something, this also means that <code>PrefixDict</code> can work with any type of mapping, not just a <code>dict</code>.</p> <hr> <p>So, no matter which way you go, where and how do these objects get created? </p> <p>The easy answer is that they're attributes that you create when you construct a <code>Config</code>:</p> <pre><code>def __init__(self): self.d = {} self.variable = PrefixedDict('Variable', self.d) self.routine = PrefixedDict('Routine', self.d) </code></pre> <p>If this needs to be dynamic (e.g., there can be an arbitrary set of prefixes), create them at load time:</p> <pre><code>def load(self): # load up self.d prefixes = set(key.split('_')[0] for key in self.d) for prefix in prefixes: setattr(self, prefix, PrefixedDict(prefix, self.d) </code></pre> <p>If you want to be able to create them on the fly (so <code>config.newprefix['foo'] = 3</code> adds <code>'Newprefix_foo'</code>), you can do this instead:</p> <pre><code>def __getattr__(self, name): return PrefixedDict(name.title(), self.d) </code></pre> <p>But once you're using dynamic attributes, you really have to question whether it isn't cleaner to use dictionary (item) syntax instead, like <code>config['newprefix']['foo']</code>. For one thing, that would actually let you call one of the sub-dictionaries <code>'global'</code>, as in your original question…</p> <p>Or you can first build the dictionary syntax, use what's usually referred to as an <code>attrdict</code> (search ActiveState recipes and PyPI for 3000 implementations…), which lets you automatically make <code>config.newprefix</code> mean <code>config['newprefix']</code>, so you can use attribute syntax when you have valid identifiers, but fall back to dictionary syntax when you don't.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. COIn this solution, is the `PrefixedDict` actually looking back at the `Config` instance's `d` attribute? It looks like it would just be copying in the contents of d at initialization time. If the `PrefixedDict` could actually reach back and modify the contents of the `Config` instance's main dictionary, this could be a great solution.
      singulars
    2. COIt _is_ reaching back. When you do `self.d = d`, that's just making another reference to the same `dict` object. So, as long as Config just mutates its `self.d` in-place (instead of rebinding it to a whole new `dict`), they're pointing at the same thing. Just like you need to do things like `d.copy()` or `copy.deepcopy(d)` to _avoid_ sharing when you don't want it, you just need to avoid calling those functions to benefit from sharing when you _do_ want it.
      singulars
    3. CO@JeffKlukas: If you're not quite getting that, look at it this way: You can always hold a reference to the parent `Config` object itself, and do `self.config.d`, right? (Don't do that, because that means a circular reference, which causes problems for the GC, but other than that, it works fine.) It's the same thing. A `dict` is an Python object, just like a `Config`. (I don't mean "object" in the sense of "instance of a subclass of `object`"—that's true in Ruby, Smalltalk, etc., but not Python. I mean "object" in the sense of "a thing that you can reference".)
      singulars
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload