How to “perfectly” override a dict?

后端 未结 5 1881
情话喂你
情话喂你 2020-11-22 08:14

How can I make as \"perfect\" a subclass of dict as possible? The end goal is to have a simple dict in which the keys are lowercase.

It would seem

5条回答
  •  孤街浪徒
    2020-11-22 08:51

    After trying out both of the top two suggestions, I've settled on a shady-looking middle route for Python 2.7. Maybe 3 is saner, but for me:

    class MyDict(MutableMapping):
       # ... the few __methods__ that mutablemapping requires
       # and then this monstrosity
       @property
       def __class__(self):
           return dict
    

    which I really hate, but seems to fit my needs, which are:

    • can override **my_dict
      • if you inherit from dict, this bypasses your code. try it out.
      • this makes #2 unacceptable for me at all times, as this is quite common in python code
    • masquerades as isinstance(my_dict, dict)
      • rules out MutableMapping alone, so #1 is not enough
      • I heartily recommend #1 if you don't need this, it's simple and predictable
    • fully controllable behavior
      • so I cannot inherit from dict

    If you need to tell yourself apart from others, personally I use something like this (though I'd recommend better names):

    def __am_i_me(self):
      return True
    
    @classmethod
    def __is_it_me(cls, other):
      try:
        return other.__am_i_me()
      except Exception:
        return False
    

    As long as you only need to recognize yourself internally, this way it's harder to accidentally call __am_i_me due to python's name-munging (this is renamed to _MyDict__am_i_me from anything calling outside this class). Slightly more private than _methods, both in practice and culturally.

    So far I have no complaints, aside from the seriously-shady-looking __class__ override. I'd be thrilled to hear of any problems that others encounter with this though, I don't fully understand the consequences. But so far I've had no problems whatsoever, and this allowed me to migrate a lot of middling-quality code in lots of locations without needing any changes.


    As evidence: https://repl.it/repls/TraumaticToughCockatoo

    Basically: copy the current #2 option, add print 'method_name' lines to every method, and then try this and watch the output:

    d = LowerDict()  # prints "init", or whatever your print statement said
    print '------'
    splatted = dict(**d)  # note that there are no prints here
    

    You'll see similar behavior for other scenarios. Say your fake-dict is a wrapper around some other datatype, so there's no reasonable way to store the data in the backing-dict; **your_dict will be empty, regardless of what every other method does.

    This works correctly for MutableMapping, but as soon as you inherit from dict it becomes uncontrollable.


    Edit: as an update, this has been running without a single issue for almost two years now, on several hundred thousand (eh, might be a couple million) lines of complicated, legacy-ridden python. So I'm pretty happy with it :)

    Edit 2: apparently I mis-copied this or something long ago. @classmethod __class__ does not work for isinstance checks - @property __class__ does: https://repl.it/repls/UnitedScientificSequence

提交回复
热议问题