Categorías
Python

Punteros en Python: ¿Cuál es el punto?

 

Tabla de Contenidos

  • ¿Por qué no Python tienen Punteros?
  • Objetos en Python
  • Inmutable vs mutable Objetos
  • Comprender VariablesVariables en CNAMES en PythonA Nota sobre Intern Objetos de variables de Python
  • en C
  • nombres en Python
  • Una nota sobre Intern Objetos en Python
  • Simulación de punteros en PythonUsing Tipos mutables como PointersUsing Python Objetos
  • Uso de tipos mutables como punteros
  • el uso de objetos de Python
  • real Punteros Con ctypes variables
  • Conclusión
  • en C
  • nombres en Python
  • Una nota sobre Intern Objetos en Python
  • Uso de tipos mutables como punteros
  • usando Python Objetos

Si alguna vez has trabajado con lenguajes de bajo nivel como C o C ++, entonces usted probablemente ha oído hablar de los punteros. Punteros le permiten crear una gran eficiencia en partes de su código. También son causa de confusión para los principiantes y pueden dar lugar a diversos errores de gestión de memoria, incluso para los expertos. Entonces, ¿dónde están ellos en Python, y cómo se puede simular punteros en Python?

Punteros son ampliamente utilizados en C y C ++. Esencialmente, son variables que contienen la dirección de memoria de otra variable. Para un repaso de los punteros, es posible considerar visitar esta visión general sobre los punteros de C.

En este artículo, obtendrá una mejor comprensión del modelo de objetos de Python y aprender qué punteros en Python no existen realmente. Para los casos en que es necesario imitar el comportamiento puntero, aprenderá formas para simular punteros en Python sin la pesadilla de administración de memoria.

En este artículo, usted:

  • Sepa por qué no existen punteros en Python
  • Explorar la diferencia entre las variables de C y Python nombres punteros
  • Simular en Python
  • Experimento con punteros reales utilizando ctypes

Nota: en este artículo, “Python” se referirá a la implementación de referencia de Python en C, también conocida como CPython. A medida que el artículo se describen algunos detalles internos de la lengua, estas notas son verdaderas para CPython 3.7, pero pueden no ser cierto en el futuro o en el pasado iteraciones del lenguaje. Bono

gratuito: Haga clic aquí para obtener acceso a un capítulo de trucos Python: El libro que te muestra las mejores prácticas de Python con ejemplos sencillos puede aplicar instantáneamente a escribir código más bonito + Pythonic.

¿Por qué no Python tienen punteros?

La verdad es que no sé. Podrían existir punteros en Python de forma nativa? Probablemente, pero punteros parecen ir en contra del Zen de Python. Punteros alientan cambios más implícito que explícito. A menudo, son complejos en lugar de simple, especialmente para los principiantes. Lo que es peor, que piden formas de fotografiar en el pie, o hacer algo realmente peligroso como leer desde una sección de memoria que no se suponía que.

Python tiende a tratar de resumen de los detalles de implementación de distancia como las direcciones de memoria de sus usuarios. Python menudo se centra en la facilidad de uso en lugar de la velocidad. Como resultado, los punteros en Python no tiene mucho sentido. No temer, sin embargo, no Python, por defecto, se dan algunos de los beneficios del uso de punteros.

punteros entendimiento en Python requiere un pequeño desvío en detalles de implementación de Python. En concreto, se tendrá que entender:

conservar su direcciones de memoria, y que vamos a empezar.

Objetos en Python

En Python, todo es un objeto. Para la prueba, se puede abrir un REPL y explorar el uso de isinstance ():

>>> isinstance(1, object)
True
>>> isinstance(list(), object)
True
>>> isinstance(True, object)
True
>>> def foo():
... pass
...
>>> isinstance(foo, object)
True

Este código muestra que todo en Python que es de hecho un objeto. Cada objeto contiene al menos tres datos: Conteo

  • Referencia
  • Tipo
  • Valor

