Спуск через объект и подобъекты, чтобы установить произвольный элемент или атрибут в иерархии - PullRequest
1 голос
/ 29 марта 2012

Как построить функцию, которая может установить произвольное значение в словаре или объекте?Или, другими словами, функция, которая может установить любое переданное ей значение.

Например, предположим, что у вас есть словарь, подобный этому:

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, чтобы создать что-то, что работает для атрибутов на классах.Я мог бы создать что-то еще, что работает для элементов в словарях.Но я понятия не имею, как бы вы сделали это для под-словарей или подобъектов.

Ответ не обязательно должен быть в такой точной форме.Скорее, я просто хочу написать функцию, которая устанавливает произвольный атрибут / элемент в иерархии объектов.

Ответы [ 2 ]

1 голос
/ 29 марта 2012

Я решил, что это интересная проблема. Прежде всего, этот класс собирает имена элементов или атрибутов, когда вы пытаетесь получить элементы или атрибуты из него. Затем он может использовать эти собранные операции, чтобы получить или установить соответствующее значение в иерархии объектов. Он также имеет несколько удобных методов для создания функции 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)

Давайте подготовим некоторые данные и сделаем несколько функций установки:

# 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 голосов
/ 29 марта 2012

вы можете проверить тип (isinstance (dict) или isinstance (объект)) для выполнения setattr () или update (), но большую часть времени, если вам это нужно, означает, что ваш дизайн имеет недостатки Для этого вам лучше выполнить две функции или найти другой способ.

НТН

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...