Funciones personalizadas en Python

Las funciones son una parte integral de los programas de Python. De hecho, en el estudio anterior, hemos utilizado muchas funciones integradas de Python, como sorted () para ordenar una secuencia de colección, len () para devolver la longitud de una secuencia de colección, y así sucesivamente.

Base de la función

Para decirlo sin rodeos, una función es un segmento de código para lograr una determinada función, siempre que esté escrito, se puede reutilizar. Primero veamos el siguiente ejemplo simple:


def my_func(message):
    print('Got a message: {}'.format(message))

# 调用函数 my_func()
my_func('Hello World')
# 输出
Got a message: Hello World

entre ellos:

  • def es la declaración de la función;
  • my_func es el nombre de la función;
  • El mensaje entre paréntesis es el parámetro de la función;
  • La línea de impresión es la parte principal de la función y se puede ejecutar la instrucción correspondiente;
  • Al final de la función, puede devolver el resultado de la llamada (retorno o rendimiento) o no.

En resumen, probablemente sea la siguiente forma:


def name(param1, param2, ..., paramN):
    statements
    return/yield value # optional

A diferencia de otros lenguajes que necesitan compilarse (como el lenguaje C), def es una declaración ejecutable, lo que significa que la función no existe hasta que se llama. Cuando el programa llama a una función, la declaración def creará un nuevo objeto de función y le dará un nombre.

Echemos un vistazo a algunos ejemplos para profundizar su impresión de las funciones:


def my_sum(a, b):
    return a + b

result = my_sum(3, 5)
print(result)

# 输出
8

Aquí, definimos la función my_sum (), que tiene dos parámetros ayb, que se usan para sumar; luego, llamamos a la función my_sum () para asignar 3 y 5 a ayb respectivamente; finalmente, devuelve la suma El valor de se asigna al resultado de la variable, y la salida es 8.

Veamos otro ejemplo:


def find_largest_element(l):
    if not isinstance(l, list):
        print('input is not type of list')
        return
    if len(l) == 0:
        print('empty input')
        return
    largest_element = l[0]
    for item in l:
        if item > largest_element:
            largest_element = item
    print('largest element is: {}'.format(largest_element)) 
      
find_largest_element([8, 1,-3, 2, 0])

# 输出
largest element is: 8

En este ejemplo, definimos la función find_largest_element, la función es recorrer la lista de entrada, encontrar el valor más grande e imprimirlo. Por lo tanto, cuando lo llamamos y pasamos la lista [8, 1, -3, 2, 0] como parámetro, el programa generará el elemento más grande: 8.

Cabe señalar que cuando el programa principal llama a una función, se debe asegurar que la función haya sido definida anteriormente, de lo contrario se reportará un error, como por ejemplo:


my_func('hello world')
def my_func(message):
    print('Got a message: {}'.format(message))
    
# 输出
NameError: name 'my_func' is not defined

Sin embargo, si llamamos a otras funciones dentro de la función, no importa qué declaración viene primero y cuál viene después de la función, porque def es una declaración ejecutable y la función no existe antes de la llamada. Solo necesitamos asegurarnos de que la función requerida se llama cuando se realiza la llamada. Tener definiciones declaradas:


def my_func(message):
    my_sub_func(message) # 调用my_sub_func()在其声明之前不影响程序执行
    
def my_sub_func(message):
    print('Got a message: {}'.format(message))

my_func('hello world')

# 输出
Got a message: hello world

Además, los parámetros de la función Python se pueden establecer en valores predeterminados, como la siguiente escritura:


def func(param = 0):
    ...

De esta manera, cuando se llama a la función func (), si no se pasa el parámetro param, el parámetro predeterminado es 0; y si se pasa el parámetro param, anulará el valor predeterminado.

Como se mencionó anteriormente, una de las principales características de Python en comparación con otros lenguajes es que Python se escribe dinámicamente y puede aceptar cualquier tipo de datos (entero, punto flotante, cadena, etc.). Lo mismo se aplica a los parámetros de función. Por ejemplo, la función my_sum hace un momento, también podemos pasar la lista como parámetro, lo que significa que las dos listas están conectadas:


print(my_sum([1, 2], [3, 4]))

# 输出
[1, 2, 3, 4]

Del mismo modo, también puede pasar una cadena como parámetro para indicar la combinación y el empalme de la cadena:


print(my_sum('hello ', 'world'))

# 输出
hello world

Por supuesto, si los tipos de datos de los dos parámetros son diferentes, por ejemplo, uno es una lista y el otro es una cadena, y los dos no se pueden agregar, se informará un error:


