【问题标题】:Javascript style dot notation for dictionary keys unpythonic?字典键的Javascript样式点符号unpythonic?
【发布时间】:2010-09-18 10:49:00
【问题描述】:

我已经开始使用这样的结构:

class DictObj(object):
    def __init__(self):
        self.d = {}
    def __getattr__(self, m):
        return self.d.get(m, None)
    def __setattr__(self, m, v):
        super.__setattr__(self, m, v)

更新:基于此线程,我已将 DictObj 实现修改为:

class dotdict(dict):
    def __getattr__(self, attr):
        return self.get(attr, None)
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

class AutoEnum(object):
    def __init__(self):
        self.counter = 0
        self.d = {}
    def __getattr__(self, c):
        if c not in self.d:
            self.d[c] = self.counter
            self.counter += 1        
        return self.d[c]

其中 DictObj 是可以通过点符号访问的字典:

d = DictObj()
d.something = 'one'

我觉得它比d['something'] 更美观。请注意,访问未定义的键会返回 None 而不是引发异常,这也很好。

更新:Smashery 提出了一个很好的观点,mhawke 对此进行了扩展以获得更简单的解决方案。我想知道使用 dict 而不是定义新字典是否有任何不良副作用;如果没有,我非常喜欢 mhawke 的解决方案。

AutoEnum 是一个自增枚举,使用如下:

CMD = AutoEnum()

cmds = {
    "peek":  CMD.PEEK,
    "look":  CMD.PEEK,
    "help":  CMD.HELP,
    "poke":  CMD.POKE,
    "modify": CMD.POKE,
}

两者对我来说都很好,但我对它们感到很奇怪。

这些实际上是坏的结构吗?

【问题讨论】:

  • 非常好!类 dotdict 简约而优雅。我想我会使用它。然而,关于“AutoEnum”......我的问题是每个“键”的“值”在每次运行时可能不同,所以你不能将它用于存储在数据库或任何东西中的值(好吧,我想你可以保持值相同,如果你小心不要改变初始访问的顺序......但值不是明确的)
  • 等等!dotdict 有一个问题:如果您尝试使用作为 dict 方法名称的键,它就不起作用,例如:“items”、“keys” ,“获取”,“清除”,“复制”等......这有点问题。有没有办法解决?有没有办法“吃蛋糕也吃”? (能够调用 dict 方法,还能够在不改变语法的情况下使用“items”和“clear”等键?)。

标签: python coding-style


【解决方案1】:

如果您正在寻找处理嵌套字典的替代方法:

递归地将字典转换为所需类的实例

import json
from collections import namedtuple


class DictTransformer():
    @classmethod
    def constantize(self, d):
        return self.transform(d, klass=namedtuple, klassname='namedtuple')

    @classmethod
    def transform(self, d, klass, klassname):
        return self._from_json(self._to_json(d), klass=klass, klassname=klassname)

    @classmethod
    def _to_json(self, d, access_method='__dict__'):
        return json.dumps(d, default=lambda o: getattr(o, access_method, str(o)))

    @classmethod
    def _from_json(self, jsonstr, klass, klassname):
        return json.loads(jsonstr, object_hook=lambda d: klass(klassname, d.keys())(*d.values()))

例如:

constants = {
  'A': {
    'B': {
      'C': 'D'
    }
  }
}
CONSTANTS = DictTransformer.transform(d, klass=namedtuple, klassname='namedtuple')
CONSTANTS.A.B.C == 'D'

优点:

  • 处理嵌套字典
  • 可能会生成其他类
  • 命名元组为常量提供不变性

缺点:

  • 如果您的 klass 上没有提供 .keys.values,则可能无法回复(尽管您有时可以使用 ._fieldslist(A.B.C) 进行模仿)

想法?

h/t 发给 @hlzr 以获得原始课程的想法

