In decorator an bestimmtes Argument der Funktion kommen

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
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

So, hab das Problem gelöst, der Code funktioniert jetzt.

Was sollte man noch verbessern?

Code: Alles auswählen

import time
import threading
import concurrent.futures
import queue

def print2(txt,*args):
    txt = str(time.time()) + ": " + txt
    print(txt,*args)

class Limiter():
 
    def __init__(self, zeitlimit, calllimit):
        self.zeitlimit = zeitlimit
        self.calllimit = calllimit # diese zahl an Calls darf innerhalb von zeitlimit sekunden gemacht werden.
        self.gemachte_anfragen =[]
        self.anfragen = queue.Queue()
        self.thread = threading.Thread(target=self.run)
 
 
    def darfich(self,a):
        e = threading.Event()
        wartezeit = []
        self.anfragen.put((e, wartezeit))
        e.wait()
        print2("darfich {} sleep {}".format(a,wartezeit[0]))
        if wartezeit[0] > 0:
            time.sleep(wartezeit[0])
           
    
    def wann_darfich(self):
        jetzt = time.time()

        anfragencopy = self.gemachte_anfragen[:] # eine Kopie machen, damit einträge entfernt werden können (gehts auch weniger fehleranfällig?)
        for timestamp in anfragencopy:
            if timestamp < jetzt - self.zeitlimit:
                self.gemachte_anfragen.remove(timestamp) # alle zu alten einträge entfernen.

        if len(self.gemachte_anfragen) < self.calllimit:
            return jetzt
            
        relevantecalls = self.gemachte_anfragen[-self.calllimit:] # auch zukünftige timestamps muessen beachtet werden

        if len(relevantecalls) < self.calllimit:
            return jetzt
        else: # es wurden soviele wie erlaubt gemacht oder gar mehr, dann warten
            return relevantecalls[0] + self.zeitlimit # relevantecalls[0] ist der timestamp des ältesten relevanten calls
            
    
    def run(self):
        count = 0
        while True:
            count += 1
            e, wartezeit = self.anfragen.get() # wartet, bis etwas vorhanden ist ... nur wie beende ich das, wenn ich es nicht mehr brauche?
            w = self.wann_darfich() # absoluter ts
            self.gemachte_anfragen.append(w)
            wartezeit.append(w-time.time())
            e.set() # e.wait() in "darfich" beenden
            if count>=7: # notmaßnahme damit der thread beendet wird
                return

                
limit = Limiter(5,2)

def test(a):
    limit.darfich(a)
    print2("test {}".format(a))
    return a
     
               
if __name__ == '__main__':  
    limit.thread.start()
    pool = concurrent.futures.ThreadPoolExecutor(7) 
    futures = []
    futures.append(pool.submit(test, 1))
    futures.append(pool.submit(test, 2))
    futures.append(pool.submit(test, 3))
    futures.append(pool.submit(test, 4))
    futures.append(pool.submit(test, 5))
    futures.append(pool.submit(test, 6))
    futures.append(pool.submit(test, 7))
    concurrent.futures.wait(futures) 
    print2("Vollständig beendet!")
__deets__
User
Beiträge: 14540
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nur kurz: du kannst dem queue.get ein timeout-Argument mitgeben. Das fuehrt dann zu einer Ausnahme wenn innerhalb der Zeit keine Anfrage kam. Dann kannst du checken, ob der Limiter noch laufen soll, und wenn nicht, run verlassen.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

__deets__ hat geschrieben:Nur kurz: du kannst dem queue.get ein timeout-Argument mitgeben. Das fuehrt dann zu einer Ausnahme wenn innerhalb der Zeit keine Anfrage kam. Dann kannst du checken, ob der Limiter noch laufen soll, und wenn nicht, run verlassen.
danke :)
Ja das wäre auch meine nächste bessere Notlösung gewesen.
Hatte gehofft es gibt noch einen "queue auflösen" befehl oderso, aber finde auch nichts in der Doku.
Dann werd ich es mit dem timeout machen.

Was ist eigentlich "schlimmer", eine while schleife die sich ständig wiederholt? Oder ein Warten dass durch Queue.get() verursacht wird?
Also kann ich problemlos den timeout auf 0.001 setzen? Oder dreht mir die while schleife sonst durch und frisst zuviele Ressourcen?

sieht jetzt so aus:

Code: Alles auswählen

def run(self):
        while True:
            try:
                e, wartezeit = self.anfragen.get(timeout=0.001) # wartet, bis etwas vorhanden ist
            except queue.Empty:
                if not self.wirdgebraucht:
                    return
                continue # das warten von get() muss ab und zu unterbrochen werden, um zu schauen, ob dieser Thread noch gebraucht wird, oder nicht. Zb. wenn sonst alles fertig ist, sollte das hier auch beendet werden

            w = self.wann_darfich() # absoluter ts
            self.gemachte_anfragen.append(w)
            wartezeit.append(w-time.time())
            e.set() # e.wait() in "darfich" beenden
            if not self.wirdgebraucht:
                return
self.wirdgebraucht wird dann halt vor "vollständig beendet" auf False gesetzt.
__deets__
User
Beiträge: 14540
Registriert: Mittwoch 14. Oktober 2015, 14:29

Schleife ist schlimm. Das verbraet Rechenzeit.

Du kannst auch stattdessen einen sentinel in die Queue tun: implementiere eine stop-Methode auf dem Limiter, und die stopft ein None-Objekt in die Queue, und ruft dann ein join auf dem self.thread auf. Das wird dann bei naechstbester Gelegenheit ausgepackt, und wenn du vorher pruefst, ob das Argument None ist statt ein Tupel das ausgepackt werden will, beendest du dich. So geht's auch ohne timeout.
Antworten