Ho un cattivo odore nel mio codice. Forse ho solo bisogno di lasciarlo andare in aria per un po ', ma in questo momento mi sta tormentando.Python: proprietà idiomatiche per i dati strutturati?
Ho bisogno di creare tre diversi file di input per eseguire tre applicazioni di RTR (Radiative Transfer Modeling), in modo da poter confrontare i loro output. Questo processo verrà ripetuto per migliaia di insiemi di input, quindi lo sto automatizzando con uno script python.
Vorrei memorizzare i parametri di input come un oggetto python generico che posso passare a tre altre funzioni, ognuna delle quali tradurrà quell'oggetto generale nei parametri specifici necessari per eseguire il software RTM di cui sono responsabili. Penso che abbia senso, ma sentiti libero di criticare il mio approccio.
Ci sono molti parametri di input possibili per ogni parte del software RTM. Molti di loro si sovrappongono. La maggior parte di essi è mantenuta a valori predefiniti, ma dovrebbe essere facilmente modificata.
Ho iniziato con un semplice dict
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar_azimuth_angle: 73, #degrees
solar_zenith_angle: 17, #degrees
...
}
Ci sono un sacco di parametri, e possono essere in modo pulito classificati in gruppi, così ho pensato di usare dict
s all'interno della dict
:
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar: {
azimuth_angle: 73, #degrees
zenith_angle: 17, #degrees
...
},
...
}
Mi piace. Ma ci sono molte proprietà ridondanti. L'azimut solare e gli angoli zenitali, ad esempio, possono essere trovati se l'altro è noto, quindi perché hard-code entrambi? Così ho iniziato a cercare all'interno di python property
. Che mi permette di fare cose eccezionali con i dati, se posso conservare come gli attributi degli oggetti:
class Configuration(object):
day_of_year = 138,
time_of_day = 36000, #seconds
solar_azimuth_angle = 73, #degrees
@property
def solar_zenith_angle(self):
return 90 - self.solar_azimuth_angle
...
config = Configuration()
Ma ora ho perso la struttura che ho avuto dalla seconda dict
esempio.
Si noti che alcune delle proprietà sono meno banali del mio esempio solar_zenith_angle
e potrebbero richiedere l'accesso ad altri attributi al di fuori del gruppo di attributi di cui fa parte. Ad esempio, posso calcolare solar_azimuth_angle
se conosco il giorno dell'anno, l'ora del giorno, la latitudine e la longitudine.
Quello che sto cercando:
Un modo semplice per memorizzare i dati di configurazione di cui tutti i valori si può accedere in modo uniforme, sono ben strutturati, e possono esistere sia come attributi (valori reali) o proprietà (calcolate da altri attributi).
Una possibilità che è una specie di noioso:
Conservare tutto nel dict di dicts ho delineato in precedenza, e con altre funzioni corrono sopra l'oggetto e calcolare i valori calcolabile? Questo non sembra divertente. O pulito. A me sembra disordinato e frustrante.
Un brutto quello che funziona:
Dopo molto tempo cercando strategie diverse e per lo più ottenere nessun dove, mi si avvicinò con una possibile soluzione che sembra funzionare:
Le mie lezioni: (odori un po 'func-y, ehm, funky def-initely..)
class SubConfig(object):
"""
Store logical groupings of object attributes and properties.
The parent object must be passed to the constructor so that we can still
access the parent object's other attributes and properties. Useful if we
want to use them to compute a property in here.
"""
def __init__(self, parent, *args, **kwargs):
super(SubConfig, self).__init__(*args, **kwargs)
self.parent = parent
class Configuration(object):
"""
Some object which holds many attributes and properties.
Related configurations settings are grouped in SubConfig objects.
"""
def __init__(self, *args, **kwargs):
super(Configuration, self).__init__(*args, **kwargs)
self.root_config = 2
class _AConfigGroup(SubConfig):
sub_config = 3
@property
def sub_property(self):
return self.sub_config * self.parent.root_config
self.group = _AConfigGroup(self) # Stinky?!
Come li posso usare: (opere come vorrei)
config = Configuration()
# Inspect the state of the attributes and properties.
print("\nInitial configuration state:")
print("config.rootconfig: %s" % config.root_config)
print("config.group.sub_config: %s" % config.group.sub_config)
print("config.group.sub_property: %s (calculated)" % config.group.sub_property)
# Inspect whether the properties compute the correct value after we alter
# some attributes.
config.root_config = 4
config.group.sub_config = 5
print("\nState after modifications:")
print("config.rootconfig: %s" % config.root_config)
print("config.group.sub_config: %s" % config.group.sub_config)
print("config.group.sub_property: %s (calculated)" % config.group.sub_property)
Il comportamento: (uscita di esecuzione di tutto il codice di cui sopra, come ci si aspettava)
Initial configuration state:
config.rootconfig: 2
config.group.sub_config: 3
config.group.sub_property: 6 (calculated)
State after modifications:
config.rootconfig: 4
config.group.sub_config: 5
config.group.sub_property: 20 (calculated)
Perché non piace:
La memorizzazione dei dati di configurazione nelle definizioni di classe all'interno dell'oggetto __init__()
dell'oggetto principale non è elegante. Soprattutto dover istanziarli immediatamente dopo una definizione del genere. Ugh. Posso occuparmene per la classe genitore, certo, ma farlo in un costruttore ...
Memorizzare le stesse classi al di fuori dell'oggetto principale Configuration
non si sente elegante neanche, poiché le proprietà nelle classi interne possono dipendere da gli attributi di Configuration
(oi loro fratelli al suo interno).
ho potuto fare con la definizione delle funzioni al di fuori di tutto, in modo da avere dentro cose come
@property
def solar_zenith_angle(self):
return calculate_zenith(self.solar_azimuth_angle)
ma io non riesco a capire come fare qualcosa di simile
@property
def solar.zenith_angle(self):
return calculate_zenith(self.solar.azimuth_angle)
(quando provo per essere intelligente su di esso mi imbatto sempre in <property object at 0xXXXXX>
)
Quindi qual è la strada giusta da fare per questo? Mi manca qualcosa di base o un approccio molto sbagliato? Qualcuno conosce una soluzione intelligente?
Help! Il mio codice Python non è bello! Devo fare qualcosa di sbagliato!
Avevo la sensazione che probabilmente avrei dovuto approfondire alcuni di quei metodi magici. Ho intenzione di giocare un po 'con i suggerimenti del codice, ma a prima vista sono preoccupato dalla 'proprietà (lambda ...'. Alcuni dei miei calcoli di proprietà non rientrano in un 'lambda', ed è qui che Comincio a correre in circolo cercando di fare 'def self.group.a():' Anche se ho qualche nuova idea con cui giocare ora, grazie! – Phil
@Phil prova l'approccio metaclass, quindi. Non richiede di usare lambda. – LaC