random und SystemRandom

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.
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

Hallo Liebe Leute,

ich würde bei Start meines Programmes gerne
auswählen, ob die Funktionen in random oder die in SystemRandom
benutzt werden. Wie macht man so etwas prinzipiell ?
Kennt jemand eine Referenz auf ein 'recipe', das so etwas oder etwas
Vergleichbares macht ? Ach ja, ein Schlagwort auf die Technik würde
mir auch schon helfen.

Grüße,
Matthias Kievernagel
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Da beide die selbem Methoden haben (auch wenn manche bei SystemRandom nichts tun), kannst du einfach eine der Klassen bzw Exemplare als Argument erwarten und darauf dann arbeiten. Oder was meinst du genau?
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

Ich würde gern in untergeordneten Modulen einfach

Code: Alles auswählen

  import random

  ...
  return random.randint(1, 6)
  ...
haben und im TopLevel-Script festlegen, ob das random.xxx oder random.SystemRandom.xxx
bedeutet. In C würde ich das im Makefile mit Compiler- / Link-Optionen machen
(DEBUG / FINAL - Build).

Ich hoffe, die Absicht ist jetzt klarer.
Matthias Kievernagel.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Code: Alles auswählen

import random

SYSRANDOM = True
myrandom = random.SystemRandom() if SYSRANDOM else random

print myrandom.randint(2,8)
print myrandom.randrange(3,19)
print myrandom.random()
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

Diese Weiche müßte ich dann aber in jedem Modul einbauen.
Ich hätte eigentlich gern, daß untergeordnete Module
von der Umschaltung gar nichts wissen und einfach
ganz blöd den Code aus meinem vorhergegangenen Posting
enthalten (Weiterverwendbarkeit, etc...).

Danke für den Vorschlag,
Matthias Kievernagel
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Diese Weiche müßte ich dann aber in jedem Modul einbauen.
Nicht, wenn du aus dem Beispielcode ein Modul myrandom machst, das du dann überall importierst.

Code: Alles auswählen

# dies ist myrandom.py
import random
SYSRANDOM = True
_m = random.SystemRandom() if SYSRANDOM else random
randint = _m.randint
random = _m.random

# dies ist dein Code
from myrandom import randint

print randint(1, 6)
Stefan
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Nein, so ohne weiteres geht das (ohne Verrenkungen nicht). Du solltest besser ein eigenes random-Modul schreiben mit der Weiche und dann auf dieses aus Untermodulen zugreifen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

Ich habe ja inzwischen auch etwas nachgedacht :-)
Die "Verrenkungen" würde Herumbasteln am importierten
random 'module'-Objekt (bzw. Ersetzen durch ein Fake) bedeuten?
Liege ich da in etwa richtig?
An den Import-Mechanismus muß man aber nicht ran,
wenn 'random' schon importiert ist, wird es ja nicht erneut
geladen, oder?

Vielen Dank,
Matthias Kievernagel

PS: Ich habe auch mal 'ne Anfrage in comp.lang.python gestartet.
Wenn da etwas herauskommt, fasse ich das hier mal zusammen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

mkiever hat geschrieben:Ich habe ja inzwischen auch etwas nachgedacht :-)
Die "Verrenkungen" würde Herumbasteln am importierten
random 'module'-Objekt (bzw. Ersetzen durch ein Fake) bedeuten?
Ne, die würden bedeuten dass du das ``random``-Modul überdeckst und dann da deinen Krams reinsetzt. *blink* *blink* dumme idee *blink* *blink*
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

So, nach einem Hinweis aus comp.lang.python habe ich jetzt folgendes
Funktionsfähiges zusammengebaut:

Code: Alles auswählen

from optparse import OptionParser
import random
import sys

__op = OptionParser()
__op.add_option('-d', '--debug',
                help='debug build with fixed random generator',
                action="store_true", dest='debugFlag', default=False)
(__opts, __args) = __op.parse_args()

if not __opts.debugFlag:
    #print('setup using SystemRandom')
    sys.modules['random'] = random.SystemRandom()

# ... alle weiteren Importe
# ... die Applikationsklasse

if __name__ == '__main__':
    b = BritApp()
    b.setup(prefix=__opts.pathPrefix, debug=__opts.debugFlag)
    b.cmdloop()
Alternativ habe ich noch folgende "monkey-patch" Variante ausprobiert:

Code: Alles auswählen

#...
if not __opts.debugFlag:
    #print('setup using SystemRandom')
    sr = random.SystemRandom()
    random.choice = sr.choice
    random.getrandbits = sr.getrandbits
    random.getstate = sr.getstate
    random.randint = sr.randint
    random.random = sr.random
    random.randrange = sr.randrange
    random.sample = sr.sample
    random.seed = sr.seed
    random.setstate = sr.setstate
    random.shuffle = sr.shuffle
