嵌套属性的Python风格切片
我在处理一些类,这些类的属性有时候是列表,而列表里的元素可能是字典或者更复杂的对象,里面还有属性等等。我想进行一些切片操作,但根据我对Python的理解,这样做感觉有点不太符合Python的风格。
我写的最简单的代码是这样的:
class X(object):
def __init__(self):
self.a = []
x=X()
x.a.append({'key1':'v1'})
x.a.append({'key1':'v2'})
x.a.append({'key1':'v3'})
# this works as desired
x.a[0]['key1'] # 'v1'
我想访问嵌套字典中的一个键,但希望能对包含这个字典的列表中的所有元素都进行这个操作。用Python的标准方法来做的话,可以用列表推导式,比如:
[v['key1'] for v in x.a]
不过,我的这个简单例子并没有完全展示我在实际场景中的复杂嵌套情况:类 X
中的属性列表 a
可能包含一些对象,这些对象的属性又是对象,而这些对象的属性又是字典,我想在遍历外层列表时选择这些字典中的键。
# I would like something like
useful_list = x.a[:]['key1'] # TypeError: list indices must be integers, not str
# or even better
cool_list = where(x.a[:]['key1'] == 'v2') # same TypeError
如果我对每个有趣的键都使用列表推导式,结果看起来就不太符合Python的风格了。有没有什么好的方法可以做到这一点,还是说我必须为所有可能的列表和字典键的组合写“获取器”方法呢?
更新: 我在研究重载列表。显然可以对 getitem 方法进行一些操作,这个方法用于列表的索引和字典的键。也许可以创建一个自定义类来遍历列表中的成员。这听起来有点复杂……
1 个回答
2
所以,你想创建一个层级结构,并且希望在不同类型上执行的操作有不同的含义,而且这个操作是递归定义的。这个时候,多态就能派上用场了。
你可以重写 __getitem__
方法,而不是我下面提到的 get_items
,但在你的情况下,定义一个非内置的操作可能更好,这样可以避免产生歧义。其实这完全取决于你。
class ItemsInterface(object):
def get_items(self, key):
raise NotImplementedError
class DictItems(ItemsInterface, dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
def get_items(self, key):
res = self[key]
# apply recursively
try:
res = res.get_items(key)
except AttributeError:
pass
return res
class ListItems(ItemsInterface, list):
def __init__(self, *args, **kwargs):
list.__init__(self, *args, **kwargs)
def get_items(self, key):
return [ x.get_items(key) for x in self ]
x = ListItems()
x.append(DictItems({'key1':'v1'}))
x.append(DictItems({'key1':'v2'}))
x.append(DictItems({'key1':'v3'}))
y = DictItems({'key1':'v999'})
x.append(ListItems([ y ]))
x.get_items('key1')
=> ['v1', 'v2', 'v3', ['v999']]
当然,这个解决方案可能并不是你所需要的(你没有说明如果键缺失应该怎么处理等等),但是你可以很容易地修改它以满足你的需求。
这个解决方案还支持将 ListItems
作为 DictItems
的值。get_items
操作是递归应用的。