Voglio prefigurare questa domanda scusandomi per la sua lunghezza, specialmente per gli esempi di codice; tuttavia, credo di aver incluso il codice minimo necessario per illustrare le differenze negli approcci.
Ho una classe con un numero di attributi di istanza e ognuno di questi attributi ha alcuni attributi rispettivi.
Ad esempio, consideriamo un'applicazione GUI in cui sono presenti numerosi attributi e ogni attributo ha un rispettivo layout e campo. Spesso è necessario eseguire un'azione su ogni layout o campo come gruppo.
Ho trovato 3 modi per organizzare la classe che potrebbe soddisfare le condizioni di cui sopra:
Approccio ingenuo
class EditUserProfile(AbstractApplication):
def __init__(self, username, fav_food):
self.username = username
self.username_layout = Layout()
self.username_field = Field()
self.fav_food = fav_food
self.fav_food_layout = Layout()
self.fav_food_field = Field()
self.captureUsername()
self.updateLayouts()
def captureUsername(self):
if __name__ == '__main__':
if self.username_field.is_changed():
self.username = self.username_field.text
# Now I have to type out the mouthful 'self.username_field'
# every time I want to access the field
# even though 'self.username_field' is the only field in
# the scope of this method.
# I could do the same thing as the dict solution below:
# field = self.username_field
# But that also kind of goes against Python convention
def updateLayouts(self):
print("Doing something to Username Layout")
self.username_layout.doSomething()
print("Doing something to Favorite Food Layout")
self.fav_food_layout.doSomething()
# I now have to manually go through each layout and do the same thing, or I could do this:
for attr, value in self.__dict__.viewitems()
if attr.endswith("_layout") and isinstance(value, Layout):
print("Doing something to %s Layout" % attr) # Not equivalent to below answers
# Would need self.[attr]_display_name attributes
self.value.doSomething()
Vantaggi e problemi con l'approccio ingenuo
Questo è l'approccio più semplice da implementare e in casi semplici potrebbe andare bene. In questo esempio, che ha solo username
e fav_food
, non è un grosso problema. Tuttavia, questo approccio non scala bene.
Questo approccio porta ad alcuni attributi seri e attributi con nomi potenzialmente molto lunghi.
È anche una seria seccatura per scorrere tutti i layout o campi. Il metodo di iterazione attraverso __dict__
usato qui dipende dalla denominazione degli attributi - sia che tutti i layout desiderati terminino con _layout
, sia che nessun attributo indesiderato termini con _layout
.
Approccio Dict
Questo è l'approccio che sto attualmente utilizzando, ma ho iniziato a sospettare che potrei essere pazzo e questa potrebbe essere una pessima soluzione.
class EditUserProfile(AbstractApplication):
def __init__(self, username, fav_food):
self.username = username
self.fav_food = fav_food
self.layouts = {}
self.fields = {}
self.layouts["Username"] = Layout()
self.layouts["Favorite Food"] = Layout()
self.fields["Username"] = Field()
self.fields["Favorite Food"] = Field()
self.captureUsername()
self.updateLayouts()
def captureUsername(self):
field = self.fields["Username"]
if field.is_changed():
self.username = field.text
# Do a bunch of other stuff with field
def updateLayouts(self):
for key, value in self.layouts.viewitems():
print("Doing something to %s Layout" % key)
value.doSomething()
Vantaggi e problemi con l'approccio Dict
Sono giunto a questo approccio per necessità di raggruppare facilmente gli attributi correlati, come fields
e layouts
in questo esempio.
Inoltre, questo approccio porta a nomi brevi carini quando si accede a un attributo in ambito limitato, come in captureUsername
.
Questo approccio consente l'iterazione abbastanza semplice che è sicura per iterare su tutti e solo gli attributi desiderati.
In termini di svantaggi, questo approccio è, a mia conoscenza, abbastanza anticonvenzionale. Potrebbe confondere le persone che leggono il codice in futuro.
Il metodo di accesso a un attributo nel dict, fornendo una stringa, è un po 'non ideale. Gli IDE non ti cattureranno se commetti un errore digitando la chiave, né forniranno il completamento automatico. Devi essere molto diligente nel nominare le chiavi in modo coerente, o questo approccio potrebbe diventare un disastro.
Approccio orientato agli oggetti
In realtà sono arrivato a questo approccio mentre stavo pensando a questa domanda e digitandola in StackExchange. Non sono sicuro del motivo per cui non ci ho pensato prima. Questo è probabilmente l'approccio più convenzionale; tuttavia, ritengo che abbia alcuni svantaggi rispetto all'approccio Dict.
class EditUserProfile(AbstractApplication):
def __init__(self, username, fav_food):
self.username = UserProfileItem(username, "Username", Layout(), Field())
self.fav_food = UserProfileItem(fav_food, "Favorite Food", Layout(), Field())
self.captureUsername()
self.updateLayouts()
def captureUsername(self):
# This still feels a little verbose, but is broken up more nicely
if self.username.field.is_changed():
self.username.item = self.username.field.text
# Do a bunch of other stuff with self.username.field
def updateLayouts(self):
# You could go through the layout attributes manually,
# or you could use the class' __dict__ attribute, as below.
#
# This still seems less than ideal, as you have to iterate
# through all of the class' attributes, which could be many,
# and then check the type.
for attr, value in self.__dict__.viewitems():
if isinstance(value, UserProfileItem):
print("Doing something to %s Layout" % value.name)
value.layout.doSomething()
class UserProfileItem(object):
def __init__(self, item, name, layout, field):
self.item = item
self.name = name
self.layout = layout
self.field = field
Vantaggi e svantaggi del metodo orientato agli oggetti
In un paradigma orientato agli oggetti, questo approccio ha molto senso. Un oggetto ti consente di raggruppare i valori correlati con facilità.
L'approccio Dict, tuttavia, offre un lavoro un po 'migliore all'accesso a tutti gli attributi Field
o Layout
come gruppo, e iterando attraverso di essi. Ti dà esattamente ciò di cui hai bisogno, piuttosto che iterare attraverso tutti gli attributi dell'istanza e richiede meno convalida.
Quindi, in conclusione, qual è l'approccio preferito a questo problema? Il mio approccio Dict è intrinsecamente sbagliato? Sono corretto che l'approccio orientato agli oggetti sia preferibile? C'è un altro approccio al problema a cui non ho pensato?