5

我有以下 Python 2.6 程序和 YAML 定义(使用PyYAML):

import yaml

x = yaml.load(
    """
        product:
           name     : 'Product X'
           sku      : 123
           features :
             - size    :  '10x30cm'
               weight  :  '10kg'

         """
    )

print type(x)
print x


这导致以下输出:
<type 'dict'>
{'product': {'sku': 123, 'name': 'Product X', 'features': [{'weight': '10kg', 'size': '10x30cm'}]}}

x可以使用来自?的字段创建对象

我想要以下内容:

print x.features[0].size

我知道可以从现有类创建和实例化,但这不是我想要的这个特定场景。

编辑:

  • 更新了关于“强类型对象”的令人困惑的部分。
  • features按照 Alex Martelli 的建议更改了对索引器的访问
4

1 回答 1

8

因此,您有一个带有字符串键和值的字典,可以是数字、嵌套字典、列表,并且您希望将其包装到一个实例中,该实例允许您使用属性访问代替 dict 索引,并“使用索引调用”代替列表索引-不确定“强类型”与此有什么关系,或者为什么您认为.features(0).features[0](索引列表的这种更自然的方式!)更好,但是,当然,这是可行的。例如,一个简单的方法可能是:

def wrap(datum):
  # don't wrap strings
  if isinstance(datum, basestring):
    return datum
  # don't wrap numbers, either
  try: return datum + 0
  except TypeError: pass
  return Fourie(datum)

class Fourie(object):
  def __init__(self, data):
    self._data = data
  def __getattr__(self, n):
    return wrap(self._data[n])
  def __call__(self, n):
    return wrap(self._data[n])

所以x = wrap(x['product'])应该给你你的愿望(当你的整体逻辑显然需要时,为什么你想跳过那个级别x.product.features(0).size,我不知道,但很明显,跳过更好地应用在调用点而不是硬编码在包装类或包装中我刚刚展示的工厂函数)。

编辑:正如 OP 所说,他确实想要features[0]而不是features(0),只需将最后两行更改为

  def __getitem__(self, n):
    return wrap(self._data[n])

即,定义__getitem__(底层索引的魔术方法)而不是__call__(底层实例调用的魔术方法)。

“现有类”(此处,Fourie)的替代方法是基于内省包装的字典动态创建一个新类——也是可行的,但严重的深灰色,如果不是真的黑色,魔法,并且没有任何我能想到的真正的运营优势。

如果 OP 可以准确地澄清为什么他可能渴望在动态创建类的元编程高峰之后,他认为他可能会获得什么优势,等等,我将展示如何做到这一点(并且,可能,我还将说明为什么渴望的优势实际上存在;-)。但是在任何编程工作中,简单性都是一个重要的品质,当像上面这样简单、直接的代码工作得很好时,使用“深奥的魔法”就可以了,通常不是最好的主意!-)

于 2010-03-14T17:17:13.450 回答