3

理想的には、私が達成しようとしているのはdict、Pythonでaを拡張する(または非常に似ている)クラスであり、次の追加機能があります。

  • ドット-値の設定と取得が可能な表記
  • dict(つまり、setitem、getitem)のようなKey-Value機能
  • ドット表記の操作を連鎖させることができます

目標は、私がそれexample = DotDict()に対して次のことを行うことができるようなものを持っていてexample.configuration.first= 'first'、それが適切なDotDictインスタンスをインスタンス化するexample場合ですが、操作が割り当てられていない場合は、単純にKeyError同じように発生させる必要があるという非常に苦痛な警告がdictあります

これが私が素朴に組み立てたものです

class DotDict(dict):
    def __getattr__(self, key):
        """ Make attempts to lookup by nonexistent attributes also attempt key lookups. """
        import traceback
        import re
        s= ''.join(traceback.format_stack(sys._getframe(1),1))
        if re.match(r'  File.*\n.*[a-zA-Z]+\w*\.[a-zA-Z]+[a-zA-Z0-9_. ]*\s*=\s*[a-zA-Z0-9_.\'"]+',s):
            self[key] = DotDict()
            return self[key]

        return self[key]

    def __setattr__(self, key, value):
        if isinstance(value,dict):
            self[key] = DotDict(value)
        self[key] = value

いくつかの一般的なエッジケースを除いては機能しますが、私はこの方法が絶対に嫌いであり、より良い方法があるはずです。スタックを見て、最後の行で正規表現を実行することは、これを達成するための良い方法ではありません。

問題の核心は、Pythonがコードの行を左から右に解釈するため、a.b.c = 3最初の操作がagetattr(a,b)であり、setattrそうではないようなステートメントに到達したときに、操作のスタックの最後の操作が割り当てであるかどうかを簡単に判断できないことです。 。

私が知りたいのは、操作のスタックの最後の操作を決定する良い方法があるかどうか、または少なくともそれがであるかどうかsetattrです。

編集:

これは、user1320237の推奨のおかげで私が思いついた解決策です。

class DotDict(dict):
    def __getattr__(self, key):
        """ Make attempts to lookup by nonexistent attributes also attempt key lookups. """
        if self.has_key(key):
            return self[key]
        import sys
        import dis
        frame = sys._getframe(1)
        if '\x00%c' % dis.opmap['STORE_ATTR'] in frame.f_code.co_code:
            self[key] = DotDict()
            return self[key]

        raise AttributeError('Problem here')

    def __setattr__(self, key, value):
        if isinstance(value,dict):
            self[key] = DotDict(value)
        self[key] = value

実際の実装にはもう少しありますが、それは素晴らしい仕事をします。それが機能する方法は、スタックの最後のフレームを検査し、バイトコードでSTORE_ATTR操作をチェックすることです。これは、実行されている操作がa.b.this.doesnt.exist.yet = 'something'説得力があることを意味します。これがCPython以外の他のインタープリターで実行できるかどうか知りたいです。

4

5 に答える 5

3

これらのエッジケースのgetattributeを上書きしてから、

object.__getattribute__

モジュールdisを見てください。しかし、あなたが書いたものは、分解するよりも優れています。

>>> import dis
>>> def g():
    a.b.c = 4


>>> dis.dis(g)
  2           0 LOAD_CONST               1 (4)
              3 LOAD_GLOBAL              0 (a)
              6 LOAD_ATTR                1 (b)
              9 STORE_ATTR               2 (c)
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE        
于 2012-05-03T20:49:42.610 に答える
1

user1320237の回答に基づいて、これが私が得たものです。それはあなたが望むことをしているようです。

class A(object):
    def __getattribute__(self, attr):
        try:
            return super(A, self).__getattribute__(attr)
        except AttributeError:
            self.__setattr__(attr, A())
            return super(A, self).__getattribute__(attr)
于 2012-05-03T21:15:53.133 に答える
1

別の解決策は次のとおりです:http://tech.zarmory.com/2013/08/python-putting-dot-in-dict.html

>>> d = DefaultDotDict({1: {2: 3}})
>>> d.a.b.c.d = "magic!"
>>> import json; print json.dumps(d, indent=2)
{
  "a": {
    "b": {
      "c": {
        "d": "magic!"
      }
    }
  }, 
  "1": {
    "2": 3
  }
}
>>>
于 2013-08-21T07:22:24.820 に答える
0

ラッパーを使用した代替ソリューションを次に示します。

于 2012-10-19T08:13:12.750 に答える
0

クラスDotDict(dict):

"""Dot natation for dict"""

def __init__(self, theDict):
    super(MyDict, self).__init__()
    for key, item in theDict.items():
        if isinstance(item, dict):
            item = MyDict(item)
        self.__dict__[key] = item

def __getattr__(self, key):
    return self.__dict__[key]

def __setattr__(self, name, value):
    self.__dict__[name] = value
于 2016-11-16T06:17:47.297 に答える