El recuento de referencia es para la gestión de memoria. Para una mirada en profundidad a las partes internas de gestión de memoria en Python, se puede leer Gestión de memoria en Python.

El tipo se utiliza en la capa de CPython para garantizar la seguridad de tipos en tiempo de ejecución. Por último, está el valor, que es el valor actual asociado con el objeto.

No todos los objetos son los mismos aunque. Hay otra diferencia importante que necesita para comprender: inmutable vs objetos mutables. Entender la diferencia entre los tipos de objetos que realmente ayuda a clarificar la primera capa de la cebolla que es punteros en Python.

Inmutable vs mutable Objetos

En Python, hay dos tipos de objetos:

La comprensión de esta diferencia es la primera clave para la navegación por el paisaje de punteros en Python. He aquí un desglose de los tipos comunes y si son o no son mutables o inmutables:

Como se puede ver, un montón de tipos primitivos de uso común son inmutables. Puede probar esto por sí mismo escribiendo algunos Python. Usted necesitará un par de herramientas de la biblioteca estándar de Python:

Una vez más, puede utilizar estos en un entorno REPL:

>>> x = 5
>>> id(x)
94529957049376

En el código anterior, se ha asignado el valor 5 a x. Si se trató de modificar este valor con la adición, a continuación, se obtendría un nuevo objeto:

>>> x += 1
>>> x
6
>>> id(x)
94529957049408

A pesar de que el código anterior parece modificar el valor de x, que está recibiendo un objeto nueva como respuesta.

La str tipo también es inmutable:

>>> s = "real_python"
>>> id(s)
140637819584048
>>> s += "_rocks"
>>> s
'real_python_rocks'
>>> id(s)
140637819609424

Una vez más, s termina con una diferentes direcciones de memoria después de la + = operación.

Bono: El operador + = traduce en diversas llamadas a métodos.

Para algunos objetos como lista, + = se traducirá en __iadd __ ((add in-place)). Esto modificará auto y regresar el mismo ID. Sin embargo, str int y no tienen estos métodos y dan lugar a __add __ () llama en lugar de __iadd __ ().

Para obtener más información detallada, disfrutar de los Python documentos de modelo de datos.

Tratando de mutar directamente los resultados de la cadena s en un error:

>>> s[0] = "R"
Traceback (most recent call last):
File "", line 1, in
TypeError: 'str' object does not support item assignment

El código anterior falla, y Python indica que str no soporta esta mutación, que está en consonancia con la definición que el tipo str es inmutable.

contraste que con un objeto mutable, como lista:

>>> my_list = [1, 2, 3]
>>> id(my_list)
140637819575368
>>> my_list.append(4)
>>> my_list
[1, 2, 3, 4]
>>> id(my_list)
140637819575368

Este código muestra una gran diferencia en los dos tipos de objetos. my_list tiene un ID originalmente. Incluso después de 4 se adjunta a la lista, my_list tiene el mismo id . Esto se debe a que el tipo de lista es mutable.

Otra manera de demostrar que la lista es mutable es con asignación:

>>> my_list[0] = 0
>>> my_list
[0, 2, 3, 4]
>>> id(my_list)
140637819575368

En este código, my_list mutar y establece su primer elemento a 0. Sin embargo, se mantiene el mismo id, incluso después de esta asignación. Con los objetos mutables e inmutables fuera del camino, el siguiente paso en su viaje a la iluminación es Python ecosistema variable de la comprensión de Python. las variables

Comprensión variables

Python son fundamentalmente diferentes de variables en C o C ++. De hecho, Python no tiene ni siquiera variables. Python tiene nombres, no variables.

Esto puede parecer pedante, y en su mayor parte, lo es. La mayoría de las veces, es perfectamente aceptable para pensar acerca de los nombres de Python como variables, pero la comprensión de la diferencia es importante. Esto es especialmente cierto cuando se está navegando el tema delicado de punteros en Python.

