Borg pattern nur bei bestimmten Class Atributen?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sonium
User
Beiträge: 66
Registriert: Mittwoch 27. Oktober 2004, 21:04

Freitag 19. Mai 2006, 16:42

Hallo,
kennt jemand eine Möglichkeit zu veranlassen, dass sich verschiedene Objekte eine Variable teilen?

Also dass folgendes funktioniert:

Code: Alles auswählen

x = theclass()
y = theclass()

y.a = 1
x.a
> 1
x.a = 2
y.a
> 2

aber eben nur für a und nicht fuer z.B. b?
- http://bash.org/?400459
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Freitag 19. Mai 2006, 17:02

sonium hat geschrieben:kennt jemand eine Möglichkeit zu veranlassen, dass sich verschiedene Objekte eine Variable teilen?
Hi sonium!

Das geht nicht mit einer Zahl oder mit einem String. Aber es geht z.B. mit einer Liste. (Wenn unbedingt notwendig. Vielleicht überdenkst du auch dein Design, da so etwas recht schnell schief gehen kann.)

Code: Alles auswählen

>>> class A:
...     pass
...     
>>> a = A()
>>> b = A()
>>> a.a = [1]
>>> b.a = a.a
>>> a.a[0] = 10 # Nicht überschreiben sondern **aendern**
>>> b.a
[10]
>>> 
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Gromit
User
Beiträge: 51
Registriert: Montag 8. Mai 2006, 19:07

Freitag 19. Mai 2006, 20:08

Es geht; ist aber, wie Gerold schon ausgeführt hat, von eher akademischem Wert.

HTH,
Gerald

Anbei der Hack:

Code: Alles auswählen

def genenerateGetter( name ):

    def getShared( borg ):
        try:
            return borg.__borg_dict__[ name ]
        except KeyError:
            raise AttributeError( 
                    "%r object has no attribute %r" % ( borg, name ) )

    return getShared


def genenerateSetter( name ):
    
    def setShared( borg, value ):
        borg.__borg_dict__[ name ] = value

    return setShared


def genenerateDeleter( name ):

    def delShared( borg ):
        try:
            del borg.__borg_dict__[ name ]
        except KeyError:
            raise AttributeError( 
                    "%r object has no attribute %r" % ( borg, name ) )

    return delShared

    
class ShizophrenicBorgMeta( type ):

    def __new__( self, name, bases, namespace ):
        try:
            sharedAttributeNames = namespace[ "sharedAttributeNames" ]
        except:
            return type.__new__( self, name, bases, namespace )
        else:
            for name in sharedAttributeNames:
                namespace[ name ] = property(
                        genenerateGetter( name ),
                        genenerateSetter( name ),
                        genenerateDeleter( name ) )
            namespace[ "__borg_dict__" ] = {}
            return type.__new__( self, name, bases, namespace )
            

class ShizophrenicBorg( object ):

    __metaclass__  = ShizophrenicBorgMeta
    sharedAttributeNames = []


__all__ = [ "ShizophrenicBorg" ]


if __name__ ==  "__main__":
    
    class Locutus( ShizophrenicBorg ):

        sharedAttributeNames = [ "brain" ]    

        def __init__( self ):
            self.brain = "brain"
            self.voice = "voice"

    l0 = Locutus()
    l1 = Locutus()
    assert l0.brain == "brain"
    assert l0.voice == "voice"
    assert l1.brain == "brain"
    assert l1.voice == "voice"
    l0.voice = None
    assert l0.voice is None
    assert l1.voice == "voice"
    l0.brain = None
    assert l0.brain is None
    assert l1.brain is None
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Samstag 20. Mai 2006, 09:53

Hi!

@Gromit: Nicht schlecht! Gefällt mir. :o

Hier ein etwas primitiveres Beispiel:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# NewStyle-Klasse mit **Slots**. Es können nur Klassenattribute verwendet
# werden, die in **__slots__** definiert wurden. So schützt man sich vor
# Schreibfehlern und die Klasse wird schneller.
class GemeinsameDaten(object):
    
    # Jede mögliche Variable als Slot definieren (das bringt Sicherheit)
    __slots__ = ("x", "y", "farbe")
    
    def __init__(self):
        """Die Variablen vorbereiten"""
        self.x = None
        self.y = None
        self.farbe = None
    
    def __repr__(self):
        """
        Die Variablen als String-Repräsentation zurück geben
        (Das war nur die Fleißaufgabe ;-) -- es geht auch ohne.)
        """
        values = [ 
            "'%s': %s" % (var, repr(getattr(self, var))) 
            for var in self.__slots__ 
        ]
        return "{%s}" % ", ".join(values)

