ich habe eine Klasse, deren Instanzen Knoten in einer Objekt-Hierarchie ("Baum") sind und verschiedene Read-Only-Eigenschaften haben. Instanzen können Eigenschaften fehlen. Werden sie trotzdem nachgefragt, soll die Anfrage unter der Haube an verlinkte höhere Knoten gegeben werden. Das Ergebnis soll zwischengespeichert und zurückgegeben werden. Für den Aufrufer soll es letztendlich keine Rolle spielen, ob die Eigenschaft von der Instanz selbst oder andere Instanzen, von denen sie "abgeleitet" ist. Dieser Ableitungsmechanismus hat nichts mit Python-Klassen zu tun. Es ist eine dynamische, vom User definierte hierarchische Struktur von Objekten.
Konkret ein Problem habe ich mit dynamisch generierten Properties (s. u. Zeile 64ff.), die nur einen Getter haben. Der Getter ist eine lambda-Funktion und hat nur die Aufgabe, eine Methode namens _lookup_attr() aufzurufen. Rufe ich die Property auf, kommt es zum Fehler:
Code: Alles auswählen
'ProtoPartial' object is not iterable
Ermittle ich die Eigenschaft direkt mittels Aufruf von _lookup_attr(), funktioniert die Sache. Diese Kurzproperties sind jetzt nicht zu wichtig, kann zur Not darauf verzichten (d.h. einfach _lookup_attr nach get() oder so umbenennen) , aber die Ursache der Fehlermeldung würde ich doch gerne verstehen, warum mein "syntactic sugar" nicht funktioniert.
Code: Alles auswählen
# -*- coding: utf-8 -*-
from __future__ import division
from Sompyler.synthesizer.sympartial import Sympartial
from Sompyler.synthesizer.modulation import Modulation
from Sompyler.synthesizer.envelope import Shape
import re
ABBREV_ARGS = {
'A': 'attack',
'S': "sustain",
'B': "boost",
'R': "release",
'AM': "amplitude_modulation",
'FM': "frequency_modulation",
'WS': "wave_shape",
'O': "oscillator" # value merely informational (not used)
}
ENV_ARGS = ('A', 'S', 'B', 'R')
OSC_ARGS = ('AM', 'FM', 'WS')
SHAPES = ENV_ARGS + ('WS',)
MODS = ('AM', 'FM')
class ProtoPartial(object):
"""
Manage all properties
"""
__slots__ = ('_base', '_upper', '_cache') + tuple(
'_' + i for i in ABBREV_ARGS
)
def __init__( self, base, upper, pp_registry, **args ):
self._base = base
self._upper = upper
self._cache = {}
for prop in ABBREV_ARGS.keys():
value = args.get(prop)
if value is None:
setattr(self, '_' + prop, None)
continue
elif prop == "O":
if isinstance(value, str):
pp = pp_registry['LOOK_UP'](value)
value = pp._lookup_attr('O')
elif value.startswith('@'):
value = value[1:]
pp = pp_registry.get( value )
if pp is None:
pp = pp_registry['LOOK_UP'](value)
value = getattr( pp, prop)
if prop in SHAPES:
value = Shape.from_string(value)
elif prop in MODS:
value = Modulation.from_string(value, pp_registry)
setattr(self, '_' + prop, value)
if self._O is None:
raise Exception("ProtoPartial instance missing oscillator")
for i in ABBREV_ARGS:
exec ('{0} = property(lambda (self, obj):'
+' obj._lookup_attr("{0}"), None, None)'
).format(i)
def _lookup_attr (self, attr):
""" Look up attribute first in own attributes, then in the ancestry
of named variation. If it is not found there, try the base and its ancestry.
"""
value = getattr(self, '_' + attr, None)
if value is not None:
return value
elif attr in self._cache:
return self.cache[attr]
for m in (self._upper, self._base):
if m is None:
continue
privm = '_' + attr
value = (
getattr(m, privm) if hasattr(m, privm)
else m._lookup_attr(attr)
)
if value is not None:
self._cache[attr] = value
return value
def sympartial ( self ):
env_args = {}; osc_args = {}
for each in ENV_ARGS:
val = getattr(self, each)()
if val: env_args[ ABBREV_ARGS[i] ] = val
for i in OSC_ARGS:
val = getattr(self, each)()
if val: osc_args[ ABBREV_ARGS[i] ] = val
return Sympartial(
Envelope(**envelope_args),
self.O().derive(**osc_args)
)
Danke schon mal,
gotridofmyphone