Para el hogar ayudan a impulsar la diferencia, se puede echar un vistazo a cómo las variables funcionan en C, lo que representan, y luego contraste con la forma en que funcionan los nombres en Python. Variables

en ejemplo de C

permiten tenían el siguiente código que define la variable x:

int x = 2337;

Este una línea de código tiene varios pasos, distintas cuando se ejecuta:

muestra en una vista simplificada de la memoria, puede ser que parezca esto:

Aquí, se puede ver que la variable x tiene una posición de memoria falsa de 0x7f1 y el valor 2337. Si, más adelante en el programa, que desea cambiar el valor de x, se puede hacer lo siguiente:

x = 2338;

lo anterior cesionarios de código de un nuevo valor (2338) a la variable x, por lo tanto sobrescribir el valor anterior. Esto significa que la variable x es mutable . La actualización de memoria de diseño muestra el nuevo valor:

en cuenta que la ubicación de x no ha cambiado, sólo el valor en sí. Este es un punto importante. Esto significa que x es la posición de memoria , no sólo un nombre para él.

Otra forma de pensar de este concepto es en términos de propiedad. En un sentido, x es dueño de la posición de memoria. x es, al principio, una caja vacía que puede caber exactamente un número entero en el que se pueden almacenar valores enteros.

Cuando se asigna un valor de x, va a colocar un valor en el cuadro que x es propietaria. Si quería introducir una nueva variable (y), puede agregar esta línea de código:

int y = x;

Este código crea una caja llamada nueva Y y copia el valor de x en la caja. Ahora el diseño de memoria se verá así:

Aviso 0x7f5 la nueva ubicación de y. A pesar de que el valor de x se copió y, la variable y es dueño de alguna nueva dirección en la memoria. Por lo tanto, se puede sobrescribir el valor de y sin afectar x:

y = 2339;

Ahora el diseño de memoria se verá así:

vez más, se ha modificado el valor de y, pero no su ubicación. Además, usted tiene no afectó la variable original x en absoluto. Esto está en marcado contraste con la forma en Python nombres trabajo.

nombres en Python

Python no tiene variables. Tiene nombres. Sí, este es un punto pedante, y que sin duda puede utilizar las variables plazo tanto como te gusta. Es importante saber que hay una diferencia entre las variables y nombres. La toma del

Let el código equivalente de lo anterior C ejemplo y escribirlo en Python:

>>> x = 2337

Al igual que en C, el código anterior se divide en varias etapas diferentes durante la ejecución:

Nota: El PyObject no es el misma como un objeto de Python. Es específico para CPython y representa la estructura de base para todos los objetos de Python.

PyObject se define como una estructura C, por lo que si usted se pregunta por qué no se puede llamar código_de_tipo o refcount directamente, es porque usted no tiene acceso a las estructuras directamente. Las llamadas a métodos como sys.getrefcount (ayuda lata) obtener algunos detalles internos.

En la memoria, puede que se ve algo como esto:

se puede ver que el diseño de memoria es muy diferente de la disposición C de antes. En lugar de x que poseen el bloque de memoria donde reside el valor de 2337, el objeto Python recién creado es propietaria de la memoria en 2337 vidas. El nombre Python x no posee directamente dirección de cualquier memoria en la forma en la variable C x poseía una ranura estática en la memoria.

Si se va a tratar de asignar un nuevo valor a x, puede probar lo siguiente:

de >>> x = 2338

Lo que sucede aquí es diferente que el equivalente de C, pero no demasiado diferente de la vinculación original en Python.

este código:

  • Crea un nuevo PyObject
  • Establece el código de tipo de número entero para el PyObject
  • Establece el valor de 2,338 para los Puntos PyObject
  • x para el nuevo PyObject
  • Aumenta el refcount de la nueva PyObject por 1
  • disminuye la refcount del viejo PyObject por 1

ahora en la memoria, se vería algo como esto:

este diagrama ayuda a ilustrar que los puntos de x para una referencia a un objeto y no posee el espacio de memoria como antes. También muestra que la x = 2,338 comando no es una asignación, sino más bien la unión del nombre x a una referencia.

