Einfaches Beispiel. Zwei Methoden, eine von anderen aufgerufen:
def method_a(arg):
some_data = method_b(arg)
def method_b(arg):
return some_data
In Python können wir def
in einer anderen def
deklarieren. Wenn also method_b
für method_a
erforderlich ist und nur von dort aufgerufen wird, sollte ich method_b
in method_a
angeben? so was :
def method_a(arg):
def method_b(arg):
return some_data
some_data = method_b
Oder sollte ich das vermeiden?
>>> def sum(x, y):
... def do_it():
... return x + y
... return do_it
...
>>> a = sum(1, 3)
>>> a
<function do_it at 0xb772b304>
>>> a()
4
Ist es das, wonach Sie gesucht haben? Es wird als Schließung bezeichnet.
Sie gewinnen dadurch nicht wirklich viel, tatsächlich verlangsamt es method_a
, weil es jedes Mal, wenn es aufgerufen wird, die andere Funktion definiert und neu kompiliert. Angesichts dessen ist es wahrscheinlich besser, dem Funktionsnamen nur einen Unterstrich voranzustellen, um anzuzeigen, dass es sich um eine private Methode handelt - d. H. _method_b
.
Ich nehme an, Sie könnten möchten dies tun, wenn sich die Definition der verschachtelten Funktion aus irgendeinem Grund jedes Mal geändert hat, aber dies kann auf einen Fehler in Ihrem Design hindeuten. Das heißt, es gibt ist einen gültigen Grund, dies zu tun, damit die verschachtelte Funktion Argumente verwenden kann, die an die äußere Funktion übergeben, aber nicht explizit an sie weitergegeben wurden, was manchmal beim Schreiben von Funktionsdekoratoren z Beispiel. Es ist das, was in der akzeptierten Antwort angezeigt wird, obwohl ein Dekorateur nicht definiert oder verwendet wird.
pdate:
Hier ist der Beweis, dass das Verschachteln langsamer ist (mit Python 3.6.1), obwohl dies in diesem trivialen Fall zugegebenermaßen nicht viel ist:
setup = """
class Test(object):
def separate(self, arg):
some_data = self._method_b(arg)
def _method_b(self, arg):
return arg+1
def nested(self, arg):
def method_b2(self, arg):
return arg+1
some_data = method_b2(self, arg)
obj = Test()
"""
from timeit import Timer
print(min(Timer(stmt='obj.separate(42)', setup=setup).repeat())) # -> 0.24479823284461724
print(min(Timer(stmt='obj.nested(42)', setup=setup).repeat())) # -> 0.26553459700452575
Hinweis: Ich habe einige self
Argumente zu Ihren Beispielfunktionen hinzugefügt, um sie den realen Methoden ähnlicher zu machen (obwohl method_b2
technisch immer noch keine Methode der Klasse Test
ist). Auch die verschachtelte Funktion wird in dieser Version tatsächlich aufgerufen, im Gegensatz zu Ihrer.
Eine Funktion innerhalb einer Funktion wird üblicherweise für Verschlüsse verwendet.
(Es gibt eine viel Streit über was genau macht eine Schließung zu einer Schließung .)
Hier ist ein Beispiel mit dem eingebauten sum()
. Es definiert einmal start
und verwendet es von da an:
def sum_partial(start):
def sum_start(iterable):
return sum(iterable, start)
return sum_start
In Benutzung:
>>> sum_with_1 = sum_partial(1)
>>> sum_with_3 = sum_partial(3)
>>>
>>> sum_with_1
<function sum_start at 0x7f3726e70b90>
>>> sum_with_3
<function sum_start at 0x7f3726e70c08>
>>> sum_with_1((1,2,3))
7
>>> sum_with_3((1,2,3))
9
Integrierte Python-Schließung
functools.partial
ist ein Beispiel für eine Schließung.
Aus den Python-Dokumenten entspricht es ungefähr:
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
(Ein Kudos an @ user225312 unten für die Antwort. Ich finde dieses Beispiel einfacher herauszufinden, und hoffentlich hilft es, den Kommentar von @ mango zu beantworten.)
Definieren Sie im Allgemeinen keine Funktionen innerhalb von Funktionen.
Es sei denn, Sie haben einen wirklich guten Grund. Was du nicht tust.
Warum nicht?
lambda
-Ausdruck verwenden .Was ist ist ein guter Grund, Funktionen innerhalb von Funktionen zu definieren?
Wenn Sie eigentlich wollen, ist ein Dingdang Schließung.
Es ist in Ordnung, eine Funktion in einer anderen zu deklarieren. Dies ist besonders nützlich beim Erstellen von Dekorateuren.
Als Faustregel gilt: Wenn die Funktion komplex ist (mehr als 10 Zeilen), empfiehlt es sich, sie auf Modulebene zu deklarieren.
Ich habe diese Frage gefunden, weil ich eine Frage stellen wollte, warum es Auswirkungen auf die Leistung gibt, wenn verschachtelte Funktionen verwendet werden. Ich habe Tests für die folgenden Funktionen mit Python 3.2.5 auf einem Windows Notebook mit einem Intel Core i5-2530M Quad Core 2,5 GHz-Prozessor durchgeführt
def square0(x):
return x*x
def square1(x):
def dummy(y):
return y*y
return x*x
def square2(x):
def dummy1(y):
return y*y
def dummy2(y):
return y*y
return x*x
def square5(x):
def dummy1(y):
return y*y
def dummy2(y):
return y*y
def dummy3(y):
return y*y
def dummy4(y):
return y*y
def dummy5(y):
return y*y
return x*x
Die folgenden 20 Messungen habe ich auch für square1, square2 und square5 gemessen:
s=0
for i in range(10**6):
s+=square0(i)
und erhielt die folgenden Ergebnisse
>>>
m = mean, s = standard deviation, m0 = mean of first testcase
[m-3s,m+3s] is a 0.997 confidence interval if normal distributed
square? m s m/m0 [m-3s ,m+3s ]
square0 0.387 0.01515 1.000 [0.342,0.433]
square1 0.460 0.01422 1.188 [0.417,0.503]
square2 0.552 0.01803 1.425 [0.498,0.606]
square5 0.766 0.01654 1.979 [0.717,0.816]
>>>
square0
hat keine verschachtelten Funktionen, square1
hat eine verschachtelte Funktion, square2
hat zwei verschachtelte Funktionen und square5
hat fünf verschachtelte Funktionen. Die verschachtelten Funktionen werden nur deklariert, aber nicht aufgerufen.
Wenn Sie also 5 verschachtelte Funktionen in einer Funktion definiert haben, die Sie nicht aufrufen, ist die Ausführungszeit der Funktion doppelt so groß wie die Funktion ohne verschachtelte Funktion. Ich denke, sollte mit verschachtelten Funktionen vorsichtig sein.
Die Python-Datei für den gesamten Test, der diese Ausgabe generiert, befindet sich unter ideone .
Am Ende geht es also weitgehend darum, wie intelligent die Python-Implementierung ist oder nicht ist, insbesondere wenn die innere Funktion keine Schließung ist, sondern lediglich eine in Funktion benötigte Hilfe.
Ein sauberes, verständliches Design, das nur dort Funktionen hat, wo sie benötigt werden und an anderer Stelle nicht sichtbar sind, ist ein gutes Design, unabhängig davon, ob sie in ein Modul, eine Klasse als Methode oder in eine andere Funktion oder Methode eingebettet sind. Wenn sie gut gemacht werden, verbessern sie wirklich die Klarheit des Codes.
Und wenn die innere Funktion eine Schließung ist, kann dies auch ein wenig zur Klarheit beitragen, auch wenn diese Funktion nicht für andere Zwecke aus der übergeordneten Funktion herausgegeben wird.
Ich würde also sagen, dass Sie sie im Allgemeinen verwenden sollten. Beachten Sie jedoch die möglichen Leistungseinbußen, wenn Sie sich tatsächlich Gedanken über die Leistung machen, und entfernen Sie sie nur dann, wenn Sie tatsächlich ein Profil erstellen, bei dem sie am besten entfernt werden.
Führen Sie keine vorzeitige Optimierung der Verwendung von "inneren Funktionen BAD" für den gesamten Python-Code durch, den Sie schreiben. Bitte.
Es ist nur ein Prinzip über Expositions-APIs.
Die Verwendung von Python ist eine gute Idee, um die Expositions-API im Weltraum (Modul oder Klasse) zu vermeiden.
Das könnte eine gute Idee sein. wenn Sie sicherstellen,
Wenn Sie diese Technik missbrauchen, kann dies jedoch zu Problemen führen und Konstruktionsfehler implizieren.
Nur von meinem Exp, missverstehen Sie vielleicht Ihre Frage.
die Antwort von mdlp hat bei mir nicht funktioniert.
Dies tat:
def some_function():
return some_other_function()
def some_other_function():
return 42
print some_function()
Es ist absolut in Ordnung, dies auf diese Weise zu tun, aber es sei denn, Sie müssen eine Schließung verwenden oder die Funktion zurückgeben, würde ich wahrscheinlich auf die Modulebene setzen. Ich stelle mir im zweiten Codebeispiel vor, dass Sie meinen:
...
some_data = method_b() # not some_data = method_b
andernfalls ist some_data die Funktion.
Wenn Sie es auf Modulebene haben, können andere Funktionen method_b () verwenden. Wenn Sie etwas wie Sphinx (und autodoc) für die Dokumentation verwenden, können Sie auch method_b dokumentieren.
Sie können auch erwägen, die Funktionalität einfach in zwei Methoden in einer Klasse zu platzieren, wenn Sie etwas tun, das von einem Objekt dargestellt werden kann. Dies beinhaltet auch Logik, wenn Sie nach allem suchen.
Tun Sie etwas wie:
def some_function():
some_other_function()
def some_other_function():
return 42
wenn Sie some_function()
ausführen würden, würde es some_other_function()
ausführen und 42 zurückgeben.
EDIT: Ich habe ursprünglich gesagt, dass Sie keine Funktion innerhalb einer anderen definieren sollten, aber es wurde darauf hingewiesen, dass es praktisch ist, dies manchmal zu tun.
Sie können es verwenden, um die Definition globaler Variablen zu vermeiden. Dies gibt Ihnen eine Alternative für andere Designs. 3 Designs, die eine Lösung für ein Problem darstellen.
def calculate_salary(employee, list_with_all_employees):
x = _calculate_tax(list_with_all_employees)
# some other calculations done to x
pass
y = # something
return y
def _calculate_tax(list_with_all_employees):
return 1.23456 # return something
_list_with_all_employees = None
def calculate_salary(employee, list_with_all_employees):
global _list_with_all_employees
_list_with_all_employees = list_with_all_employees
x = _calculate_tax()
# some other calculations done to x
pass
y = # something
return y
def _calculate_tax():
return 1.23456 # return something based on the _list_with_all_employees var
def calculate_salary(employee, list_with_all_employees):
def _calculate_tax():
return 1.23456 # return something based on the list_with_a--Lemployees var
x = _calculate_tax()
# some other calculations done to x
pass
y = # something
return y
Lösung C) erlaubt die Verwendung von Variablen im Bereich der äußeren Funktion, ohne sie in der inneren Funktion deklarieren zu müssen. Kann in manchen Situationen nützlich sein.