Molte delle risposte fornite richiedono così tante righe per proprietà, cioè / e / o - quella che considererei un'implementazione brutta o noiosa a causa della ripetitività richiesta per più proprietà, ecc. Preferisco continuare a far bollire le cose / semplificarle finché non non può più essere semplificato o finché non serve a molto farlo.
In breve: nei lavori completati, se ripeto 2 righe di codice, in genere lo converto in una funzione di aiuto a riga singola, e così via ... Semplifico argomenti matematici o dispari come (start_x, start_y, end_x, end_y) a (x, y, w, h) cioè x, y, x + w, y + h (a volte richiedono min / max o se w / h sono negativi e l'implementazione non piace, sottraggerò da x / ye abs w / h. ecc ..).
Ignorare i getter / setter interni è un buon modo per andare, ma il problema è che devi farlo per ogni classe, o genitore della classe a quella base ... Questo non funziona per me come preferirei essere libero di scegliere i figli / genitori per l'eredità, i nodi figlio, ecc.
Ho creato una soluzione che risponde alla domanda senza utilizzare un tipo di dati Dict per fornire i dati poiché trovo che sia noioso inserire i dati, ecc ...
La mia soluzione richiede di aggiungere 2 righe extra sopra la tua classe per creare una classe base per la classe a cui desideri aggiungere le proprietà, quindi 1 riga per e hai la possibilità di aggiungere callback per controllare i dati, informarti quando i dati cambiano , limitare i dati che possono essere impostati in base al valore e / o al tipo di dati e molto altro ancora.
Hai anche la possibilità di utilizzare _object.x, _object.x = value, _object.GetX (), _object.SetX (value) e vengono gestiti in modo equivalente.
Inoltre, i valori sono gli unici dati non statici assegnati all'istanza della classe, ma la proprietà effettiva è assegnata alla classe, il che significa che le cose che non vuoi ripetere, non devono essere ripetute ... Tu può assegnare un valore predefinito in modo che il getter non ne abbia bisogno ogni volta, sebbene ci sia un'opzione per sovrascrivere il valore predefinito predefinito, e c'è un'altra opzione in modo che il getter restituisca il valore memorizzato non elaborato sovrascrivendo i rendimenti predefiniti (nota: questo metodo significa che il valore grezzo viene assegnato solo quando viene assegnato un valore, altrimenti è None - quando il valore è Reset, allora assegna Nessuno, ecc.)
Ci sono anche molte funzioni di supporto: la prima proprietà che viene aggiunta aggiunge 2 o più helper alla classe per fare riferimento ai valori dell'istanza ... Sono ResetAccessors (_key, ..) vararg ripetuti (tutti possono essere ripetuti usando i primi argomenti denominati ) e SetAccessors (_key, _value) con la possibilità di aggiungerne altri alla classe principale per aiutare nell'efficienza - quelli pianificati sono: un modo per raggruppare le funzioni di accesso insieme, quindi se si tende a reimpostarne alcune alla volta, ogni volta , puoi assegnarli a un gruppo e reimpostarlo invece di ripetere ogni volta i tasti nominati e altro ancora.
L'istanza / valore non elaborato archiviato viene archiviato in classe., la classe. fa riferimento alla classe Accessor che contiene variabili / valori / funzioni statiche per la proprietà. _classe. è la proprietà stessa che viene chiamata quando si accede tramite la classe di istanza durante l'impostazione / acquisizione, ecc.
L'Accessor _class .__ punta alla classe, ma poiché è interna deve essere assegnata nella classe, motivo per cui ho scelto di utilizzare __Name = AccessorFunc (...) per assegnarla, una singola riga per proprietà con molti optional argomenti da usare (usando vararg con chiave perché sono più facili ed efficienti da identificare e mantenere) ...
Creo anche molte funzioni, come accennato, alcune delle quali utilizzano le informazioni sulle funzioni accessorie, quindi non è necessario chiamarle (poiché al momento è un po 'scomodo - in questo momento è necessario utilizzare _class. .FunctionName (_class_instance , args) - Sono andato in giro usando lo stack / trace per afferrare il riferimento all'istanza per acquisire il valore aggiungendo le funzioni che eseguono questa maratona di bit, o aggiungendo le funzioni di accesso all'oggetto e usando self (chiamato questo per indicare che per l'istanza e per mantenere l'accesso a self, al riferimento alla classe AccessorFunc e ad altre informazioni dall'interno delle definizioni di funzione).
Non è ancora finito, ma è un fantastico appoggio. Nota: se non usi __Name = AccessorFunc (...) per creare le proprietà, non avrai accesso al tasto __ anche se lo definisco all'interno della funzione init. Se lo fai, non ci sono problemi.
Inoltre: si noti che il nome e la chiave sono diversi ... Il nome è "formale", utilizzato nella creazione del nome della funzione e la chiave è per l'archiviazione e l'accesso ai dati. cioè _class.x dove x minuscola è la chiave, il nome sarebbe X maiuscolo in modo che GetX () sia la funzione invece di Getx () che sembra un po 'strano. questo consente a self.x di funzionare e apparire appropriato, ma anche di consentire a GetX () di apparire appropriato.
Ho una classe di esempio impostata con chiave / nome identici e diversi da mostrare. molte funzioni di supporto create per produrre i dati (Nota: non tutto questo è completo) in modo da poter vedere cosa sta succedendo.
L'elenco corrente di funzioni utilizzando key: x, name: X restituisce come:
Questo non è affatto un elenco completo - ce ne sono alcuni che non lo hanno fatto al momento della pubblicazione ...
_instance.SetAccessors( _key, _value [ , _key, _value ] .. ) Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines. In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. ) Instance Class Helper Function: Allows resetting many key stored values to None on a single line. In short: Calls this.Reset<Name>() for each name provided.
Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.
this.GetX( _default_override = None, _ignore_defaults = False ) GET: Returns IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE 100
this.GetXRaw( ) RAW: Returns STORED_VALUE 100
this.IsXSet( ) ISSET: Returns ( STORED_VALUE != None ) True
this.GetXToString( ) GETSTR: Returns str( GET ) 100
this.GetXLen( _default_override = None, _ignore_defaults = False ) LEN: Returns len( GET ) 3
this.GetXLenToString( _default_override = None, _ignore_defaults = False ) LENSTR: Returns str( len( GET ) ) 3
this.GetXDefaultValue( ) DEFAULT: Returns DEFAULT_VALUE 1111
this.GetXAccessor( ) ACCESSOR: Returns ACCESSOR_REF ( self.__<key> ) [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848 Default: 1111 Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"} Allowed Values: None
this.GetXAllowedTypes( ) ALLOWED_TYPES: Returns Allowed Data-Types {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( ) ALLOWED_VALUES: Returns Allowed Values None
this.GetXHelpers( ) HELPERS: Returns Helper Functions String List - ie what you're reading now... THESE ROWS OF TEXT
this.GetXKeyOutput( ) Returns information about this Name / Key ROWS OF TEXT
this.GetXGetterOutput( ) Returns information about this Name / Key ROWS OF TEXT
this.SetX( _value ) SET: STORED_VALUE Setter - ie Redirect to __<Key>.Set N / A
this.ResetX( ) RESET: Resets STORED_VALUE to None N / A
this.HasXGetterPrefix( ) Returns Whether or Not this key has a Getter Prefix... True
this.GetXGetterPrefix( ) Returns Getter Prefix... Get
this.GetXName( ) Returns Accessor Name - Typically Formal / Title-Case X
this.GetXKey( ) Returns Accessor Property Key - Typically Lower-Case x
this.GetXAccessorKey( ) Returns Accessor Key - This is to access internal functions, and static data... __x
this.GetXDataKey( ) Returns Accessor Data-Storage Key - This is the location where the class instance value is stored.. _x
Alcuni dei dati in uscita sono:
Questo è per una nuova classe creata utilizzando la classe Demo senza alcun dato assegnato oltre al nome (quindi può essere emesso) che è _foo, il nome della variabile che ho usato ...
_foo --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016
Key Getter Value | Raw Key Raw / Stored Value | Get Default Value Default Value | Get Allowed Types Allowed Types | Get Allowed Values Allowed Values |
Name: _foo | _Name: _foo | __Name.DefaultValue( ): AccessorFuncDemoClass | __Name.GetAllowedTypes( ) <class 'str'> | __Name.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
x: 1111 | _x: None | __x.DefaultValue( ): 1111 | __x.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __x.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
y: 2222 | _y: None | __y.DefaultValue( ): 2222 | __y.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __y.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
z: 3333 | _z: None | __z.DefaultValue( ): 3333 | __z.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __z.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Blah: <class 'int'> | _Blah: None | __Blah.DefaultValue( ): <class 'int'> | __Blah.GetAllowedTypes( ) <class 'str'> | __Blah.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Width: 1 | _Width: None | __Width.DefaultValue( ): 1 | __Width.GetAllowedTypes( ) (<class 'int'>, <class 'bool'>) | __Width.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Height: 0 | _Height: None | __Height.DefaultValue( ): 0 | __Height.GetAllowedTypes( ) <class 'int'> | __Height.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
Depth: 2 | _Depth: None | __Depth.DefaultValue( ): 2 | __Depth.GetAllowedTypes( ) Saved Value Restricted to Authorized Values ONLY | __Depth.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
this.IsNameSet( ): True this.GetName( ): _foo this.GetNameRaw( ): _foo this.GetNameDefaultValue( ): AccessorFuncDemoClass this.GetNameLen( ): 4 this.HasNameGetterPrefix( ): <class 'str'> this.GetNameGetterPrefix( ): None
this.IsXSet( ): False this.GetX( ): 1111 this.GetXRaw( ): None this.GetXDefaultValue( ): 1111 this.GetXLen( ): 4 this.HasXGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetXGetterPrefix( ): None
this.IsYSet( ): False this.GetY( ): 2222 this.GetYRaw( ): None this.GetYDefaultValue( ): 2222 this.GetYLen( ): 4 this.HasYGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetYGetterPrefix( ): None
this.IsZSet( ): False this.GetZ( ): 3333 this.GetZRaw( ): None this.GetZDefaultValue( ): 3333 this.GetZLen( ): 4 this.HasZGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetZGetterPrefix( ): None
this.IsBlahSet( ): False this.GetBlah( ): <class 'int'> this.GetBlahRaw( ): None this.GetBlahDefaultValue( ): <class 'int'> this.GetBlahLen( ): 13 this.HasBlahGetterPrefix( ): <class 'str'> this.GetBlahGetterPrefix( ): None
this.IsWidthSet( ): False this.GetWidth( ): 1 this.GetWidthRaw( ): None this.GetWidthDefaultValue( ): 1 this.GetWidthLen( ): 1 this.HasWidthGetterPrefix( ): (<class 'int'>, <class 'bool'>) this.GetWidthGetterPrefix( ): None
this.IsDepthSet( ): False this.GetDepth( ): 2 this.GetDepthRaw( ): None this.GetDepthDefaultValue( ): 2 this.GetDepthLen( ): 1 this.HasDepthGetterPrefix( ): None this.GetDepthGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ): False this.GetHeight( ): 0 this.GetHeightRaw( ): None this.GetHeightDefaultValue( ): 0 this.GetHeightLen( ): 1 this.HasHeightGetterPrefix( ): <class 'int'> this.GetHeightGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
E questo è dopo aver assegnato tutte le proprietà _foo (eccetto il nome) i seguenti valori nello stesso ordine: 'string', 1.0, True, 9, 10, False
this.IsNameSet( ): True this.GetName( ): _foo this.GetNameRaw( ): _foo this.GetNameDefaultValue( ): AccessorFuncDemoClass this.GetNameLen( ): 4 this.HasNameGetterPrefix( ): <class 'str'> this.GetNameGetterPrefix( ): None
this.IsXSet( ): True this.GetX( ): 10 this.GetXRaw( ): 10 this.GetXDefaultValue( ): 1111 this.GetXLen( ): 2 this.HasXGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetXGetterPrefix( ): None
this.IsYSet( ): True this.GetY( ): 10 this.GetYRaw( ): 10 this.GetYDefaultValue( ): 2222 this.GetYLen( ): 2 this.HasYGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetYGetterPrefix( ): None
this.IsZSet( ): True this.GetZ( ): 10 this.GetZRaw( ): 10 this.GetZDefaultValue( ): 3333 this.GetZLen( ): 2 this.HasZGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetZGetterPrefix( ): None
this.IsBlahSet( ): True this.GetBlah( ): string Blah this.GetBlahRaw( ): string Blah this.GetBlahDefaultValue( ): <class 'int'> this.GetBlahLen( ): 11 this.HasBlahGetterPrefix( ): <class 'str'> this.GetBlahGetterPrefix( ): None
this.IsWidthSet( ): True this.GetWidth( ): False this.GetWidthRaw( ): False this.GetWidthDefaultValue( ): 1 this.GetWidthLen( ): 5 this.HasWidthGetterPrefix( ): (<class 'int'>, <class 'bool'>) this.GetWidthGetterPrefix( ): None
this.IsDepthSet( ): True this.GetDepth( ): 9 this.GetDepthRaw( ): 9 this.GetDepthDefaultValue( ): 2 this.GetDepthLen( ): 1 this.HasDepthGetterPrefix( ): None this.GetDepthGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ): True this.GetHeight( ): 9 this.GetHeightRaw( ): 9 this.GetHeightDefaultValue( ): 0 this.GetHeightLen( ): 1 this.HasHeightGetterPrefix( ): <class 'int'> this.GetHeightGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
_foo --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016
Key Getter Value | Raw Key Raw / Stored Value | Get Default Value Default Value | Get Allowed Types Allowed Types | Get Allowed Values Allowed Values |
Name: _foo | _Name: _foo | __Name.DefaultValue( ): AccessorFuncDemoClass | __Name.GetAllowedTypes( ) <class 'str'> | __Name.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
x: 10 | _x: 10 | __x.DefaultValue( ): 1111 | __x.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __x.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
y: 10 | _y: 10 | __y.DefaultValue( ): 2222 | __y.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __y.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
z: 10 | _z: 10 | __z.DefaultValue( ): 3333 | __z.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __z.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Blah: string Blah | _Blah: string Blah | __Blah.DefaultValue( ): <class 'int'> | __Blah.GetAllowedTypes( ) <class 'str'> | __Blah.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Width: False | _Width: False | __Width.DefaultValue( ): 1 | __Width.GetAllowedTypes( ) (<class 'int'>, <class 'bool'>) | __Width.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Height: 9 | _Height: 9 | __Height.DefaultValue( ): 0 | __Height.GetAllowedTypes( ) <class 'int'> | __Height.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
Depth: 9 | _Depth: 9 | __Depth.DefaultValue( ): 2 | __Depth.GetAllowedTypes( ) Saved Value Restricted to Authorized Values ONLY | __Depth.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
Si noti che a causa di tipi di dati limitati o limitazioni di valore, alcuni dati non sono stati assegnati: questo è di progettazione. Il setter proibisce l'assegnazione di tipi di dati o valori errati, anche di essere assegnati come valore predefinito (a meno che non si ignori il comportamento di protezione del valore predefinito)
Il codice non è stato pubblicato qui perché non avevo spazio dopo gli esempi e le spiegazioni ... Anche perché cambierà.
Nota: al momento di questo post, il file è disordinato - questo cambierà. Ma, se lo esegui in Sublime Text e lo compili, o lo esegui da Python, compilerà e sputerà un sacco di informazioni: la parte AccessorDB non viene eseguita (che verrà utilizzata per aggiornare Print Getters e GetKeyOutput helper funzioni insieme ad essere cambiato in una funzione di istanza, probabilmente messo in una singola funzione e rinominato - cercalo ..)
Avanti: Non tutto è richiesto per l'esecuzione - molte delle cose commentate in fondo servono per ulteriori informazioni usate per il debug - potrebbe non essere lì quando lo scarichi. Se lo è, dovresti essere in grado di decommentare e ricompilare per ottenere maggiori informazioni.
Sto cercando una soluzione per aver bisogno di MyClassBase: pass, MyClass (MyClassBase): ... - se conosci una soluzione - pubblicala.
L'unica cosa necessaria nella classe sono le righe __ - la str è per il debug come l' init - possono essere rimosse dalla classe Demo ma sarà necessario commentare o rimuovere alcune delle righe sottostanti (_foo / 2/3 ) ..
Le classi String, Dict e Util in alto fanno parte della mia libreria Python - non sono complete. Ho copiato alcune cose di cui avevo bisogno dalla libreria e ne ho create alcune nuove. Il codice completo si collegherà alla libreria completa e lo includerà insieme a fornire chiamate aggiornate e rimuovere il codice (in realtà, l'unico codice rimasto sarà la classe Demo e le istruzioni di stampa - il sistema AccessorFunc verrà spostato nella libreria). ..
Parte del file:
class AccessorFuncDemoClassBase( ):
pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
__Name = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name', default = 'AccessorFuncDemoClass', allowed_types = ( TYPE_STRING ), allowed_values = VALUE_ANY, documentation = 'Name Docs', getter_prefix = 'Get', key = 'Name', allow_erroneous_default = False, options = { } )
__x = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X', default = 1111, allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ), allowed_values = VALUE_ANY, documentation = 'X Docs', getter_prefix = 'Get', key = 'x', allow_erroneous_default = False, options = { } )
__Height = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height', default = 0, allowed_types = TYPE_INTEGER, allowed_values = VALUE_SINGLE_DIGITS, documentation = 'Height Docs', getter_prefix = 'Get', key = 'Height', allow_erroneous_default = False, options = { } )
Questa bellezza rende incredibilmente facile creare nuove classi con proprietà aggiunte dinamicamente con AccessorFuncs / callback / data-type / value enforcement, ecc.
Per ora, il collegamento è a (Questo collegamento dovrebbe riflettere le modifiche al documento.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
Inoltre: se non usi Sublime Text, lo consiglio su Notepad ++, Atom, Visual Code e altri a causa delle corrette implementazioni di threading che lo rendono molto, molto più veloce da usare ... Sto anche lavorando su un codice simile all'IDE sistema di mappatura per esso - dai un'occhiata a: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Aggiungi prima Repo in Package Manager, quindi Installa Plugin - quando la versione 1.0.0 è pronta, aggiungerò all'elenco principale dei plugin ...)
Spero che questa soluzione aiuti ... e, come sempre:
Solo perché funziona, non lo fa bene - Josh 'Acecool' Moser
:
e__init__
riferimentiself.fn_readyonly
.