proposal - python package naming conventions
Si la función A es requerida solo por la función B, ¿debería definirse A dentro de B? (11)
Así que, al final, se trata en gran medida de cuán inteligente es o no la implementación de python, particularmente en el caso de que la función interna no sea un cierre sino simplemente una función que solo se necesita como ayuda.
En un diseño limpio y comprensible que tenga funciones solo donde se necesiten y no estén expuestas en otra parte, es un buen diseño, ya sea que estén integradas en un módulo, una clase como método o dentro de otra función o método. Cuando se hace bien, realmente mejoran la claridad del código.
Y cuando la función interna es un cierre que también puede ayudar bastante con la claridad, incluso si esa función no se devuelve de la función que lo contiene para su uso en otro lugar.
Así que, en general, les diría que sí utilícenlos, pero estén al tanto de los posibles problemas de rendimiento cuando en realidad les preocupa el rendimiento y solo elimínenlos si realiza un perfil real que muestre que es mejor eliminarlos.
No haga una optimización prematura de simplemente usar "funciones internas MALAS" en todo el código de Python que escriba. Por favor.
Ejemplo simple Dos métodos, uno llamado de otro:
def method_a(arg):
some_data = method_b(arg)
def method_b(arg):
return some_data
En Python podemos declarar def
dentro de otra def
. Entonces, si se requiere method_b
y se llama solo desde method_a
, ¿debo declarar method_b
dentro de method_a
? Me gusta esto :
def method_a(arg):
def method_b(arg):
return some_data
some_data = method_b
¿O debería evitar hacer esto?
De hecho, está bien declarar una función dentro de otra. Esto es especialmente útil para crear decoradores.
Sin embargo, como regla general, si la función es compleja (más de 10 líneas), podría ser una mejor idea declararla en el nivel del módulo.
Encontré esta pregunta porque quería plantear una pregunta sobre por qué hay un impacto en el rendimiento si uno usa funciones anidadas. Ejecuté pruebas para las siguientes funciones usando Python 3.2.5 en una computadora portátil Windows con un procesador Intel Core i5-2530M Quad Core 2.5 GHz
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
Medí las siguientes 20 veces, también para square1, square2 y square5:
s=0
for i in range(10**6):
s+=square0(i)
y obtuve los siguientes resultados
>>>
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
no tiene función anidada, square1
tiene una función anidada, square2
tiene dos funciones anidadas y square5
tiene cinco funciones anidadas. Las funciones anidadas solo se declaran pero no se llaman.
Por lo tanto, si ha definido 5 funciones anidadas en una función a la que no llama, el tiempo de ejecución de la función es el doble de la función sin una función anidada. Creo que debería ser prudente al usar funciones anidadas.
El archivo Python para la prueba completa que genera este resultado se puede encontrar en ideone .
Es solo un principio sobre las API de exposición.
Usando python, es una buena idea evitar la API de exposición en el espacio exterior (módulo o clase), la función es un buen lugar de encapsulación.
Podría ser una buena idea. cuando te aseguras
- la función interna SOLO es utilizada por la función externa.
- La función de información privilegiada tiene un buen nombre para explicar su propósito porque el código habla.
- el código no puede ser comprendido directamente por sus colegas (u otro lector de códigos).
Aunque, el abuso de esta técnica puede causar problemas e implica un defecto de diseño.
Solo a partir de mi exp, Quizás malinterprete tu pregunta.
Está perfectamente bien hacerlo de esa manera, pero a menos que necesites usar un cierre o devolver la función probablemente lo pondría en el nivel del módulo. Me imagino que en el segundo ejemplo de código quieres decir:
...
some_data = method_b() # not some_data = method_b
de lo contrario, some_data será la función.
Tenerlo en el nivel de módulo permitirá que otras funciones usen method_b () y si está utilizando algo como Sphinx (y autodoc) para documentación, también le permitirá documentar method_b.
También puede considerar simplemente poner la funcionalidad en dos métodos en una clase si está haciendo algo que pueda ser representable por un objeto. Esto también contiene lógica si eso es todo lo que estás buscando.
Generalmente, no, no define funciones dentro de funciones.
A menos que tengas una buena razón. Que no.
Por qué no?
- Impide ganchos fáciles para pruebas unitarias. Estás en pruebas unitarias, ¿verdad?
- En realidad, no lo ofusca por completo de todos modos , es más seguro suponer que nunca habrá nada en Python.
- Utilice las pautas de estilo de código automáticas estándar de Python para encapsular métodos en su lugar.
- Recrear innecesariamente un objeto de función para el código idéntico cada vez que ejecute la función externa .
- Si su función es realmente así de simple, debería usar una expresión
lambda
su lugar .
¿Cuál es realmente una buena razón para definir funciones dentro de las funciones?
Cuando lo que realmente quieres es un closure dingdang.
Haz algo como:
def some_function():
some_other_function()
def some_other_function():
return 42
si tuviera que ejecutar some_function()
, entonces ejecutaría some_other_function()
y devolvería 42.
EDITAR: Originalmente dije que no deberías definir una función dentro de otra, pero se señaló que a veces es práctico hacerlo.
La respuesta de mdlp no funcionó para mí.
Esto hizo:
def some_function():
return some_other_function()
def some_other_function():
return 42
print some_function()
Realmente no ganas mucho haciendo esto, de hecho ralentiza a method_a
porque definirá y recompilará la otra función cada vez que se llame. Teniendo en cuenta que, probablemente sería mejor simplemente prefijar el nombre de la función con guión bajo para indicar que es un método privado, es decir, _method_b
.
Supongo que es posible que desee hacer esto si la definición de la función anidada varía cada vez por alguna razón, pero eso puede indicar un defecto en su diseño. Dicho esto, hay una razón válida para hacer esto para permitir que la función anidada use argumentos que fueron pasados a la función externa pero no explícitamente pasados a ellos, lo que a veces ocurre al escribir decoradores de funciones, por ejemplo. Es lo que se muestra en la respuesta aceptada, aunque no se está definiendo ni utilizando un decorador.
Actualizar:
Aquí hay una prueba de que anidarlos es más lento (usando Python 3.6.1), aunque no por mucho en este caso trivial:
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
Nota method_b2
algunos argumentos method_b2
a sus funciones de muestra para hacer que se method_b2
más a métodos reales (aunque method_b2
aún no es técnicamente un método de la clase Test
). También la función anidada se llama realmente en esa versión, a diferencia del tuyo.
Una función dentro de una función se usa comúnmente para closures .
(Hay mucha controversia sobre qué es exactamente lo que hace que un cierre sea un cierre ).
Aquí hay un ejemplo usando la sum()
incorporada sum()
. Define start
una vez y lo usa a partir de ese momento:
def sum_partial(start):
def sum_start(iterable):
return sum(iterable, start)
return sum_start
En uso:
>>> 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
Cierre de pitón incorporado
functools.partial
es un ejemplo de un cierre.
De los documentos de Python , es más o menos equivalente a:
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
(Felicitaciones a @ user225312 a continuación por la respuesta. Encuentro que este ejemplo es más fácil de descifrar y espero que ayude a responder el comentario de @ mango).
>>> 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
Esto es lo que estabas buscando? Se llama closure .