Python - il modo migliore per avere una variabile interdipendente in una classe

0

Domande di Newbie Python: voglio modellare qualcosa che può avere diverse variabili di istanza interconnesse e se ne imposto una, voglio ricalcolare le altre. Qualsiasi può essere impostato.

es. un cerchio - ogni istanza ha un'area, una circonferenza e un raggio (ignoriamo il diametro). Se imposto l'area, voglio la circonferenza e amp; raggio ricalcolato e viceversa. Dovrei:

class circle:

   def area(self):
       return cmath.pi*self.radius*self.radius

   def circumference(self):
      return 2 * cmath.pi * self.radius

   def radius(self):
      return self.circumference/(2*cmath.pi)

circle1 = circle()
circle1.radius = 5
print "Circle1's area is {0} and its circumference is {1}".format(circle1.area, circle1.circumference) 

Ora non c'è nulla nel codice di esempio per calcolare l'area una volta che il raggio è stato impostato, quindi ho bisogno di @property con setter e getter per il raggio, l'area e la circonferenza per calcolare gli altri attributi quando questo è impostato o sto abbaiando completamente dall'albero sbagliato?

    
posta Steve Ives 11.05.2015 - 16:45
fonte

2 risposte

3

Invece di mantenere 3 campi membri e sincronizzarli, è sufficiente memorizzare un valore reale in memoria - il raggio è una buona scelta in questo esempio - e sempre usarlo:

import cmath

class circle(object):
    def __init__(self, radius=0.0):
        self._radius = radius

    @property
    def radius(self):
       return self._radius

    @radius.setter
    def radius(self, radius):
        self._radius = float(radius)

    @property
    def area(self):
       return cmath.pi*self._radius*self._radius

    @area.setter
    def area(self, area):
        self._radius = (area / cmath.pi) ** 0.5

    @property
    def circumference(self):
        return 2 * cmath.pi * self._radius

    @circumference.setter
    def circumference(self, circumference):
        self._radius = circumference / cmath.pi / 2

circle1 = circle()
circle1.radius = 5
print "Circle1's radius is {0}, it's area is {1} and it's circumference is {2}".format(circle1.radius, circle1.area, circle1.circumference)

circle1.area = 5
print "Circle1's radius is {0}, it's area is {1} and it's circumference is {2}".format(circle1.radius, circle1.area, circle1.circumference)

circle1.circumference = 5
print "Circle1's radius is {0}, it's area is {1} and it's circumference is {2}".format(circle1.radius, circle1.area, circle1.circumference)

Questa soluzione sembra troppo ovvia però - c'è un motivo specifico per cui vuoi avere campi membri separati? Di solito puoi trovare un numero inferiore di valori per calcolare il resto da ...

    
risposta data 11.05.2015 - 18:32
fonte
0

Oggi ho scritto una piccola libreria pure-python esattamente per gestire le dipendenze aciclic negli attributi della classe python , noto anche come programmazione del flusso di dati . A volte non è possibile aggiornare semplicemente tutti gli attributi quando ne viene modificato uno. Idealmente, la modifica di un attributo non causa nessun aggiornamento. Solo ottenere un attributo dovrebbe causare aggiornamenti e solo gli aggiornamenti richiesti.

Questo è esattamente ciò che fa la biblioteca. È molto semplice da implementare in qualsiasi definizione di classe.

Ad esempio, considera una struttura di dipendenza degli attributi mostrata di seguito:

Questastrutturadidipendenzaècompletamentedefinitautilizzandoi descrittori della libreria

:

class DataflowSuccess():

    # The following defines the directed acyclic computation graph for these attributes.
    a1 = DependentAttr(1, [], None, 'a1')
    a2 = DependentAttr(None, ['a1'], 'update_a2', 'a2')
    a3 = DependentAttr(None, ['a2'], 'update_a3', 'a3')
    a4 = DependentAttr(None, ['a1','a2'], 'update_a4', 'a4')
    a5 = DependentAttr(None, ['a1','a2','a3','a6'], 'update_a5', 'a5')
    a6 = DependentAttr(6, [], None, 'a6')
    a7 = DependentAttr(None, ['a4','a5'], 'update_a7', 'a7')

    # ...... define the update functions update_a2, update_a3 etc
    def update_a2():
        ....

Gli argomenti forniti sopra al descrittore DependentAttr sono il valore iniziale dell'attributo, i nomi degli attributi da cui dipende l'attributo, il nome della funzione membro responsabile dell'aggiornamento del suo valore e il nome dell'attributo.

La biblioteca si occupa di tutto il resto. La modifica di un attributo fa sì che i suoi figli (attributi che influisce) (e i loro figli, ecc.) Sappiano che i loro valori devono essere ricalcolati in futuro. Quando viene richiesto il valore di un attributo chiamando __get__ (ad esempio eseguendo obj.a5 ), si verificano solo gli aggiornamenti richiesti, tutti automaticamente dietro le quinte.

Se non soddisfa le tue esigenze, almeno è un utile esempio di come funzionano le funzionalità dei descrittori. Questo esempio è disponibile nella pagina github I descrittori offrono molta flessibilità per modificare come __get__ e __set__ e __del__ lavora per gli attributi della tua classe, che possono risolvere i problemi di dipendenza.

    
risposta data 16.01.2019 - 10:45
fonte

Leggi altre domande sui tag