2012-03-29 56 views
1

如何构建可以在字典或对象内设置任意值的函数?换句话说,一个可以设置任何值的函数。通过对象和子对象下降来设置层次结构中的任意项目或属性

例如,假设你有一个这样的词典:

my_dict = { 
    'foo' : 'bar', 
    'subdict':{ 
    'sub1' : 1, 
    'sub2' : 2 
    } 
} 

我想创建一个setter功能,使用这样的:

x = 'x' 
# I want to be able to set normal dictionary items 
setter(lambda val: val['foo'], my_dict, x) 

# And also set subdictionary items 
setter(lambda val: val['subdict']['sub1'], my_dict, x) 

# Setting items that don't exist would be great too 
setter(lambda val: val['new_item'], my_dict, x) 

# If my_dict was a class, I'd like to use the same function, like this 
setter(lambda val: val.myproperty, my_dict, x) 

# Would also be cool if it worked with lists 
my_list = [1, 2] 
setter(lambda val: val[1], my_list, x) 

我没有看到一个明显这样做的方法。下面清楚地用简单的方式不起作用:

def setter(get_attr_func, param, x): 
    # This doesn't work, but I'd like something like it 
    get_attr_func(param) = x 

我可以使用setattr来建立一些有关类属性的作品。我可以构建其他可用于字典中的项目的其他内容。但是我不知道你会怎么做它的子词典或子对象。

答案不一定要用这种确切形式。相反,我只想编写一个函数,在对象层次结构中设置任意的属性/项目。

回答

1

我决定这是一个有趣的问题。首先,当您尝试从中获取项目或属性时,此类将收集项目或属性名称。然后它可以使用这些收集的操作来获取或设置对象层次结构上的对应值。它也有一些便利的方法来制作set_to_x函数,就像你的例子中的函数一样。

类:

class LocationProxy(object): 
    def __init__(self, ops = None): 
     self.ops = ops or [] 
    def __getitem__(self, item): 
     self.ops.append((True, item)) 
     return self 
    def __getattr__(self, attr): 
     self.ops.append((False, attr)) 
     return self 
    @staticmethod 
    def iterativeget(obj, ops): 
     for isitem, key in ops: 
      obj = obj[key] if isitem else getattr(obj, key) 
     return obj 
    def get(self, obj): 
     return self.iterativeget(obj, self.ops) 
    def set(self, obj, value): 
     isitem, key = self.ops[-1] 
     obj = self.iterativeget(obj, self.ops[:-1]) 
     if isitem: 
      obj[key] = value 
     else: 
      setattr(obj, key, value) 
    def set_from_object(self, obj): 
     return lambda value: self.set(obj, value) 
    def set_to_value(self, value): 
     return lambda obj: self.set(obj, value) 
    @staticmethod 
    def object_value_setter(obj, value): 
     return lambda location: location.set(obj, value) 
    @staticmethod 
    def object_setter(obj): 
     return lambda location, value: location.set(obj, value) 
    @staticmethod 
    def value_setter(value): 
     return lambda location, obj: location.set(obj, value) 

让我们准备一些数据,并提出一些setter函数:

# since you can't set attributes on a normal dict, use a subclass 
class MyDict(dict): pass 

my_dict = MyDict({ 
    'foo' : 'bar', 
    'subdict':{ 
    'sub1' : 1, 
    'sub2' : 2 
    } 
}) 

my_list = [1, 2] 

x = 'x' 

# we're going to set multiple things in my_dict to x, let's not repeat ourselves 
set_my_dict_to_x = LocationProxy.object_value_setter(my_dict, x) 

# we'll use set_to_x as you used it later on my_list 
set_to_x = LocationProxy.value_setter(x) 

现在让我们来测试他们:

# you can assign a name to a setter to use multiple times 
foosetter = LocationProxy()['foo'] 

# set normal dictionary items 
set_my_dict_to_x(foosetter) 

# And also set subdictionary items 
set_my_dict_to_x(LocationProxy()['subdict']['sub1']) 

# Set items that don't exist 
set_my_dict_to_x(LocationProxy()['new_item']) 

print 'my_dict', my_dict 

# my_dict is a class, use the same function 
set_my_dict_to_x(LocationProxy().myproperty) 

print 'myproperty', my_dict.myproperty 

# it works with lists 
set_to_x(LocationProxy()[1], my_list) 

print 'my_list', my_list 
+0

不错的尝试。除了更改调用约定的子字典之外,它对所有值都很有用。也许这是不可能的。像C中的指针会帮助你。 – speedplane 2012-03-29 14:06:07

+0

@speedplane我用一个和你的问题中的例子完全一样的工具取代了我的答案。它完全是类型不可知的 - 它可以与任何可以获取的对象或设置属性或项目。 – agf 2012-03-31 12:28:04

+0

这令人印象深刻。我发现你可以将任意一组操作串在一起,所以更有趣的东西可以工作,例如:'set_my_dict_to_x(LocationProxy()['item'] [0] .prop ['item2'] [3])''。很酷! – speedplane 2012-04-01 22:02:13

0

您可以测试类型(isinstance(dict)或isinstance(object))以执行setattr()或update(),但大多数情况下,如果您需要它,则意味着您的设计存在缺陷。你应该为此做好两个功能,或者找到另一种方式。

HTH

+0

我并不需要它,它只会让代码更漂亮。 – speedplane 2012-03-29 13:54:28

相关问题