Además, el objeto anterior (que tenía el valor de 2337) ahora se está sentando en la memoria con un recuento de referencia de 0 y se limpiaron por el recolector de basura.

Se podría introducir un nuevo nombre, y, a la mezcla como en el ejemplo C:

>>> y = x

en la memoria, que tendría un nuevo nombre, pero no necesariamente un nuevo objeto:

Ahora se puede ver que un nuevo objeto Python tiene no sido creado, sólo un nuevo nombre que apunta al mismo objeto. Además, refcount del objeto se ha incrementado en uno. Podrías comprobar por la igualdad de la identidad del objeto para confirmar que son los mismos:

>>> y is x
True

El código anterior indica que X e Y son el mismo objeto. No se equivoquen, aunque: y sigue siendo inmutable.

Por ejemplo, se podría realizar la suma de y:

>>> y += 1
>>> y is x
False

Después de la llamada Además, se regresa con un nuevo objeto de Python. Ahora, las miradas de memoria como esto:

Un nuevo objeto ha sido creado, e y ahora apunta al nuevo objeto. Curiosamente, este es el mismo estado final si se hubiera obligado a 2339 y directa:

>>> y = 2339

La anterior sentencia tiene como resultado el mismo estado final de memoria como la adición. En resumen, en Python, lo haces variables no asignar. En su lugar, se enlaza a los nombres de referencias.

Una nota sobre Intern Objetos en Python

Ahora que usted entiende cómo los objetos de Python se crean y los nombres quedan obligados a esos objetos, es hora de lanzar una llave en la maquinaria. Esa llave se conoce con el nombre de objetos internados.

Supongamos que tenemos el siguiente código Python:

>>> x = 1000
>>> y = 1000
>>> x is y
True

Como el anterior, X e Y son los dos nombres que apuntan al mismo objeto Python. Pero el objeto de Python que contiene el valor de 1000 no siempre está garantizado para tener la misma dirección de memoria. Por ejemplo, si se va a sumar dos números para obtener 1000, que terminaría con una dirección de memoria diferente:

>>> x = 1000
>>> y = 499 + 501
>>> x is y
False

Esta vez, la línea x es y devuelve falso. Si esto es confuso, entonces no se preocupe. Estos son los pasos que se producen cuando se ejecuta este código:

técnicos Nota: Las etapas anteriores se producen sólo cuando se ejecuta este código dentro de un REPL. Si se va a tomar el ejemplo anterior, pegarlo en un archivo, y ejecutar el archivo y, a continuación, se verá que la x es recta y se devolverá True.

Esto se debe a que los compiladores son inteligentes. Los intentos del compilador CPython para hacer optimizaciones llamados optimizaciones de mirilla, que ayudar a salvar pasos de ejecución siempre que sea posible. Para obtener información detallada, se puede extraer el código fuente optimizador mirilla de CPython.

no es este desperdicio? Bueno, sí lo es, pero ese es el precio que paga por todos los grandes beneficios de Python. Usted nunca tiene que preocuparse por la limpieza de estos objetos intermedios o incluso necesidad de saber que existen! La alegría es que estas operaciones son relativamente rápido, y nunca tenía que saber cualquiera de esos detalles hasta ahora. desarrolladores

El núcleo de Python, en su sabiduría, también notaron estos residuos y decidieron hacer algunas optimizaciones. Estas optimizaciones como resultado un comportamiento que puede ser sorprendente para los recién llegados:

>>> x = 20
>>> y = 19 + 1
>>> x is y
True

En este ejemplo, se ve casi el mismo código como antes, excepto que esta vez el resultado es verdadero. Este es el resultado de objetos internados. Python pre-crea un cierto subconjunto de objetos en memoria, y los guarda en el espacio de nombres global para el uso diario. objetos

que dependen de la implementación de Python. CPython 3.7 pasantes lo siguiente:

El razonamiento detrás de esto es que estas variables son muy propensos a ser utilizados en muchos programas. Por internar estos objetos, Python evita que las llamadas de asignación de memoria para objetos que se utilizan de forma coherente.

cadenas que son menos de 20 caracteres y que contenga letras ASCII, dígitos o guiones serán internados. El razonamiento detrás de esto es que estos se supone que son una especie de identidad:

>>> s1 = "realpython"
>>> id(s1)
140696485006960
>>> s2 = "realpython"
>>> id(s2)
140696485006960
>>> s1 is s2
True

Aquí se puede ver que S1 y S2 ambos apuntan a la misma dirección en la memoria. Si se va a introducir una letra que no sea ASCII, número o un guión, a continuación, se obtendría un resultado diferente:

>>> s1 = "Real Python!"
>>> s2 = "Real Python!"
>>> s1 is s2
False

Debido a que este ejemplo tiene un signo de exclamación en él, estas cadenas no están internados y son diferentes objetos en (!) memoria.

Bono: Si realmente quiere estos objetos para hacer referencia al mismo objeto interno, entonces es posible que desee echa un vistazo a sys.intern (). Uno de los casos de uso de esta función se describe en la documentación: cadenas

internar es útil para ganar un poco de rendimiento en el diccionario de búsqueda, si las claves en un diccionario internadas, y es internado la clave de búsqueda, las comparaciones clave (después de hash) se puede hacer mediante un puntero en lugar de comparar una cadena comparar. objetos (Fuente)

internados son a menudo una fuente de confusión. Sólo recuerde, si alguna vez tiene dudas, que siempre se puede utilizar una ID () y consiste en determinar la igualdad de objeto.

Simulación de punteros en Python

El hecho de que los punteros en Python no existen de forma nativa no quiere decir que usted no puede obtener los beneficios del uso de punteros. De hecho, hay varias maneras para simular punteros en Python. Usted aprenderá dos en esta sección:

Está bien, vamos a llegar al punto.

Uso de tipos mutables como punteros

que ya has aprendido acerca de los tipos mutables. Debido a que estos objetos son mutables, se pueden tratar como si fueran punteros a simular el comportamiento puntero. Suponga que desea replicar el siguiente código c:

void add_one(int *x) {
*x += 1;
}

Este código toma un puntero a un número entero (* x) y después se incrementa el valor por uno. Aquí es una función principal de ejercer el código:

#include

int main(void) {
int y = 2337;
printf("y = %d\n", y);
add_one(&y);
printf("y = %d\n", y);
return 0;
}

En el código anterior, se asigna a 2337 y, a imprimir el valor actual, incrementar el valor en uno, y luego imprimir el valor modificado. La salida de la ejecución de este código sería el siguiente:

y = 2337
y = 2338

Una forma de replicar este tipo de comportamiento en Python es mediante el uso de un tipo mutable. Considere el uso de una lista y la modificación del primer elemento:

>>> def add_one(x):
... x[0] += 1
...
>>> y = [2337]
>>> add_one(y)
>>> y[0]
2338

Aquí, add_one (x) accede el primer elemento e incrementa su valor por uno. El uso de un medio de listas que el resultado final parece haber modificado el valor. Así punteros en Python ¿Existen? Bueno no. Esto sólo es posible porque la lista es un tipo mutable. Si se trató de utilizar una tupla, se llega a un error:

>>> z = (2337,)
>>> add_one(z)
Traceback (most recent call last):
File "", line 1, in
File "", line 2, in add_one
TypeError: 'tuple' object does not support item assignment

El código anterior demuestra que la tupla es inmutable. Por lo tanto, no admite la asignación artículo. lista no es el único tipo mutable. Otro enfoque común para imitar punteros en Python es usar un diccionario. de

Digamos que usted tenía una aplicación en la que quería hacer un seguimiento de cada vez que un evento interesante ocurrió. Una forma de lograr esto sería la creación de un diccionario y el uso uno de los elementos como un contador:

>>> counters = {"func_calls": 0}
>>> def bar():
... counters["func_calls"] += 1
...
>>> def foo():
... counters["func_calls"] += 1
... bar()
...
>>> foo()
>>> counters["func_calls"]
2

En este ejemplo, el diccionario contadores se utiliza para realizar un seguimiento del número de llamadas a funciones. Después de llamar a foo (), el contador se ha incrementado a 2 como se esperaba. Todo dict porque es mutable.

Tenga en cuenta que esto es sólo simula el comportamiento puntero y no se correlaciona directamente a los verdaderos punteros en C o C ++. Es decir, estas operaciones son más caros de lo que serían en C o C ++.

utilizar Python Objetos

La opción dict es una gran manera de punteros Emular en Python, pero a veces se vuelve tedioso para recordar el nombre de la clave que utilizó. Esto es especialmente cierto si usted está utilizando el diccionario en varias partes de su aplicación. Aquí es donde una clase de Python a medida realmente puede ayudar.

para construir el último ejemplo, supongamos que desea realizar un seguimiento de métricas en su aplicación. La creación de una clase es una gran manera de abstraer los detalles molestos:

class Metrics(object):
def __init__(self):
self._metrics = {
"func_calls": 0,
"cat_pictures_served": 0,
}

Este código define una clase de métricas. Esta clase todavía utiliza un diccionario para la celebración de los datos reales, que se encuentra en la variable miembro _metrics. Esto le dará la mutabilidad que necesita. Ahora sólo tiene que ser capaz de acceder a estos valores. Una buena manera de hacerlo es con propiedades:

class Metrics(object):
# ...

@property
def func_calls(self):
return self._metrics["func_calls"]

@property
def cat_pictures_served(self):
return self._metrics["cat_pictures_served"]

hace que este código utilizan de @property. Si no está familiarizado con decoradores, usted puede sacar de esta cartilla en Python decoradores. El decorador @property aquí se permite a func_calls de acceso y cat_pictures_served como si fueran atributos:

>>> metrics = Metrics()
>>> metrics.func_calls
0
>>> metrics.cat_pictures_served
0

El hecho de que se puede acceder a estos nombres como medios atributos que se extrajo el hecho de que estos valores están en un diccionario. También hace que sea más explícito lo que los nombres de los atributos son. Por supuesto, tiene que ser capaz de incrementar estos valores:

class Metrics(object):
# ...

def inc_func_calls(self):
self._metrics["func_calls"] += 1

def inc_cat_pics(self):
self._metrics["cat_pictures_served"] += 1

Usted ha introducido dos nuevos métodos:

Estos métodos modificar los valores en el dict métricas. Ahora tiene una clase que se modifica como si usted está modificando un puntero:

>>> metrics = Metrics()
>>> metrics.inc_func_calls()
>>> metrics.inc_func_calls()
>>> metrics.func_calls
2

Aquí, se puede acceder func_calls y inc_func_calls call () en varios lugares de las aplicaciones y los punteros de simular en Python. Esto es útil cuando se tiene algo así como indicadores que deben ser utilizados y actualiza con frecuencia en varias partes de sus aplicaciones.

Nota: En esta clase, en particular, por lo que inc_func_calls () y inc_cat_pics () explícito en lugar de utilizar @ evita que los usuarios property.setter de establecer estos valores a un int arbitraria o un valor no válido como un dict.

Aquí está el código fuente completo para la clase Métrica:

class Metrics(object):
def __init__(self):
self._metrics = {
"func_calls": 0,
"cat_pictures_served": 0,
}

@property
def func_calls(self):
return self._metrics["func_calls"]

@property
def cat_pictures_served(self):
return self._metrics["cat_pictures_served"]

def inc_func_calls(self):
self._metrics["func_calls"] += 1

def inc_cat_pics(self):
self._metrics["cat_pictures_served"] += 1

real Punteros Con ctypes

