2012-03-29 4 views
1

Wie erstellt man eine Funktion, die einen willkürlichen Wert innerhalb eines Wörterbuchs oder Objekts setzen kann? Oder mit anderen Worten, eine Funktion, die einen beliebigen Wert setzen kann.Absteigend durch ein Objekt und Unterobjekte zum Festlegen eines beliebigen Elements oder Attributs in der Hierarchie

Beispiel: Angenommen, Sie ein Wörterbuch wie dieses:

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

Ich möchte eine setter Funktion erstellen, wie diese verwendet werden:

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) 

ich nicht ein offensichtliches gesehen haben Weg, dies zu tun. Die naive Art und Weise unten eindeutig nicht funktioniert:

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

ich die setattr verwenden könnte, etwas zu bauen, die für Attribute auf Klassen funktionieren. Ich könnte etwas anderes bauen, das für Wörter in Wörterbüchern funktioniert. Aber ich habe keine Ahnung, wie Sie das für Unterwörterbücher oder Unterobjekte tun würden.

Die Antwort muss nicht in dieser exakten Form sein. Ich möchte nur eine Funktion schreiben, die ein beliebiges Attribut/Objekt innerhalb einer Objekthierarchie setzt.

Antwort

1

ich beschlossen, dass dies ein interessantes Problem war. In erster Linie sammelt diese Klasse Element- oder Attributnamen, wenn Sie versuchen, Elemente oder Attribute daraus abzurufen. Er kann dann diese gesammelten Operationen verwenden, um den entsprechenden Wert für eine Objekthierarchie abzurufen oder festzulegen. Es hat auch ein paar bequeme Methoden, um eine set_to_x Funktion wie die in Ihrem Beispiel zu machen.

Die Klasse:

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) 

Lassen Sie uns einige Daten vorbereiten und einige Setter-Funktionen machen:

# 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) 

die sie Nun lassen Sie testen:

# 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

Netter Versuch. Es sieht für alle Werte gut aus, außer für das Unterwörterbuch, in dem Sie die Aufrufkonvention ändern. Vielleicht ist das einfach nicht möglich. Zeiger, wie sie sie in C haben, würden wirklich helfen. – speedplane

+0

@speedplane Ich habe meine Antwort durch eine ersetzt, die genau wie das Beispiel in Ihrer Frage funktioniert. Es ist völlig typunabhängig - es funktioniert mit jedem Objekt, das Sie erhalten oder Attribute oder Objekte festlegen können. – agf

+0

Das ist beeindruckend. Ich sehe, dass man beliebig viele Ops aneinanderreihen kann, so dass schickere Sachen wie folgt funktionieren würden: 'set_my_dict_to_x (LocationProxy() ['item'] [0] .prop ['item2'] [3])'. Sehr cool! – speedplane

0

Sie können für den Typ (isinstance (dict) oder isinstance (object)) entweder setattr() oder update() testen, aber die meiste Zeit, wenn Sie es brauchen, bedeutet, dass Ihr Design fehlerhaft ist. Sie sollten besser zwei Funktionen für diesen Zweck ausführen oder einen anderen Weg finden.

HTH

+0

Ich brauche es nicht, es würde den Code nur schöner machen. – speedplane

Verwandte Themen