Seite 1 von 1
Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 15:01
von Serpens66
Hallo
Ich bin auf folgendes python skript gestoßen:
https://github.com/tomasbasham/ratelimi ... _init__.py
Trotz der Erklärung:
# To get around issues with function local scope
# and reassigning variables, we wrap the time
# within a list. When updating the value we're
# not reassigning `last_called`, which would not
# work, but instead reassigning the value at a
# particular index.
last_called = [0.0]
Verstehe ich nicht, warum last_called nicht einfach = 0.0 gesetzt werden kann.
Kann mir das jemand auf deutsch erklären, oder Stichworte auf Deutsch nennen, die ich in einem deutschen Tutorial nachlesen kann?
Auf stackoverflow liest man als Erklärung noch:
# It's a list because simple types like float are constant when captured by a closure. By making it a list, the list is constant, but its contents are not.
Leider hilft das auch noch nicht um es zu verstehen.
Wenn ich nach "function scope" suche, dann stoße ich auf Erklärungen zu lokalen und globalen Variablen. Wüsste aber nicht, warum das in diesem Fall zu einem Problem werden sollte? Liegt das an der Verwendung von decorator/wrapper? Oder den Threads?
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 15:12
von BlackJack
In der `wrapper()`-Funktion wird der Wert in der Liste ja geändert. Das geht (in Python 2) nur *so*, denn wenn dort einfach eine Zuweisung an `last_called` stehen würde, dann wäre damit `last_called` automatisch ein lokaler Name der Funktion `wrapper()` und nicht der Name `last_called` in der umgebenden Funktion.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 15:16
von __deets__
Ganz einfach:
Code: Alles auswählen
def test():
x = 100
def inc_x():
x += 1
return inc_x
test()()
Aufgabe: wie bekommst du das hin, ohne x global zu machen?
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 16:00
von BlackJack
Ich denke ich hätte da einfach eine Klasse geschrieben die man auch ohne einen Kommentar versteht. Das wäre wahrscheinlich auch nicht länger als wenn man eine Erklärung dazu schreiben muss.

Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 16:01
von kbr
__deets__ hat geschrieben:Aufgabe: wie bekommst du das hin, ohne x global zu machen?
Eine klassenbasierte Implementierung des Decorators wäre hier die bessere Lösung gewesen. Die eignet sich für genau solche Fälle.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 16:01
von __deets__
Oder in Python 3 nonlocal verwenden.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 16:12
von kbr
Stimmt. Aber wer mag schon nonlocal ...

Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 16:33
von __deets__
Ich. Objects are a poor mans closure. And closures are a poor mans objects. Man muss es nicht übertreiben, aber auch mal ohne Klasse auskommen ist klasse. Hust.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 17:11
von Serpens66
vielen Dank
und warum werden Listen anders behandelt als Konstanten?
Und da du BlackJack Python 2 erwähnst, in Python 3 gibt es die Problematik nicht?
Und du schreibst man könnte das ganze auch in eine Klasse verpacken. Das fänd ich auch schöner.
Also einfach das ganze skript in eine Klasse verpacken und self.last_call draus machen?
Wie würde dann die Anwendung aussehen?
Aktuell ist es ja einfach:
Wenn meine Klasse nun
heißt, wäre es dann zb.
Code: Alles auswählen
rates = Rate()
@rates.rate_limited(1,2)
def test(a):
return a
?
( ich teste es einfach mal und editiere dann hier, falls ich schnell genug bin)
...
ja scheint so zu funktionieren

Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 17:19
von __deets__
Listen werden nicht anders behandelt. Wenn du
liste = []
machen würdest, hättest du das gleiche Problem. Aber Listen sind veränderbar! Und weil
liste[0] = 100
keine Zuweisung an einen Namen, sondern die Veränderung einer gegebenen Datenstruktur bedeutet, stellt es einen etwas hässlichen workaround dar.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 17:20
von __deets__
Und in meinem Beitrag #2,steht auch was in Python3 anders ist.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 17:28
von Serpens66
Danke

Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 18:24
von BlackJack
@Serpens66: Die Anwendung würde als Klasse genau so aussehen, nur der Name wäre halt ein Klassenname. Es sei denn man weicht hier von der Konvention für Klassennamen ab, dann sieht's genau so aus.
Re: Erklärung "get around issues with function local scope and reassigning variables"
Verfasst: Mittwoch 23. August 2017, 20:24
von Sirius3
Ich würde das ja so schreiben:
Code: Alles auswählen
class WaitingLock(threading.RLock):
def __init__(self, period, every):
self.frequency = abs(every) / float(clamp(period))
self.next_call = 0
def wait(self):
time.sleep(max(time.time() - self.next_call, 0))
self.next_call = time.time() + self.frequency
def rate_limited(period=1, every=1.0, func=None):
'''
Prevent a method from being called
if it was previously called before
a time widows has elapsed.
:param int period: Maximum method invocations within a period. Must be greater than 0.
:param float every: A dampening factor (in seconds). Can be any number greater than 0.
:return: Decorated function that will forward method invocations if the time window has elapsed.
'''
if func is None:
return partial(rate_limited, period, every)
lock = WaitingLock(period, every)
def wrapper(*args, **kargs):
'''Decorator wrapper function'''
with lock:
lock.wait()
return func(*args, **kargs)
return wrapper