2009-11-05 15 views
6

Básicamente, tengo una lista como: [START, 'foo', 'bar', 'spam', eggs', END] y los identificadores de INICIO/FINAL son necesarios para más tarde para que pueda comparar más adelante. En este momento, lo tengo configurado de esta manera:¿La manera correcta de tener un identificador único en Python?

START = object() 
END = object() 

Esto funciona bien, pero tiene el problema de no trabajar con decapado. He intentado hacerlo de la siguiente manera, pero parece que un método terribles para lograrlo:

class START(object):pass 
class END(object):pass 

Podría alguien compartir una mejor forma de hacer esto? Además, el ejemplo que he configurado arriba es solo una simplificación excesiva de un problema diferente.

+0

creo que sería mejor llamarlo simplemente una simplificación en lugar de "una simplificación excesiva" – Casebash

+0

Dado que lo que está haciendo es muy complicada (sin ninguna explicación de por qué), yo diría que es una "complicación" o tal vez una "sobrecomplicación". Los objetos centinela son esencialmente inútiles: hay formas mucho mejores de organizar los datos. –

+7

S: ese habría sido un comentario útil si hubiera incluido alguna explicación de una de las formas "mucho, mucho mejor". Tal como está, solo parece malhumorado y condescendiente. –

Respuesta

9

Si desea que un objeto que se garantiza que sea único y también puede ser garantizado para conseguir restaurado a exactamente el mismo identificar si en vinagre y unpickled la derecha de nuevo, de alto nivel funciones, clases, instancias de clases , y si te importa is en lugar de == también enumera (y otras tablas mutables), están bien. Es decir, cualquiera de:

# work for == as well as is 
class START(object): pass 
def START(): pass 
class Whatever(object): pass 
START = Whatever() 

# if you don't care for "accidental" == and only check with `is` 
START = [] 
START = {} 
START = set() 

Ninguno de estos es terrible, no tiene ninguna ventaja especial (dependiendo si se preocupan por == o simplemente is). Probablemente def gana a fuerza de generalidad, concisión y peso más ligero.

+0

Prefiero la opción n. ° 1 de usar la clase como centinela, porque ofrece las excepciones más informativas: obtiene AttributeError que identifica el valor que falló. Aunque es un poco extraño porque te permite crear objetos, pero no hay una manera simple de evitarlo, y realmente no es tan importante. – acjay

0

Creo que tal vez esto sería más fácil de responder si eran más explícitos acerca de lo que necesita esto para, pero mi inclinación si se enfrentan a un problema como este sería algo así como:

>>> START = os.urandom(16).encode('hex') 
>>> END = os.urandom(16).encode('hex') 

Pros de este enfoque, como lo estoy viendo

  • Sus marcadores son cadenas (puede conservar en vinagre o de otra manera fácilmente serializar, por ejemplo, para JSON o una base de datos, sin ningún esfuerzo especial)
  • muy pocas probabilidades de chocar accidentalmente oa propósito
  • Se serializará y deserializará en valores idénticos, incluso en los reinicios de proceso, que (creo) no sería el caso para object() o una clase vacía.

Contras (?)

  • Cada vez que han sido recientemente elegidos que serán completamente diferentes. (Esto es bueno o malo depende de los detalles que no has proporcionado, creo).
+0

Lo siento, pero esto no funciona si selecciono el resultado y lo abro en un proceso aparte ya que ahora cada uno es diferente. –

+0

I * really * no me gusta esta solución (sé que la posibilidad de una colisión es pequeña, pero si sucede, ¡estás realmente abarrotada!), Pero sí enumeraste las limitaciones, así que me resisto a mi impulso de rechazar – Casebash

+1

@Casebash ¿1/2^64 parece una probabilidad irracionalmente alta para usted, o tiene motivos para creer que/dev/* al azar está roto? –

1

Si su lista no tiene cadenas, solo usaría "inicio", "fin", ya que Python hace la comparación O (1) debido a la internación.

Si necesita cuerdas, pero no tuplas, el método tacaño completa es:

[("START",), 'foo', 'bar', 'spam', eggs', ("END",)] 

PD: Me fue que su lista fue números antes, no cadenas, pero no puedo ver ninguna revisión por lo debo haber imaginado

1

En realidad, me gusta su solución.

Hace un tiempo estaba pirateando un módulo de Python, y quería tener un valor mágico especial que no podría aparecer en ningún otro lado. Pasé un tiempo pensando en ello y lo mejor que se me ocurrió fue el mismo truco que usaste: declarar una clase y usar el objeto de la clase como el valor mágico especial.

cuando se está comprobando para el centinela, que debe, por supuesto, utilizar el operador is, de la identidad del objeto:

for x in my_list: 
    if x is START: 
     # handle start of list 
    elif x is END: 
     # handle end of list 
    else: 
     # handle item from list 
+0

Si declara su propia clase == solo comprobará la identidad de forma predeterminada – Casebash

+3

Es cierto, pero yo no lo recomendaría. Si escribe '==' cuando realmente quiere decir 'is', no está expresando claramente su intención.Y no estoy seguro de qué tan probable es, pero es posible que alguien pueda escribir una clase cuyo método '__cmp __()' devuelve 0 cuando se compara con tu clase centinela. (Acabo de escribir uno como prueba de concepto ...) Si usas 'is', sabes exactamente lo que hará. – steveha

2

Se puede definir una clase Symbol para el manejo inicial y final.

class Symbol: 
    def __init__(self, value): 
     self.value = value 

    def __eq__(self, other): 
     return isinstance(other, Symbol) and other.value == self.value 

    def __repr__(self): 
     return "<sym: %r>" % self.value 

    def __str__(self): 
     return str(self.value) 

START = Symbol("START") 
END = Symbol("END") 

# test pickle 
import pickle 
assert START == pickle.loads(pickle.dumps(START)) 
assert END == pickle.loads(pickle.dumps(END)) 
+0

De hecho, lo estaba haciendo así por un tiempo. –

+0

Entonces, ¿por qué quieres cambiar a otro enfoque? De hecho, algunos otros idiomas tienen soporte integrado para símbolos. En Ruby, puede escribir ': start' y': end' para iniciar y detener símbolos. –

+0

En Python, la cadena está destinada a ser utilizada como símbolos. Excepto que a veces también queremos usar cadenas, por lo que necesitamos estos otros enfoques – Casebash

Cuestiones relacionadas