2009-09-03 7 views
5

Tengo una función que toma un argumento tipo cadena.En python, ¿cómo se prueba si un objeto similar a una cadena es mutable?

Quiero decidir si puedo guardar el argumento de forma segura y estar seguro de que no cambiará. Así que me gustaría probar si es mutable, por ejemplo, el resultado de buffer() construido a partir de array.array(), o no.

Actualmente uso:

type(s) == str 

¿Hay una mejor manera de hacerlo?

(copiar el argumento es demasiado costoso, por eso quiero evitarlo)

+3

me gustaría recomendar que reformular su título de la pregunta. Por definición, las cadenas (instancias 'str') en Python son ** nunca ** mutables. Los objetos tipo cadena pueden ser, sin embargo. –

+1

Esto no responde a su pregunta, así que lo estoy publicando como comentario: debe usar isinstance (s, basetring) en lugar de comparar el tipo, por lo que no desaproba las clases derivadas y unicode. En Python 3, use isinstance (s, str). –

+0

¿Qué estás haciendo si el argumento es mutable? – bstpierre

Respuesta

4

Si es solo heurístico para su almacenamiento en caché, simplemente use lo que funcione. isinstance (x, str), por ejemplo, casi exactamente como ahora. (Dado que desee para decidir si la caché o no; una prueba falsa de soporte sólo significa una pérdida de caché, no se hace nada malo.)


(Observación: Resulta que los objetos tampón son hashable , aunque su representación de cadena puede cambiar bajo sus pies;. la discusión de hash a continuación es interesante, pero no es la solución pura que estaba destinado a ser)

sin embargo, las clases bien implementados deben tener instancias siendo hashable si son inmutables y no si son mutables. Una prueba general sería analizar su objeto y probar el éxito.

>>> hash({}) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: dict objects are unhashable 

Esto dará falsos positivos, estoy seguro, pero los objetos mutables que se pueden manipular son estrictamente un salto de interfaz; Me esperar tipos biblioteca de Python a obedecer esta interfaz, una prueba de una pequeña muestra da respuestas correctas:

hashabe: str (Immutable), buffer (Warning, immutable slice of (possibly) mutable object!) 
unhashable: list, array.array 
+0

Si quiere evitar la excepción, puede hacer esto también: si '__hash__' en dir (obj) y obj .__ hash__: # hacer cosas –

+0

Sí, parece que no hay realmente una mejor manera de hacerlo. Por diversión, timed isinstance() vs type() e isinstance() es más lento cuando devuelve false. ¿Es porque hace una prueba/excepto internamente? – tonfa

+0

@ kaizer.se: el búfer es mutable (si crea un búfer de un array.array) – tonfa

3

Sólo tiene que utilizar duck typing - recuerde, es "más fácil pedir perdón que pedir permiso". Intenta mutar el objeto tipo cadena, y prepárate para atrapar una excepción si no puedes.

+3

Y tenga en cuenta que esto no es a prueba de agua; alguien siempre puede pasar un objeto que se parece a una cadena, pero tiene un método que efectivamente cambia su valor. Por ejemplo, un objeto tipo cadena podría tener un método force_upper() que no cambie la cadena en sí, pero hace que los getters devuelvan datos en mayúsculas. Si tiene invariantes importantes que dependen de saber si está recibiendo algo mutable, asegúrese de documentarlo, ya que a la mayoría de las API no les importa. –

+0

Esto es cierto, pero el OP quiere asegurarse de que el argumento * no * se modifique. – bstpierre

+0

La idea es buena (y pitonica), pero es difícil probar si la mutación funciona manteniendo la cadena "sin modificar" al mismo tiempo ('a [0] = a [0]' + caso especial para cadena vacía). Y, por cierto, try/except es más lento que una sentencia if si exceptúa una excepción que se debe generar. – tonfa

5

Sería mejor utilizar

isinstance(s, basestring) 

Funciona para cadenas Unicode también.

+0

No necesito que funcione para unicode, es realmente para bytes (estoy trabajando en un VCS). – tonfa

+0

También tiene la ventaja de trabajar para las clases derivadas de 'str'. –

+0

Esto no es una respuesta pero, por lo tanto, debe publicarse como comentario. –

3

que acababa de convertirlo en una cadena inmutable:

>>> s1 = "possibly mutable" 
>>> 
>>> s2 = str(s1) 
>>> s1 is s2 
True 

En caso s1 es inmutable el mismo objeto se da vuelta, lo que no hay sobrecarga de memoria. Si es mutable, se está haciendo una copia.

+0

no funciona tan bien si s1 no es un str real: >>> s1 = u "Foo" >>> s2 = str (S1) >>> s2 s1 es Falso – bstpierre

+0

No hace trabajo, una copia es muy costosa aquí. – tonfa

+2

¿Podría explicar por qué "una copia es muy costosa aquí"? Esta es la forma natural de Python para resolver su problema: si pasa una cadena inmutable a str() no hace nada, rápidamente; si pasas algo mutable, obtienes una cadena segura e inmutable. Hace el trabajo mínimo necesario para asegurar la inmutabilidad. Intento imaginar cuándo sería demasiado caro y aún no lo veo. – steveha

Cuestiones relacionadas