Was passiert, wenn wir bearbeiten (anhängen, entfernen …) eine Liste und können wir Aktionen ausführen, jedes Mal, wenn eine Liste bearbeitet wird

Ich würde gerne wissen, ob es einen Weg, um eine list , die einige Aktionen ausführen wird jedes Mal, wenn ich die Methode append (oder eine andere ähnliche Methode). Ich weiß, dass ich eine class erstellen kann, die von der list erbt und append , remove und alle anderen Methoden, die den Inhalt der list ändern, aber ich würde gerne wissen, ob es einen anderen Weg gibt. Im Vergleich, wenn ich "editiert" jedes Mal, wenn ich ein Attribut eines Objekts bearbeiten, wird ich nicht ausführen print("edited") in allen Methoden der class dieses Objekts. Stattdessen werde ich nur __setattribute__ überschreiben. Ich habe versucht, meinen eigenen Typ zu erstellen, der die list erbt und __setattribute__ überschreibt. Das __setattribute__ aber nicht. Wenn ich myList.append __setattribute__ wird nicht aufgerufen. Ich würde gerne wissen, was wirklich passiert ist, wenn ich myList.append ? Gibt es einige magische Methoden, die ich überschreiben könnte?

Ich weiß, dass die Frage schon dort gefragt wurde: Was passiert, wenn du "append" auf einer Liste nennst? . Die Antwort ist nur, es gibt keine Antwort … Ich hoffe es ist ein Fehler.

Ich weiß nicht, ob es eine Antwort auf meine Anfrage gibt, also werde ich Ihnen auch erklären, warum ich mit diesem Problem konfrontiert werde. Vielleicht kann ich in eine andere Richtung suchen, um zu tun, was ich will Ich habe eine class mit mehreren Attributen. Wenn ein Attribut bearbeitet wird, möchte ich einige Aktionen ausführen. Wie ich es vorher erkläre, um das zu tun, bin ich benutze, um __setattribute__ zu überschreiben. Das funktioniert für die meisten Attribute gut. Das Problem ist lists . Wenn das Attribut wie folgt verwendet wird: myClass.myListAttr.append(something) , __setattribute__ wird nicht aufgerufen, während sich der Wert des Attributs geändert hat.

Das Problem wäre das gleiche mit dictionaries . Methoden wie pop nennt nicht __setattribute__ .

One Solution collect form web for “Was passiert, wenn wir bearbeiten (anhängen, entfernen …) eine Liste und können wir Aktionen ausführen, jedes Mal, wenn eine Liste bearbeitet wird”

Wenn ich es richtig verstehe, würdest du so etwas wie Notify_list , das irgendeine Methode nennen würde (Argument für den Konstruktor in meiner Implementierung) jedes Mal, wenn eine mutierende Methode aufgerufen wird, also könntest du so etwas machen:

 class Test: def __init__(self): self.list = Notify_list(self.list_changed) def list_changed(self,method): print("self.list.{} was called!".format(method)) >>> x = Test() >>> x.list.append(5) self.list.append was called! >>> x.list.extend([1,2,3,4]) self.list.extend was called! >>> x.list[1] = 6 self.list.__setitem__ was called! >>> x.list [5, 6, 2, 3, 4] 

Die einfachste Implementierung wäre es, eine Unterklasse zu erstellen und jede mutierende Methode zu überschreiben:

 class Notifying_list(list): __slots__ = ("notify",) def __init__(self,notifying_method, *args,**kw): self.notify = notifying_method list.__init__(self,*args,**kw) def append(self,*args,**kw): self.notify("append") return list.append(self,*args,**kw) #etc. 