class A:
    def __init__(self, gemdat):
        self.gemdat = gemdat

# Die Klasseninstanz mit den gemeinsamen Daten wird einfach 
# als Parameter uebergeben. So spart man sich das unangenehme
# und fehleranfaellige arbeiten mit globalen Variablen.
gemdat = GemeinsameDaten()
a = A(gemdat)
b = A(gemdat)

a.gemdat.x = 10
a.gemdat.y = 10

print "b:", b.gemdat

b.gemdat.x = 8
b.gemdat.farbe = "Gelb"

print "a:", a.gemdat
print "b:", b.gemdat

Code: Alles auswählen

b: {'x': 10, 'y': 10, 'farbe': None}
a: {'x': 8, 'y': 10, 'farbe': 'Gelb'}
b: {'x': 8, 'y': 10, 'farbe': 'Gelb'}
Hier das gleiche Beispiel, vereinfacht, noch einmal. Dafür wurde die Methode **__repr__** weggelassen, da sie die Klasse komplizierter macht. Das ist nicht gut, wenn man eigentlich etwas anderes erklären möchte.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class GemeinsameDaten(object):
    
    __slots__ = ("x", "y", "farbe")
    
    def __init__(self):
        """Die Variablen vorbereiten"""
        self.x = None
        self.y = None
        self.farbe = None
    
class A:
    def __init__(self, gemdat):
        self.gemdat = gemdat


gemdat = GemeinsameDaten()
a = A(gemdat)
b = A(gemdat)

a.gemdat.x = 10
a.gemdat.y = 10

print "b:", b.gemdat.x, b.gemdat.y, b.gemdat.farbe

b.gemdat.x = 8
b.gemdat.farbe = "Gelb"

print "a:", a.gemdat.x, a.gemdat.y, a.gemdat.farbe
print "b:", b.gemdat.x, b.gemdat.y, b.gemdat.farbe

Code: Alles auswählen

b: 10 10 None
a: 8 10 Gelb
b: 8 10 Gelb
mfg
Gerold
:-)

(Suchworte: gemeinsam, gemeinsame, Variable, Variablen, global, globale, vermeiden, Klasse, teilen)
Zuletzt geändert von gerold am Samstag 20. Mai 2006, 10:10, insgesamt 1-mal geändert.
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Samstag 20. Mai 2006, 10:04

Hi!

Das vorher aufgezeigte Schema funktioniert auch ohne **NewStyle**-Klassen, aber dann ist es nicht mehr so sicher. Das folgende Beispiel zeigt wie es ohne NewStyle-Klasse funktioniert. ABER --> das ist dann wirklich schon eine Stufe vor globalen Variablen. Und das ist als Warnung zu verstehen.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

class GemeinsameDaten:
    pass
    
class A:
    def __init__(self, gemdat):
        self.gemdat = gemdat


gemdat = GemeinsameDaten()
a = A(gemdat)
b = A(gemdat)

a.gemdat.x = 10
a.gemdat.y = 10
a.gemdat.farbe = None # muss jetzt definiert werden, sonst gibt es einen Fehler
a.gemdat.irgendeinevariable = "irgendeinwert" # Es können unkontrolliert, Werte
                                              # übergeben werden.
a.gemdat.Farbe = "Blau" # Das ist ein typischer Schreibfehler

print "b:", b.gemdat.x, b.gemdat.y, b.gemdat.farbe

b.gemdat.x = 8
b.gemdat.farbe = "Gelb"

print "a:", a.gemdat.x, a.gemdat.y, a.gemdat.farbe
print "b:", b.gemdat.x, b.gemdat.y, b.gemdat.farbe

Code: Alles auswählen

b: 10 10 None
a: 8 10 Gelb
b: 8 10 Gelb
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Antworten