【讨论】:

    【解决方案2】:

    您的 DictObj 示例实际上很常见。如果您正在处理“类似于对象的事物”,那么对象样式的点符号访问可能是一个胜利,即。它们具有固定的属性名称,仅包含在 Python 标识符中有效的字符。诸如数据库行或表单提交之类的东西可以有用地存储在这种对象中,从而使代码更具可读性,而无需过多的 ['item access']。

    实现有点受限——你不会得到 dict、len()、comparisons、'in'、iteration 或 nice reprs 这样的构造函数语法。你当然可以自己实现这些东西,但是在新风格类的世界里,你可以通过简单地继承 dict 来免费获得它们:

    class AttrDict(dict):
        __getattr__ = dict.__getitem__
        __setattr__ = dict.__setitem__
        __delattr__ = dict.__delitem__
    

    要获得 default-to-None 行为,只需子类化 Python 2.5 的 collections.defaultdict 类而不是 dict。

    【讨论】:

    • 我用 defaultdict 试过这个,但它仍然没有返回 None 为 underfined:
       >>> d = AttrDict() >>> d.me Traceback (最近一次调用最后): File " ",第 1 行,在  KeyError: 'me' 
    • 我选择了一个稍微修改过的版本,现在包含在上面的更新问题中。
    • defaultdict 为我工作 - 你记得在构造函数中包含工厂函数吗? IE。 d = defaultdict(lambda:无)。如果你忘记了那个位 defaultdict 就像一个普通的 dict 一样工作。您也可以覆盖 AttrDict 中的 init 以自动执行,也许。
    【解决方案3】:

    不要忽视Bunch

    它是字典的子类,可以导入 YAML 或 JSON,或将任何现有字典转换为 Bunch,反之亦然。一旦“bunchify”了,字典就会获得点符号,而不会丢失任何其他字典方法。

    【讨论】:

    • bunch 已退役,现在首选替换为 munch (pypi.python.org/pypi/munch)。使用Munch() 而不是Bunch()munchify 而不是bunchify
    【解决方案4】:

    thisthis 之间存在对称性答案:

    class dotdict(dict):
        __getattr__= dict.__getitem__
        __setattr__= dict.__setitem__
        __delattr__= dict.__delitem__
    

    相同的接口,只是反过来实现...

    class container(object):
        __getitem__ = object.__getattribute__
        __setitem__ = object.__setattr__
        __delitem__ = object.__delattr__
    

    【讨论】:

      【解决方案5】:

      因为你要求不受欢迎的副作用:

      一个缺点是在像 eclipse+pyDev 这样的可视化编辑器中,你会在使用点符号的行上看到许多未定义的变量错误。 Pydef 将无法找到这样的运行时“对象”定义。而在普通字典的情况下,它知道您只是在获取字典条目。

      您需要 1) 忽略这些错误并忍受红十字; 2) 使用 #@UndefinedVariable 逐行抑制这些警告或 3) 完全禁用未定义​​变量错误,导致您错过真正的未定义变量定义。

      【讨论】:

        【解决方案6】:

        使用像 DictObj 这样的东西的一个主要缺点是你要么必须限制允许的键,要么你的 DictObj 上不能有方法,例如 .keys().values().items() 等。

        【讨论】:

          【解决方案7】:

          这样做并没有“错误”,如果您的字典在某些时候很有可能变成对象,那就更好了,但首先要警惕使用括号访问的原因:

          1. 点访问不能使用关键字作为键。
          2. 点访问必须在键中使用 Python 标识符有效字符。
          3. 字典可以包含任何可散列的元素,而不仅仅是字符串。

          另外请记住,如果您决定稍后切换到对象,则始终可以让您的对象像字典一样访问。

          对于这样的情况,我会默认使用“可读性很重要”的口头禅:大概其他 Python 程序员会阅读您的代码,他们可能不会期望到处都有字典/对象混合。如果对于特定情况这是一个好的设计决策,请使用它,但我不会在没有必要的情况下使用它。

          【讨论】:

            【解决方案8】:

            关于DictObj,以下内容对您有用吗?空白类将允许您任意添加或替换容器对象中的内容。

            class Container(object):
                pass
            
            >>> myContainer = Container()
            >>> myContainer.spam = "in a can"
            >>> myContainer.eggs = "in a shell"
            

            如果你想在没有属性的时候不抛出AttributeError,你怎么看下面的?就个人而言,为了清楚起见,我更喜欢使用 dict,或者使用 try/except 子句。

            class QuietContainer(object):
                def __getattr__(self, attribute):
                    try:
                        return object.__getattr__(self,attribute)
                    except AttributeError:
                        return None
            
            >>> cont = QuietContainer()
            >>> print cont.me
            None
            

            对吗?

            【讨论】:

            • 我同意创建一个类似乎是一种更简洁的方式来处理他正在尝试的事情。
            • 关键的区别是我不想在之前没有定义密钥时抛出异常。
            • Kevin 示例的哪一部分引发了异常?
            • @Robert:您尝试“print myContainer.blah”的部分会引发 AttributeError。在 Parand 的 DictObj 中,“d.blah”将返回 None,没有例外。
            • 定义一个特殊的 getattr 方法怎么样?它可以捕获异常并且不返回任何异常。让我修复我的代码以包含它。
            【解决方案9】:

            如果它符合您的目的,那还不错。 “实用胜于纯粹”。

            我在其他地方看到过这种方法(例如在Paver),所以这可以被认为是共同需求(或愿望)。

            【讨论】:

              【解决方案10】:

              这是您的 DictObj 类的更简单版本:

              class DictObj(object):
                  def __getattr__(self, attr):
                      return self.__dict__.get(attr)
              
              >>> d = DictObj()
              >>> d.something = 'one'
              >>> print d.something
              one
              >>> print d.somethingelse
              None
              >>> 
              

              【讨论】:

              • 使用 __dict__ 有什么副作用吗?如果没有,我喜欢它。
              • 如果我记得,dict 在技术上并不是 python 规范的一部分。也就是说,其他虚拟机不需要实现。我还记得所有当前的虚拟机(IronPython、Jython、PyPy)实际上确实使用dict作为对象字典。
              • 这很糟糕,我更喜欢问题中的dotdict很多dotdict 是一个实际的字典,因此可以自然地转换为 JSON,而你的这个对象不是。
              【解决方案11】:

              我个人比字典字段更喜欢点符号。原因是它使自动完成功能更好。

              【讨论】:

                【解决方案12】:

                据我所知,Python 类无论如何都使用字典来存储它们的属性(这对程序员来说是隐藏的),所以在我看来,你在那里所做的实际上是模拟一个 Python 类......使用 python班级。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-10-18
                  • 1970-01-01
                  • 2019-01-09
                  • 2018-05-23
                  相关资源
                  最近更新 更多