print(my_sum([1, 2], 'hello'))
TypeError: can only concatenate list (not "str") to list

Podemos ver que Python no necesita considerar el tipo de datos de entrada, sino que lo entrega al código específico para juzgarlo y ejecutarlo. La misma función (como la función de suma my_sum () aquí) se puede aplicar a los enteros, en la operación de listas, cadenas, etc.

En los lenguajes de programación, a este comportamiento lo llamamos polimorfismo. Esta también es una gran diferencia entre Python y otros lenguajes, como Java y C. Por supuesto, esta conveniente función de Python también traerá muchos problemas en el uso real. Por lo tanto, si es necesario, agregue la verificación del tipo de datos al principio.

Otra característica importante de las funciones de Python es que Python admite el anidamiento de funciones. El llamado anidamiento de funciones significa que hay funciones dentro de la función, tales como:


def f1():
    print('hello')
    def f2():
        print('world')
    f2()
f1()

# 输出
hello
world

Aquí, dentro de la función f1 (), se define la función f2 (). Cuando se llama a la función f1 (), la cadena 'hola' se imprimirá primero, y luego se llamará a f2 () dentro de f1 () para imprimir la cadena 'mundo'. Puede preguntar, ¿por qué necesitamos el anidamiento de funciones? ¿Cuáles son los beneficios de hacer esto?

De hecho, el anidamiento de funciones tiene principalmente los dos aspectos siguientes.

Primero, el anidamiento de funciones puede garantizar la privacidad de las funciones internas. Las funciones internas solo pueden ser llamadas y accedidas por funciones externas, y no estarán expuestas al alcance global. Por lo tanto, si tiene algunos datos privados (como usuarios de bases de datos, contraseñas, etc.) dentro de su función que no desea para ser expuesto, entonces puede usar El anidamiento de la función lo encapsula en la función interna y solo se accede a través de la función externa. tal como:


def connect_DB():
    def get_DB_configuration():
        ...
        return host, username, password
    conn = connector.connect(get_DB_configuration())
    return conn

La función get_DB_configuration aquí es una función interna y no se puede llamar por separado fuera de la función connect_DB (). En otras palabras, las siguientes llamadas directas externas son incorrectas:


get_DB_configuration()

# 输出
NameError: name 'get_DB_configuration' is not defined

Solo podemos acceder a él llamando a la función externa connect_DB (), de esta forma se ha mejorado mucho la seguridad del programa.

En segundo lugar, el uso razonable del anidamiento de funciones puede mejorar la eficiencia del programa. Veamos el siguiente ejemplo:


def factorial(input):
    # validation check
    if not isinstance(input, int):
        raise Exception('input must be an integer.')
    if input < 0:
        raise Exception('input must be greater or equal to 0' )
    ...

    def inner_factorial(input):
        if input <= 1:
            return 1
        return input * inner_factorial(input-1)
    return inner_factorial(input)


print(factorial(5))

Aquí, usamos un método recursivo para calcular el factorial de un número. Debido a que es necesario verificar si la entrada es válida antes del cálculo, la escribí en forma de funciones anidadas, de modo que la entrada sea válida solo una vez. Y si no usamos el anidamiento de funciones, se verificará cada vez que se llame a la recursividad, esto es innecesario y reducirá la eficiencia del programa.

En el trabajo real, si se encuentra con una situación similar, la verificación de entrada no es muy rápida y consumirá una cierta cantidad de recursos, entonces es muy necesario usar el anidamiento de funciones.

Alcance de la variable de función

El alcance de las variables en las funciones de Python es similar al de otros lenguajes. Si la variable está definida dentro de la función, se llama variable local y solo es válida dentro de la función. Una vez que se ejecuta la función, las variables locales se reciclarán y no se podrá acceder, como en el siguiente ejemplo:


def read_text_from_file(file_path):
    with open(file_path) as file:
        ...

Definimos el archivo de variables dentro de la función, esta variable solo es válida en la función read_text_from_file, y no se puede acceder fuera de la función.

En consecuencia, las variables globales se definen en todo el nivel de archivo, como el siguiente código:


MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    if value < MIN_VALUE or value > MAX_VALUE:
        raise Exception('validation check fails')

El MIN_VALUE y MAX_VALUE aquí son variables globales, a las que se puede acceder en cualquier parte del archivo y, por supuesto, también es posible dentro de la función. Sin embargo, no podemos cambiar arbitrariamente el valor de las variables globales dentro de la función. Por ejemplo, lo siguiente es incorrecto:


MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    ...
    MIN_VALUE += 1
    ...
validation_check(5)

Si ejecuta este código, el programa informará un error:


UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment

Esto se debe a que el intérprete de Python predetermina las variables dentro de la función como variables locales, pero también encuentra que la variable local MIN_VALUE no está declarada, por lo que no puede realizar operaciones relacionadas. Por lo tanto, si debemos cambiar el valor de las variables globales dentro de la función, debemos agregar la declaración global:


MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    global MIN_VALUE
    ...
    MIN_VALUE += 1
    ...
validation_check(5)

La palabra clave global aquí no significa que se recrea una variable global MIN_VALUE, pero le dice al intérprete de Python que la variable MIN_VALUE dentro de la función es la variable global previamente definida, no una nueva variable global, ni una variable local. De esta forma, el programa puede acceder a la variable global dentro de la función y modificar su valor.

Además, si encuentra una situación en la que la variable local y la variable global dentro de la función tienen el mismo nombre, entonces la variable local cubrirá la variable global dentro de la función, como la siguiente:


MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    MIN_VALUE = 3
    ...

Dentro de la función validation_check (), definimos la variable local MIN_VALUE con el mismo nombre que la variable global, luego, el valor de MIN_VALUE dentro de la función debe ser 3 en lugar de 1.

De manera similar, para las funciones anidadas, la función interna puede acceder a las variables definidas por la función externa, pero no se puede modificar. Si desea modificarla, debe agregar la palabra clave nonlocal:


def outer():
    x = "local"
    def inner():
        nonlocal x # nonlocal关键字表示这里的x就是外部函数outer定义的变量x
        x = 'nonlocal'
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
# 输出
inner: nonlocal
outer: nonlocal

Si no se agrega la palabra clave nonlocal, y la variable de la función interna tiene el mismo nombre que la variable de función externa, entonces la variable de función interna cubrirá la variable de función externa de la misma manera.


def outer():
    x = "local"
    def inner():
        x = 'nonlocal' # 这里的x是inner这个函数的局部变量
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
# 输出
inner: nonlocal
outer: local

Cierre

Los cierres son en realidad similares a las funciones anidadas que acabamos de mencionar, la diferencia es que la función externa aquí devuelve una función en lugar de un valor específico. La función devuelta generalmente se asigna a una variable, que se puede ejecutar más tarde.

Por ejemplo, si queremos calcular la n-ésima potencia de un número, podemos escribir el siguiente código con un cierre:


def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of # 返回值是exponent_of函数

square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方 
square
# 输出
<function __main__.nth_power.<locals>.exponent(base)>

cube
# 输出
<function __main__.nth_power.<locals>.exponent(base)>

print(square(2))  # 计算2的平方
print(cube(2)) # 计算2的立方
# 输出
4 # 2^2
8 # 2^3

Aquí, el valor de retorno de la función externa nth_power () es la función exponent_of (), no un valor específico. Cabe señalar que después de ejecutar square = nth_power (2) y cube = nth_power (3), el parámetro exponente de la función externa nth_power () seguirá siendo recordado por la función interna exponent_of (). De esta manera, cuando llamamos al cuadrado (2) o al cubo (2) más tarde, el programa puede generar el resultado sin problemas sin informar un error que diga que el exponente del parámetro no está definido.

¿Por qué deberíamos cerrarlo? El programa anterior, también puedo escribir el siguiente formulario!


def nth_power_rewrite(base, exponent):
    return base ** exponent

Sí, pero, ya sabes, una de las razones para usar cierres es hacer que el programa sea más conciso y legible. Imagina, por ejemplo, que necesitas calcular el cuadrado de muchos números. ¿Cuál de las siguientes formas crees que es mejor?


# 不适用闭包
res1 = nth_power_rewrite(base1, 2)
res2 = nth_power_rewrite(base2, 2)
res3 = nth_power_rewrite(base3, 2)
...

# 使用闭包
square = nth_power(2)
res1 = square(base1)
res2 = square(base2)
res3 = square(base3)
...

En primer lugar, intuitivamente, la segunda forma le permite ingresar un parámetro menos cada vez que llama a la función, y la expresión es más concisa.

En segundo lugar, de manera similar a las ventajas de las funciones anidadas mencionadas anteriormente, es necesario realizar un trabajo adicional al comienzo de la función, y cuando necesite llamar a esta función varias veces, coloque el código de trabajo adicional en la función externa, lo que puede reducir el número de llamadas provocadas La sobrecarga innecesaria, mejora la eficiencia operativa del programa.

Otro punto, del que hablaremos más adelante, los cierres se suelen utilizar con los decoradores.

Supongo que te gusta

Origin blog.csdn.net/qq_41485273/article/details/114097755
Recomendado
Clasificación