maneira correta de ter um identificador exclusivo em Python?
-
16-09-2019 - |
Pergunta
Basicamente, eu tenho uma lista como: [START, 'foo', 'bar', 'spam', eggs', END]
eo START / identificadores finais são necessárias para mais tarde para que eu possa comparar mais tarde. Agora, eu tenho que configurar como esta:
START = object()
END = object()
Esta multa funciona, mas sofre com o problema de não trabalhar com decapagem. Eu tentei fazê-lo da seguinte maneira, mas parece que um método terrível de fazer isso:
class START(object):pass
class END(object):pass
poderia compartilhar qualquer um melhor meio de fazer isso? Além disso, o exemplo que eu tenha configurado acima é apenas uma simplificação de um problema diferente.
Solução
Se você quer um objeto que está garantido para ser único e pode também ser garantido para se restaurado para exatamente o mesmo identificar se para trás, funções de nível superior, classes, instâncias de classe em conserva e unpickled certas, e se você se preocupa com is
em vez de ==
também listas (e outros mutables), são todos muito bem. Ou seja, qualquer um:
# 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()
Nenhum destes é terrível, nenhum tem qualquer vantagem especial (dependendo se você se preocupa ==
ou apenas is
). vitórias provavelmente def
por força de generalidade, concisão, e mais leve.
Outras dicas
Você pode definir uma classe Symbol
para lidar com início e fim.
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))
Na verdade, eu como sua solução.
Um tempo atrás eu estava cortando em um módulo Python, e eu queria ter um valor mágico especial que não poderia aparecer em qualquer outro lugar. Passei algum tempo pensando sobre isso e o melhor que eu vim com é o mesmo truque que você usou:. Declarar uma classe, e usar o objeto de classe como o valor mágico especial
Quando você está verificando para o Sentinel, você deve, naturalmente, utilizar o operador is
, para a identidade do 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
Se a lista não tem cordas, eu tinha acabado de usar o "start", "fim", como Python faz a comparação O (1), devido a internar.
Se você fizer cordas precisa, mas não tuplas, o método cheapskate completa é a seguinte:
[("START",), 'foo', 'bar', 'spam', eggs', ("END",)]
PS: Eu tinha certeza de sua lista estava números antes, nem cordas, mas eu não consigo ver quaisquer revisões então eu devo ter imaginado
Eu acho que talvez isso seria mais fácil de responder se você fosse mais explícito sobre o que você precisa disso para, mas a minha inclinação se depara com um problema como este seria algo como:
>>> START = os.urandom(16).encode('hex')
>>> END = os.urandom(16).encode('hex')
Pros desta abordagem, como eu estou vendo
- Seus marcadores são strings (pode picles ou de outra forma facilmente serialize, por exemplo, para JSON ou um DB, sem qualquer esforço especial)
- Muito pouco provável que colidem acidentalmente ou de propósito
- Será serializar e desserializar para valores idênticos, mesmo através de processo é reiniciado, o que (eu acho) não seria o caso para
object()
ou uma classe vazia.
Contras (?)
- Cada vez que eles são recém-escolhido que será completamente diferente. (Este ser bom ou ruim depende de detalhes que você não ter fornecido, eu acho).