Problem
W celach pedagogicznych chciałbym liczyć, ile razy dana linia jest wykonywana w danej funkcji bez modyfikowania lub dekoracji go . Na przykład dla funkcji:
def binary_search(seq, x):
(a, b) = (0, len(seq) - 1)
while a <= b:
m = (a + b) / 2
if x < seq[m]:
b = m - 1
elif x > seq[m]:
a = m + 1
else:
return m
Właśnie piszę coś takiego:
print count_exec(binary_search, range(100), 44, line_number = 4)
... a nawet taki:
print count_exec(binary_search(range(100), 44), line = "m = (a + b) / 2")
... Którego powinni drukować liczbę razy czwarta linia jest wykonywana (która wynosi 7). Ostatecznym celem jest zapewnienie empirycznego podejścia do złożoności dowolnej funkcji:
Niestety
Moje obecne rozwiązanie polega na dodaniu atrybutu funkcji:
def binary_search(seq, x):
binary_search.count = 0 # <------------------ added
(a, b) = (0, len(seq) - 1)
while a <= b:
binary_search.count += 1 # <------------- added
m = (a + b) / 2
if x < seq[m]:
b = m - 1
elif x > seq[m]:
a = m + 1
else:
return m
binary_search(range(100), 44)
print binary_search.count
Myślę, że mogę utworzyć zdobione funkcję count_this_line
:
def binary_search(seq, x):
(a, b) = (0, len(seq) - 1)
while a <= b:
count_this_line() # <-------------------- added
m = (a + b) / 2
...
Może być możliwe, aby udekorować samą funkcję binary_search
, ale dla mnie liczy się jako modyfikacja.
Pomysły
- Standardowa biblioteka AST może pobrać abstrakcyjne drzewo składni o dowolnym skrypcie i nawet wykonaj go.
- Mam małe doświadczenie w użyciu profilera Pythona. Wydaje się to dość ciężkie dla moich potrzeb. Czy może to być droga?
2 odpowiedzi
Możesz użyć modułu line_profiler
do zrobienia (Patrz Docs). Należy pamiętać, że musiałem uzyskać wersję zgodną 3.x z rozwidlony repo - nie jestem pewien, czy jest połączony jeszcze.
Na przykład. Umieściłem funkcję wyszukiwania binarnego w pliku, a następnie dodałem to:
prof = profile(binary_search)
prof(range(100), 44)
To jest to samo, co A @profile
Decorator, jak wspomniano w Dokumentach, ale nie musisz modyfikować kodu oryginału . Pobiegłem
kernprof.py -l binsearch.py
python -m line_profiler binsearch.py.lprof
I wypłynęło to:
Function: binary_search at line 1
Total time: 4.1e-05 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def binary_search(seq, x):
2 1 6 6.0 14.6 (a, b) = (0, len(seq) - 1)
3 7 8 1.1 19.5 while a <= b:
4 7 7 1.0 17.1 m = (a + b) // 2
5 7 8 1.1 19.5 if x < seq[m]:
6 2 2 1.0 4.9 b = m - 1
7 5 5 1.0 12.2 elif x > seq[m]:
8 4 4 1.0 9.8 a = m + 1
9 else:
10 1 1 1.0 2.4 return m
"Hits" to numer, którego szukasz. Jako bonus otrzymujesz również informacje o czasie, choć byłoby bardziej dokładne z wieloma egzekucjami.
Po sugestii Jasona napisałem czyste rozwiązanie Pythona:
import line_profiler
import __builtin__
import cStringIO
import re
def profile(path, function_call, line_number):
prof = line_profiler.LineProfiler()
__builtin__.__dict__['profile'] = prof
script = open(path).read()
ns = locals()
function_name = function_call[:function_call.index("(")]
rex = re.compile("((?ms)^def %s.+)" % function_name)
script = rex.sub(r"@profile\n\1\n%s" % function_call, script)
exec(script, ns, ns)
stream = cStringIO.StringIO()
prof.print_stats(stream)
s = stream.getvalue()
stream.close()
return int(re.search(r"(?m)^\s*%s\s*(\S*)" % (line_number+1), s).group(1))
if __name__ == "__main__":
print profile("binary_search.py", "binary_search(range(100), 44)", 3)
Odczytuje Źródło skryptu zawierającego funkcję do profilu, zdobi to, dołącza żądane połączenie do końca, wykonuje go, zrzuca statystyki w ciąg, wyodrębnia liczbę trafień na dany numer linii i zwraca go jako int
. Działa zgodnie z wymaganiami, ale z ważną karą wydajności.
Być może lepsze rozwiązanie polegało na upuszczeniu profilera, ale utrzymanie idei dekorowania i wykonania kodu źródłowego w locie. Edytuję moją odpowiedź, jeśli go implantuję.
W każdym razie, dziękuję Jasonowi, aby zapewnić mi wyjście!
Podobne pytania
Nowe pytania
python
Python to wielozadaniowy, wielozadaniowy język programowania dynamicznie typowany. Został zaprojektowany tak, aby był szybki do nauczenia się, zrozumienia i użycia oraz wymuszania czystej i jednolitej składni. Należy pamiętać, że Python 2 oficjalnie nie jest obsługiwany od 01-01-2020. Mimo to, w przypadku pytań Pythona specyficznych dla wersji, dodaj znacznik [python-2.7] lub [python-3.x]. Korzystając z wariantu Pythona (np. Jython, PyPy) lub biblioteki (np. Pandas i NumPy), należy umieścić go w tagach.