Hallo,
ich möchte in einem Programm eine "for"-Schleife über alle Instanzen/Objekte einer Klasse laufen lassen. Ich brauche also eine Liste aller Instanzen/Objekte der Klasse X. - Klingt ziemlich einfach, aber ich habe leider nicht herausgefunden, wie das geht. - Könnt ihr mir helfen?
Herzlichen Dank!
Holger
Schleife über alle Instanzen/Objekte einer Klasse
Damit bewegst du dich auf exotischem Terrain. Man könnte das über ein Klassenattribut (zB ein Set) und mit der __new__()-Methode lösen, wo man mittels cls die neue Instanz hinzufügt und in __del__() die abzuräumenden Instanzen löscht. Das ist aber mehr oder weniger schwarze Magie und nur zu empfehlen, wenn man weiß, was man tut. Fast immer gibt es bessere Alternativen, die man dem Verfahren vorziehen sollte.
Die Antwort ist, packe alle Instanzen, die Du betrachten möchtest, in eine Liste. Dann kannst Du einfach darauf zugreifen.
Kannst Du näher beschreiben, warum Du denkst, dass Du alle Instanzen einer Klasse auf magische Weise brauchst?
Kannst Du näher beschreiben, warum Du denkst, dass Du alle Instanzen einer Klasse auf magische Weise brauchst?
- __blackjack__
- User
- Beiträge: 14030
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
An der Stelle bitte ganz dringend `__del__()` wieder vergessen. snafu Du Schuft, wie kannst Du nur!!!1!
Wenn man so etwas machen wollen würde, dann bitte einen Blick ins `weakref`-Modul werfen um Referenzen zu erstellen die nicht ”gelten” wenn sie die einzige Referenz sind, also so zwar den Zugriff ermöglichen wenn das Objekt irgendwo anders noch referenziert werden kann, aber nicht das Löschen/Abräumen verhindern, falls es keine ”starken” Referenzen mehr gibt.
Ist aber Voodoo/schwarze Magie, und mich würde auch erst mal interessieren wozu das eigentlich gebraucht wird.
Wenn man so etwas machen wollen würde, dann bitte einen Blick ins `weakref`-Modul werfen um Referenzen zu erstellen die nicht ”gelten” wenn sie die einzige Referenz sind, also so zwar den Zugriff ermöglichen wenn das Objekt irgendwo anders noch referenziert werden kann, aber nicht das Löschen/Abräumen verhindern, falls es keine ”starken” Referenzen mehr gibt.
Ist aber Voodoo/schwarze Magie, und mich würde auch erst mal interessieren wozu das eigentlich gebraucht wird.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Falls man wirklich meinen Ansatz nutzen möchte, ist das weakref-Modul tatsächlich dem __del__() vorzuziehen. Mit einem WeakSet wird automatisch das Abräumen von nicht mehr referenzierten Objekten übernommen.
Hier mal etwas Code dazu:
Hier ist das natürlich unsinnig, weil die Personen ja bereits in der zuvor erzeugten Liste drinstehen. Jedenfalls kann das get_references() natürlich auch von jeder anderen Stelle im Code abgefragt werden. Ob man das wirklich in dieser Form braucht, kommt auf den Anwendungsfall an. Ganz trivial ist die Nutzung von __new__() und Klassenattributen halt nicht.
Code: Alles auswählen
#!/usr/bin/env python3
from weakref import WeakSet
class RefHolder:
_refs = WeakSet()
def __new__(cls, *args, **kwargs):
instance = object.__new__(cls)
cls._refs.add(instance)
return instance
@classmethod
def get_references(cls):
yield from cls._refs
class Person(RefHolder):
def __init__(self, name, alter, geschlecht):
self.name = name
self.alter = alter
self.geschlecht = geschlecht
def create_persons():
return [
Person("Bob", 42, "m"),
Person("Clara", 18, "w"),
Person("Yoki", 22, "d")
]
def main():
persons = create_persons()
for obj in Person.get_references():
print(obj.name)
if __name__ == "__main__":
main()
@Holger Chapman: safu zeigt zwar, was technisch möglich ist, aber so ein Konstrukt möchte man nicht in einem sauberen Programm haben, das ist zu magisch. Magie bedeutet, dass etwas im Hintergrund verborgen passiert, was man am schwer nachvollziehen kann, und deshalb zu Fehlern führen kann, die man nicht einfach versteht.
Was ist Dein eigentliches Problem, das Du mit Deiner "Lösungsidee" versuchst zu lösen?
Was ist Dein eigentliches Problem, das Du mit Deiner "Lösungsidee" versuchst zu lösen?
Das mit der Magie ist so ein bisschen Ansichtssache. Zu f-Strings gab es hier auch Stimmen, die es zu magisch fanden, dass im String "irgendwie" Bezug auf Objekte aus dem Namensraum genommen wurde. Oder dataclasses: Da wird auch basierend auf Klassenattributen magisch alles mögliche an Methoden hinzugefügt. Oder was SQLAlchemy so treibt, wirkt anfangs auch wie Magie und spielt sogar im Namen noch darauf an. Trotzdem sind diese Tools allesamt sehr praktisch und gar nicht mehr sooo magisch, wenn man mal dahinter geblickt hat.
Klar kann es hier bei der Fragestellung auch ein klassischer Fall von "Wie nummeriere ich Variablen" sein, wo das Problem bloß ist, dass jemand noch keine Listen oder andere geeignete Datenstrukturen kennt und somit erstmal an Nummerierung denkt. Da müsste sich der OP halt mal näher zu äußern...
Klar kann es hier bei der Fragestellung auch ein klassischer Fall von "Wie nummeriere ich Variablen" sein, wo das Problem bloß ist, dass jemand noch keine Listen oder andere geeignete Datenstrukturen kennt und somit erstmal an Nummerierung denkt. Da müsste sich der OP halt mal näher zu äußern...
-
- User
- Beiträge: 35
- Registriert: Samstag 12. Juli 2014, 01:59
Ich versuche ein kleines Programm zu schreiben, das mir in einer Verzeichnishierarchie einen Überblick über alle Verzeichnisse (und Unterverzeichnisse usw.) verschaffen soll. (Z. B.: enthält x MP3s. Oder: hat y Unterordner. Oder: enthält Dateien mit insgesamt z MB.)Sirius3 hat geschrieben: Dienstag 4. Januar 2022, 07:41 [...]
Was ist Dein eigentliches Problem, das Du mit Deiner "Lösungsidee" versuchst zu lösen?
Mein Ansatz war: Ich bilde jedes Verzeichnis als Instanz der Klasse "dir" ab. Die Klasse hat eine Methode "scan", die alle Einträge in dem Verzeichnis (in der Regel: Dateien und Unterverzeichnisse) durchgeht und Informationen sammelt. Im Fall von Unterverzeichnissen ruft die Methode für dieses Unterverzeichnis die Methode "scan" (und damit implizit auch "__init__") auf usw.
Das scheint soweit auch zu funktionieren. Ich habe am Ende für jedes (Unter-)Verzeichnis, das ich abfrage, ein Objekt. Aber ich habe keine Liste aller Objekte, über die ich iterieren kann, um zum Beispiel Ordner mit bestimmten Eigenschaften aufzulisten.
Hab ich mich verständlich ausgedrückt?
Danke für eure Hilfe und
viele Grüße!
Holger
Was hindert dich, diese Objekte in eine Liste zu stecken? So macht man das einfach. Du operierst hier schon wieder gedanklich auf globalen Datenstrukturen. Das war auch vor einigen Jahren schon eine doofe Idee, und ist es immer noch.
Eben, stecke die Ergebnisse in geeignete Strukturen. Wie willst du so ein globales Wirrwarr sonst handhaben? Das wäre bei meinem weakref-Code übrigens auch der Fall. Denn da wird (bisher) nicht nach Typ unterschieden, sondern stumpf alles an neuen Instanzen in das Set geschmissen. Das ist zur produktiven Nutzung also eher Murks. In erster Linie wollte ich zeigen, dass es prinzipiell funktioniert.
EDIT:
Klappt aber, wenn man sich auch den Typen merkt. Sieht nur etwas seltsam aus:
Ich würde das letztlich wohl auch eher als Decorator lösen, ähnlich wie @dataclass das macht. Trotzdem dürfte das keine angemessene Lösung für den OP sein.
EDIT:
Klappt aber, wenn man sich auch den Typen merkt. Sieht nur etwas seltsam aus:
Code: Alles auswählen
from weakref import WeakKeyDictionary, WeakSet
class RefHolder:
_refs = WeakKeyDictionary()
def __new__(cls, *args, **kwargs):
instance = object.__new__(cls)
if cls not in cls._refs:
cls._refs[cls] = WeakSet()
cls._refs[cls].add(instance)
return instance
@classmethod
def get_references(cls):
yield from cls._refs[cls]
-
- User
- Beiträge: 35
- Registriert: Samstag 12. Juli 2014, 01:59
Hallo,
ich habe mal versucht, eure Anregungen zu berücksichtigen, und ein Beispiel geschrieben: ein kleines Programm, das mir (beginnend mit $HOME) die Verzeichnisse mit dem meisten Speicherverbrauch ausgibt.
(Das Programm ist erstmal nur ein Grundgerüst. Es ignoriert zum Beispiel (fehlende) Zugriffsrechte und Verzeichnis-Einträge, die nicht normale Dateien oder Unterverzeichnisse sind.)
Mich interessiert, ob das Programm aus eurer Sicht sauber geschrieben ist, oder was ich verbessern könnte.
Vielen Dank!
Holger
ich habe mal versucht, eure Anregungen zu berücksichtigen, und ein Beispiel geschrieben: ein kleines Programm, das mir (beginnend mit $HOME) die Verzeichnisse mit dem meisten Speicherverbrauch ausgibt.
(Das Programm ist erstmal nur ein Grundgerüst. Es ignoriert zum Beispiel (fehlende) Zugriffsrechte und Verzeichnis-Einträge, die nicht normale Dateien oder Unterverzeichnisse sind.)
Mich interessiert, ob das Programm aus eurer Sicht sauber geschrieben ist, oder was ich verbessern könnte.
Vielen Dank!
Holger
Code: Alles auswählen
#!/usr/bin/env python3
# list 10 dirs with maximum recursive file size (starting with $HOME)
import os
class dir:
def __init__(self, abspath):
self.path = abspath
self.recursivefilesizesum = 0
def scan(self, scanneddirs):
self = dir(self)
for direntry in os.listdir(self.path):
direntry = os.path.join(self.path, direntry)
if os.path.isdir(direntry):
scanneddirs, additionalfilesize = dir.scan(direntry, scanneddirs)
self.recursivefilesizesum = self.recursivefilesizesum + additionalfilesize
elif os.path.isfile(direntry):
self.recursivefilesizesum = self.recursivefilesizesum + file.getsize(direntry)
scanneddirs.append(self)
return scanneddirs, self.recursivefilesizesum
class file:
def __init__(self, abspath):
self.path = abspath
self.size = 0
def getsize(self):
self = file(self)
filesize = os.stat(self.path).st_size
return filesize
def humanReadableFilesize(filesize):
if filesize > (1024**4):
filesizeString = format(filesize/(1024**4), '.1f').rjust(5) + ' T'
elif filesize > (1024**3):
filesizeString = format(filesize/(1024**3), '.1f').rjust(5) + ' G'
elif filesize > (1024**2):
filesizeString = format(filesize/(1024**2), '.1f').rjust(5) + ' M'
elif filesize > 1024:
filesizeString = format(filesize/1024, '.1f').rjust(5) + ' K'
else:
filesizeString = format(filesize, '.1f').rjust(5) + ' B'
return filesizeString
def printlargestdirs(dirlist, number):
dirsizes = {}
for dir in dirlist:
dirsizes[dir.path] = dir.recursivefilesizesum
dirssorted = list(reversed(sorted(dirsizes.items(), key=lambda x: x[1])))
for path, size in dirssorted[0:number]:
print(humanReadableFilesize(size) + " in dir: " + str(path))
def main():
path = os.environ.get("HOME")
showdirs = 10
scanneddirs, x = dir.scan(path, [])
printlargestdirs(scanneddirs, showdirs)
if __name__ == "__main__":
main()
- __blackjack__
- User
- Beiträge: 14030
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Holger Chapman: die Klassen machen keinen Sinn weil bei beiden die Klasse gar nicht wirklich verwendet wird, und das was da gemacht wird ist Murks. `self` sollte bitte `self` bleiben und nicht gleich am Anfang der ”Methode” durch etwas anderes ersetzt werden, was extrem verwirrend ist.
Bei den Klassen sind `size` und `recursivefilesizesum` keine sinnvollen Attribute wenn die nach `__init__()` nicht den tatsächlichen Wert haben.
Klassennamen werden in PascalCase geschrieben. `dir()` ist zudem auch noch der Name einer eingebauten Funktion. Ausser Konstanten (KOMPLETT_GROSS) werden Namen `klein_mit_unterstrichen` geschrieben. Und Unterstriche sind für Leerzeichen zwischen Worten, also `printlargestdirs` istaucheherschwezulesensoganzohne Leerzeichen.
In neuem Code würde man eher zu `pathlib` statt zum alten `os.path` greifen.
Zusammenstückeln von Zeichenketten und Werten mit `str()` und ``+`` ist eher BASIC denn Python. Python hat dafür f-Zeichenkettenliterale.
Das formatieren der Dateigrösse(n) ist ziemlich regelmässig aufgebaut, das liesse sich mit einer Schleife kompakter schreiben.
Bei den Klassen sind `size` und `recursivefilesizesum` keine sinnvollen Attribute wenn die nach `__init__()` nicht den tatsächlichen Wert haben.
Klassennamen werden in PascalCase geschrieben. `dir()` ist zudem auch noch der Name einer eingebauten Funktion. Ausser Konstanten (KOMPLETT_GROSS) werden Namen `klein_mit_unterstrichen` geschrieben. Und Unterstriche sind für Leerzeichen zwischen Worten, also `printlargestdirs` istaucheherschwezulesensoganzohne Leerzeichen.
In neuem Code würde man eher zu `pathlib` statt zum alten `os.path` greifen.
Zusammenstückeln von Zeichenketten und Werten mit `str()` und ``+`` ist eher BASIC denn Python. Python hat dafür f-Zeichenkettenliterale.
Das formatieren der Dateigrösse(n) ist ziemlich regelmässig aufgebaut, das liesse sich mit einer Schleife kompakter schreiben.
Code: Alles auswählen
#!/usr/bin/env python3
# list 10 dirs with maximum recursive file size (starting with $HOME)
from itertools import islice
from pathlib import Path
def format_file_size(size):
for exponent, unit in reversed(list(enumerate("BKMGT"))):
factor = 1024 ** exponent
if size >= factor:
return f"{size / factor:.1f} {unit}"
raise ValueError(f"illegal file size {size!r}")
def scan_directories(path):
result = []
def recurse(path):
size = 0
for sub_path in path.iterdir():
if sub_path.is_dir():
size += recurse(sub_path)
elif sub_path.is_file():
size += sub_path.stat().st_size
result.append((size, path))
return size
recurse(path)
return result
def print_largest_dirs(sizes_and_paths, count):
for size, path in islice(sorted(sizes_and_paths, reverse=True), count):
print(f"{format_file_size(size)} in dir: {path}")
def main():
print_largest_dirs(scan_directories(Path.home()), 10)
if __name__ == "__main__":
main()
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
-
- User
- Beiträge: 35
- Registriert: Samstag 12. Juli 2014, 01:59
Hallo __blackjack__,
vielen Dank für Deine Anregungen und Deine Mühe! Das ging ja deutlich über eine Fünf-Minuten-Antwort hinaus. Super!
Vor allem das doppelte Definieren von "self" kam mir schon selbst komisch vor, aber anders hab ich die Funktionalität zunächst nicht hingekriegt.
Ich werde mir Deine Anregungen in Ruhe ansehen ... ich glaube, damit komme ich schon ein ganzes Stück weiter!
Viele Grüße
Holger
vielen Dank für Deine Anregungen und Deine Mühe! Das ging ja deutlich über eine Fünf-Minuten-Antwort hinaus. Super!
Vor allem das doppelte Definieren von "self" kam mir schon selbst komisch vor, aber anders hab ich die Funktionalität zunächst nicht hingekriegt.
Ich werde mir Deine Anregungen in Ruhe ansehen ... ich glaube, damit komme ich schon ein ganzes Stück weiter!
Viele Grüße
Holger