#...
Funktioniert auch. Obige Variante gefällt mir besser, da ich da gleich
eine Fehlermeldung bekomme, falls irgendetwas Untergeordnetes versucht,
SystemRandom direkt zu benutzen.

Grüße,
Matthias Kievernagel
lunar

@mkiever: Der Hinweis von Leonidas, dass es sich dabei um eine „dumme Idee“ handelt, war durchaus ernst gemeint. Im Allgemeinen werden solche „Hacks“ ziemlich kritisch gesehen. Möchtest Du, dass Deine Module "SystemRandom()" benutzt, dann schreibe sie so um, dass sich das explizit tun:

Code: Alles auswählen

from random import SystemRandom
random = SystemRandom()
Wenn Dir das zu viel Arbeit ist, spricht das nicht für Deine Methode, sondern eher gegen Deinen Quelltext, welcher dann so restrukturiert werden sollte, dass die Erzeugung des Zufallsgenerators nur an einer Stelle stattfindet, beispielsweise indem Du im Toplevel-Paket Deiner Anwendung obigen Quelltext einfügst, und anschließend in den anderen Modulen "from myapp import random" nutzt.

Was genau bezweckst du mit dem Gebrauch zweier Unterstriche vor den Namen?! Und wieso erzeugst Du die OptionParser-Klasse auf Modul-Ebene, wenn Du sie doch genauso gut unterhalb von "__main__" erzeugen könntest?
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

@lunar:
lunar hat geschrieben:Der Hinweis von Leonidas, dass es sich dabei um eine „dumme Idee“ handelt, war durchaus ernst gemeint. Im Allgemeinen werden solche „Hacks“ ziemlich kritisch gesehen.
Tja, wenn's nun mal nicht anders geht.
lunar hat geschrieben:Möchtest Du, dass Deine Module "SystemRandom()" benutzt, dann schreibe sie so um, dass sich das explizit tun:
Das möchte ich gar nicht. Ich möchte:
mkiever hat geschrieben:ich würde bei Start meines Programmes gerne auswählen, ob die Funktionen in random oder die in SystemRandom benutzt werden.
Und zwar, wie im Listing zu sehen, anhand eines Kommandozeilenparameters "-d".
lunar hat geschrieben:beispielsweise indem Du im Toplevel-Paket Deiner Anwendung obigen Quelltext einfügst, und anschließend in den anderen Modulen "from myapp import random" nutzt.
Hmm. Alle diese Module wären nicht wiederverwendbar.
Das ist für mich ein Ausschlußkriterium.
lunar hat geschrieben:Was genau bezweckst du mit dem Gebrauch zweier Unterstriche vor den Namen?!
Ops. Das mußte ich nachschauen. Ich war irgendwie der Meinung,
das ginge auch auf Modulebene zum Markieren von privaten Namen.
Vielen Dank.
lunar hat geschrieben:Und wieso erzeugst Du die OptionParser-Klasse auf Modul-Ebene, wenn Du sie doch genauso gut unterhalb von "__main__" erzeugen könntest?
Ich muß den Umbau mit 'random' machen, bevor sich untergeordnete Module
ggf. mit "from random import randint" Zugriff auf die falsche Funktion verschaffen.
Aber es sollte trotzdem mit einem "if __name__ == '__main__':" versehen sein, korrekt.

Grüße,
Matthias Kievernagel.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mkiever hat geschrieben:Ops. Das mußte ich nachschauen. Ich war irgendwie der Meinung,
das ginge auch auf Modulebene zum Markieren von privaten Namen.
Zum Festlegen von privaten Namen ist _immer_ nur ein führender Unterstrich vorgesehen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

@mkiever: Das klingt nach nem furchtbar kaputten Programm dass du da monkeypatcht. Kannst du es nicht einfach umschreiben, damits nicht mehr so kaputt ist? Vielleicht gegebenfalls ein paar Patches an den ursprünglichen Autor? Es ist auch irgendwie nicht ganz klar warum du unbedingt so etwas seltsames machen musst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

@ /me:
/me hat geschrieben:Zum Festlegen von privaten Namen ist _immer_ nur ein führender Unterstrich vorgesehen.
Ich hatte das folgende im Kopf und hatte es auf Module ausgedehnt:

http://docs.python.org/reference/lexica ... dentifiers
__*
Class-private names. Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes. See section Identifiers (Names).
Grüße,
Matthias Kievernagel
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

