如何构建一个可以在字典或对象中设置任意值的函数?或者换句话说,一个可以设置传递给它的任何值的函数。
例如,假设您有一个这样的字典:
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
来构建适用于类的属性的东西。我可以构建一些适用于词典中的项目的东西。但我不知道你将如何为子词典或子对象做这件事。
答案不一定是这种形式。相反,我只想编写一个在对象层次结构中设置任意属性/项的函数。
答案 0 :(得分: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
答案 1 :(得分:0)
您可以测试类型(isinstance(dict)或isinstance(object))来执行setattr()或update(),但大多数时候,如果需要它,则意味着您的设计存在缺陷。你最好为此目的做两个功能,或者找另一种方式。
HTH