webentwicklung-frage-antwort-db.com.de

Wenn Funktion A nur von Funktion B benötigt wird, sollte A in B definiert werden?

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?

127
nukl
>>> 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.

130
user225312

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.

44
martineau

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.)

24
CivFan

Definieren Sie im Allgemeinen keine Funktionen innerhalb von Funktionen.

Es sei denn, Sie haben einen wirklich guten Grund. Was du nicht tust.

Warum nicht?

Was ist ist ein guter Grund, Funktionen innerhalb von Funktionen zu definieren?

Wenn Sie eigentlich wollen, ist ein Dingdang Schließung.

15
CivFan

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.

10
vz0

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 .

7
miracle173

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. 

4
user1969453

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, 

  1. innere Funktion istONLYwird von äußerer Funktion verwendet. 
  2. insider-Funktion hat einen guten Namen, um ihren Zweck zu erklären, da der Code spricht.
  3. code kann von Ihren Kollegen (oder einem anderen Codeleser) nicht direkt verstanden werden.

Wenn Sie diese Technik missbrauchen, kann dies jedoch zu Problemen führen und Konstruktionsfehler implizieren. 

Nur von meinem Exp, missverstehen Sie vielleicht Ihre Frage. 

4
chao787

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()
1
colin382

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.

1
mikelikespie

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.

1
mdlp0716

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.

A) Funktionen ohne Globals verwenden

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

B) Verwenden von Funktionen mit Globalen

_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

C) Verwenden von Funktionen in einer anderen Funktion

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. 

0
Elmex80s