Das ist offensichtlich nicht sehr praktisch, das Schreiben der ganzen Definition wäre sehr langweilig und sehr repetitiv, so dass wir die neue Unterklasse dynamisch für jede gegebene Klasse mit Funktionen wie die folgenden erstellen können:

 import functools import types def notify_wrapper(name,method): """wraps a method to call self.notify(name) when called used by notifying_type""" @functools.wraps(method) def wrapper(*args,**kw): self = args[0] # use object.__getattribute__ instead of self.notify in # case __getattribute__ is one of the notifying methods # in which case self.notify will raise a RecursionError notify = object.__getattribute__(self, "_Notify__notify") # I'd think knowing which method was called would be useful # you may want to change the arguments to the notify method notify(name) return method(*args,**kw) return wrapper def notifying_type(cls, notifying_methods="all"): """creates a subclass of cls that adds an extra function call when calling certain methods The constructor of the subclass will take a callable as the first argument and arguments for the original class constructor after that. The callable will be called every time any of the methods specified in notifying_methods is called on the object, it is passed the name of the method as the only argument if notifying_methods is left to the special value 'all' then this uses the function get_all_possible_method_names to create wrappers for nearly all methods.""" if notifying_methods == "all": notifying_methods = get_all_possible_method_names(cls) def init_for_new_cls(self,notify_method,*args,**kw): self._Notify__notify = notify_method namespace = {"__init__":init_for_new_cls, "__slots__":("_Notify__notify",)} for name in notifying_methods: method = getattr(cls,name) #if this raises an error then you are trying to wrap a method that doesn't exist namespace[name] = notify_wrapper(name, method) # I figured using the type() constructor was easier then using a meta class. return type("Notify_"+cls.__name__, (cls,), namespace) unbound_method_or_descriptor = ( types.FunctionType, type(list.append), #method_descriptor, not in types type(list.__add__),#method_wrapper, also not in types ) def get_all_possible_method_names(cls): """generates the names of nearly all methods the given class defines three methods are blacklisted: __init__, __new__, and __getattribute__ for these reasons: __init__ conflicts with the one defined in notifying_type __new__ will not be called with a initialized instance, so there will not be a notify method to use __getattribute__ is fine to override, just really annoying in most cases. Note that this function may not work correctly in all cases it was only tested with very simple classes and the builtin list.""" blacklist = ("__init__","__new__","__getattribute__") for name,attr in vars(cls).items(): if (name not in blacklist and isinstance(attr, unbound_method_or_descriptor)): yield name 

Sobald wir Notify_list create Notify_list oder Notify_dict wäre so einfach wie:

 import collections mutating_list_methods = set(dir(collections.MutableSequence)) - set(dir(collections.Sequence)) Notify_list = notifying_type(list, mutating_list_methods) mutating_dict_methods = set(dir(collections.MutableMapping)) - set(dir(collections.Mapping)) Notify_dict = notifying_type(dict, mutating_dict_methods) 

Ich habe das nicht ausgiebig getestet und es ist wohl auch Bugs / unbehandelte Eckkästen, aber ich weiß, dass es richtig funktioniert mit der list !

  • Wie überprüfe ich, ob eine Eingabe in einer Liste in Python ist?
  • Python-Liste zum Speichern der Klasseninstanz?
  • Python-Listen / Arrays: Deaktivieren Sie die negativen Indexing-Wrap-around in Slices
  • Unerwartetes Verhalten von verschachtelten Listen in Python
  • Effizient zu wissen, ob Kreuzung von zwei Liste ist leer oder nicht, in Python
  • Python-Suche Regex aus Variable innerhalb einer Liste
  • Die effizienteste Möglichkeit, Duplikate aus der Python-Liste zu entfernen, während die Reihenfolge beibehalten und das älteste Element entfernt wird
  • Python-Funktion, um eine Liste von gemeinsamen Buchstaben im Vor- und Nachnamen zurückzugeben
  • Python: Zählfrequenz von Wörtern in einer Liste
  • Python - Liste Komplementarität mit Test, um die Division durch Null zu vermeiden
  • AttributeError: 'list' Objekt hat kein Attribut 'dtype'
  • Python ist die beste Programmiersprache der Welt.