@ Leonidas:

Der originale Autor bin ich selbst. An der kaputten Struktur ist also definitiv niemand
anders schuld ;-)
Leonidas hat geschrieben:Es ist auch irgendwie nicht ganz klar warum du unbedingt so etwas seltsames machen musst.
Also ich versuche das mal zu erklären.
Vorweg gebe ich gleich mal zu, daß ich das ganze Thema verschärft habe, indem ich
random und SystemRandom alternativ haben wollte. Für den Applikationsbereich (Spiel)
würde sicherlich random mit konstantem Seed vs. random mit time Seed ausreichen.
Aber dann wollte ich gucken wie man das machen würde - es gibt ja durchaus Bereiche
in denen SystemRandom für eine finale Version unumgänglich wäre.

Für die debug-Version will ich einfach einen Zufallsgenerator haben, der eine scheint's
zufällige Folge von Zahlen liefert, aber eben jedesmal dieselbe. Die echte Version
soll eben richtig zufällige Zahlen verwenden. Das ganze gesteuert über einen
Kommandozeilenparameter. SystemRandom hat eben den Nachteil, daß es
"richtig" zufällig ist und keine wiederholbaren Folgen liefern kann.

Einige hatten ein Modul "myrandom" oder ein Zugriff auf random in der Applikationsklasse
vorgeschlagen. Das gefällt mir überhaupt nicht, da es die Weiterverwendung der Module unmöglich machen
würde. Oder gibt es dafür Beispiele in existierenden Modulen / Bibliotheken ?

Ich hoffe, damit ist meine Absicht etwas klarer.
Matthias Kievernagel.
lunar

Beim Monkey-Patchen globaler Module sind Deine eigenen Module nicht mehr und nicht weniger wiederverwendbar als bei der Nutzung eines eigenen Random-Moduls. Eine Abhängigkeit besteht so oder so, nur eben zu einem anderen Modul. Beim eigenen Modul ist die Abhängigkeit aber wenigstens explizit, so dass man auch nach sechs Monaten noch versteht, was da passiert.

Wenn Dir der Lösungsansatz über ein Modul nicht gefällt, dann erzeuge eben ein Exemplar des Zufallsgenerators an zentraler Stelle und reiche es an die Klassen und Funktionen, die einen Zufallsgenerator benötigen, durch. Alternativ kannst Du auch die Klasse des Zufallsgenerators übergeben.

Das Argument „geht nicht anders“ lasse ich nicht gelten. Es geht immer anders, man muss es nur wollen. Mag sein, dass eine explizite Lösung aufwendiger ist als das Monkey-Patchen, dafür ist sie leichter zu testen, verständlicher und wartbarer und somit auf lange Sicht sinnvoller.
BlackJack

@mkiever: Sauber gelöst würde man halt überall wo Zufall erzeugt werden soll nicht fest auf das `random`-Modul zugreifen, sondern das Objekt als Argument übergeben. Oder eben auf ein "Zwischenmodul" zugreifen. Wobei ich nicht ganz verstehe was das mit dem "nicht wiederverwendbar" auf sich hat. Natürlich kann man die Module wiederverwenden. Man muss halt nur ein `myrandom`-Modul zur Verfügung stellen.

Von `SystemRandom` würde ich in einem Spiel wohl auch die Finger lassen. Je nach System und Implementierung kann die Erzeugung von Zufallszahlen blockieren wenn das System meint es hat nicht genügend "Entropie gesammelt" um Zahlen zu liefern die zufällig genug sind. Die Funktion kehrt dann zum Beispiel erst zurück wenn genug Tastatur- und Mausaktivität vom Benutzer kam.

Feste vs. "zufällige" Folge kannst Du mit dem normalen `random` einfacher haben. Wenn fest, dann `random.seed()`-Aufrufen, sonst gar nichts machen.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mkiever hat geschrieben:Ich hatte das folgende im Kopf und hatte es auf Module ausgedehnt:

http://docs.python.org/reference/lexica ... dentifiers
Verständlich. Darauf bin ich auch zu Beginn hereingefallen. Das Interessante daran ist allerdings das "mangled form".
Schau dir mal an, was im Namensraum passiert wenn du zwei Unterstriche verwendest.

Code: Alles auswählen

class Foo(object):
    _bar = 'x'
    __baz = 'y'
    
print dir(Foo)

class Foobar(Foo):
    _bar = 'a'
    __baz = 'b'
    
print dir(Foobar)
lunar

@BlackJack: Auf welchem System blockiert "/dev/urandom" denn? Ich wüsste keines ... es ist halt nur wesentlich langsamer als ein PRNG.
Antworten