bien, así que tal vez hay punteros en Python, específicamente CPython. Utilizando la orden interna Módulo ctypes, puede crear punteros de tipo C real en Python. Si no está familiarizado con ctypes, a continuación, puede echar un vistazo a Extensión de Python C Bibliotecas y los “ctypes” Módulo.

La verdadera razón por la que usaría esto es si usted necesita para hacer una llamada a una función de una biblioteca de C que requiere un puntero. Volvamos a la add_one () C-función de delante:

void add_one(int *x) {
*x += 1;
}

Una vez más, este código está incrementando el valor de x por uno. Para utilizarlo, primero compilar en un objeto compartido. Suponiendo que el archivo anterior se almacena en add.c, se podría lograr esto con gcc:

$ gcc -c -Wall -Werror -fpic add.c
$ gcc -shared -o libadd1.so add.o

El primer comando compila el archivo fuente de C en un objeto llamado add.o. El segundo comando detiene fichero objeto desvinculado y produce un objeto compartido denominado libadd1.so.

libadd1.so debe estar en el directorio actual. Puede cargarlo en Python usando ctypes: Código

>>> import ctypes
>>> add_lib = ctypes.CDLL("./libadd1.so")
>>> add_lib.add_one
<_FuncPtr object at 0x7f9f3b8852a0>

El ctypes.CDLL devuelve un objeto que representa el objeto libadd1 compartido. Debido a que ha definido add_one () en este objeto compartido, se puede acceder a él como si fuera cualquier otro objeto Python. Antes de llamar a la función, sin embargo, se debe especificar la firma de la función. Esto ayuda a asegurar Python que se pase el tipo correcto de la función.

En este caso, la firma de la función es un puntero a un entero. ctypes le permitirá especificar esto usando el siguiente código:

>>> add_one = add_lib.add_one
>>> add_one.argtypes = [ctypes.POINTER(ctypes.c_int)]

En este código, se está configurando la firma de la función para que coincida con lo que C está a la espera. Ahora, si usted fuera a tratar de llamar a este código con el tipo equivocado, entonces se obtendría una buena advertencia en lugar de un comportamiento indefinido:

>>> add_one(1)
Traceback (most recent call last):
File "", line 1, in
ctypes.ArgumentError: argument 1: : \
expected LP_c_int instance instead of int

Python lanza un error, explicando que add_one () quiere un puntero en lugar de sólo un entero. Por suerte, ctypes tiene una manera de pasar punteros a estas funciones. En primer lugar, declarar un número entero de estilo C:

>>> x = ctypes.c_int()
>>> x
c_int(0)

Lo anterior código crea un estilo de C número entero x con un valor de 0. ctypes proporciona la byref práctico () para permitir pasar una variable por referencia.

Nota: El término por referencia se opone a pasar una variable por valor .

Al pasar por referencia, estás pasando la referencia a la variable original, por lo que las modificaciones se reflejará en la variable original. Pasando por los resultados de valor en una copia de la variable original, y las modificaciones no se reflejan en el original.

Usted puede usar esto para llamar add_one ():

>>> add_one(ctypes.byref(x))
998793640
>>> x
c_int(1)

Nice! Su número entero se incrementa en uno. Felicitaciones, usted ha utilizado con éxito punteros reales en Python.

Conclusión

Ahora tiene una mejor comprensión de la intersección entre objetos y punteros Python. A pesar de que algunas de las distinciones entre nombres y variables parece pedante, la comprensión de estos términos clave fundamentalmente amplía su comprensión de cómo las variables asas de Python.

También ha aprendido algunas maneras excelentes a los punteros de simular en Python:

  • Utilizando objetos mutables como punteros de baja sobrecarga
  • Creación de objetos personalizados de Python para la facilidad de uso de punteros reales
  • Apertura con la ctypes módulo

Estos métodos le permiten simular punteros en Python sin sacrificar la seguridad de memoria que ofrece Python.

Gracias por leer. Si todavía tiene preguntas, no dude en llegar, ya sea en la sección de comentarios o en Twitter.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *