2009-11-05 11 views
6

Zasadniczo mam listę taką jak: [START, 'foo', 'bar', 'spam', eggs', END], a identyfikatory START/END są potrzebne na później, więc mogę później porównać. Teraz mam to ustawione w następujący sposób:Właściwy sposób na posiadanie unikalnego identyfikatora w Pythonie?

START = object() 
END = object() 

Działa to dobrze, ale cierpi na problem braku pracy z marynowaniem. Próbowałem zrobić to w następujący sposób, ale wydaje się strasznym sposobu realizacji tego:

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

Czy ktoś może podzielić się lepszych sposobów to zrobić? Również powyższy przykład jest tylko nadmiernym uproszczeniem innego problemu.

+0

myślę, że byłoby lepiej po prostu nazwać to uproszczenie zamiast „uproszczeniem” – Casebash

+0

Ponieważ to, co robisz jest bardzo skomplikowana (bez wyjaśnienia dlaczego), nazwałbym ją „powikłanie” lub może "nadmierna komplikacja". Obiekty Sentinel są w zasadzie bezużyteczne - istnieją o wiele lepsze sposoby organizowania danych. –

+7

S: Byłby to przydatny komentarz, gdybyś zamieścił wyjaśnienie jednego z "daleko, dużo lepszych" sposobów. W tej chwili wydaje się po prostu crabby i protekcjonalny. –

Odpowiedz

9

Jeśli chcesz obiekt, który jest gwarancją wyjątkowy i można również zagwarantować, aby przywrócić dokładnie taka sama określić, czy marynowane i unpickled powrotem, najwyższego poziomu funkcje, klasy, instancje klasy i jeśli zależy Ci na is zamiast na == również listy (i inne mutables) są w porządku. To znaczy, każdy z:

# 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() 

Żaden z nich nie jest straszne, nikt nie ma żadnych specjalnych korzyści (w zależności czy dbasz o == lub po prostu is). Prawdopodobnie def wygrywa dzięki ogólności, zwięzłości i lżejszej wadze.

+0

Preferuję opcję # 1 używania klasy jako wartownika, ponieważ daje ona najbardziej informatywne wyjątki - otrzymujesz AttributeError, który identyfikuje wartość, która się nie powiodła. Jest to trochę dziwne, ponieważ pozwala tworzyć obiekty, ale nie ma prostego sposobu zapobiegania temu, a to naprawdę nie jest wielka sprawa. – acjay

0

Myślę, że to byłoby łatwiej odpowiedzieć, jeśli były bardziej wyraźne, co trzeba to na, ale moja skłonność jeśli do czynienia z problemem, jak to byłoby coś takiego:

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

Plusy to podejście, jak widzę to

  • Twoje wskaźniki są ciągi (może marynowane lub w inny sposób łatwo serializacji, np JSON lub DB, bez specjalnego wysiłku)
  • bardzo mało prawdopodobne, aby zderzyć się przypadkowo lub celowo
  • Dokonuje serializacji i deserializacji do identycznych wartości, nawet podczas restartów procesów, co (jak sądzę) nie byłoby możliwe w przypadku object() lub pustej klasy.

Wady (?)

  • każdym razem, gdy nowo wybrane będą całkowicie różne. (To jest dobre lub złe, zależy od szczegółów, których nie dostarczyłeś, tak mi się wydaje).
+0

Przepraszam, ale to nie działa, jeśli podniosę wynik i otworzę go w oddzielnym procesie, ponieważ teraz każdy jest inny. –

+0

Ja * naprawdę * nie podoba mi się to rozwiązanie (wiem, że prawdopodobieństwo kolizji jest niewielkie, ale jeśli tak się dzieje, to jesteś naprawdę wypchany!), Ale wymieniłeś ograniczenia, więc przeciwstawiam się mojej chęci downward – Casebash

+1

@Casebash Czy 1/2^64 wydaje ci się niedorzecznie wysokie prawdopodobieństwo, czy masz powody, by wierzyć/dev/* losowy jest zepsuty? –

1

Jeśli twoja lista nie zawierała napisów, po prostu użyłbym "start", "end", ponieważ Python tworzy porównanie O (1) z powodu interningu.

Jeśli potrzebują struny, ale nie krotki, pełna metoda cheapskate jest:

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

PS: Byłem pewien, czy lista została numery przed, a nie ciągi, ale nie widzę żadnych korekt tak muszę sobie wyobrazić to

1

Właściwie to lubię twoje rozwiązanie.

Jakiś czas temu hackowałem na module Pythona i chciałem mieć specjalną magiczną wartość, która nie mogłaby pojawić się nigdzie indziej. Spędziłem trochę czasu na myśleniu o tym, a najlepsze, co wymyśliłem, to ta sama sztuczka, której użyłeś: zadeklaruj klasę i użyj obiektu klasy jako specjalnej magicznej wartości.

Podczas sprawdzania Sentinel, należy oczywiście użyć operatora is, tożsamości obiektu:

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

Jeśli zadeklarujesz własną klasę ==, domyślnie sprawdzisz tylko tożsamość – Casebash

+3

Prawda, ale nie poleciłbym jej. Jeśli napiszesz "==", kiedy naprawdę masz na myśli "jest", nie wyrażasz jasno swojego zamiaru.I nie jestem pewien, jak bardzo jest prawdopodobne, ale jest możliwe, że ktoś mógłby napisać klasę, której metoda '__cmp __()' zwraca 0, gdy porównuje ją do klasy wartownika. (Napisałem jeden jako dowód koncepcji ...) Jeśli używasz 'jest', wiesz dokładnie, co zrobi. – steveha

2

Można zdefiniować klasę Symbol do obsługi początkowe i końcowe.

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

Tak naprawdę robiłem to przez jakiś czas. –

+0

Więc dlaczego chcesz zmienić inne podejście? W rzeczywistości niektóre inne języki mają wbudowaną obsługę symboli. W Ruby możesz napisać ': start' i': end' dla symboli start i stop. –

+0

W języku Python ciąg znaków jest przeznaczony do użycia jako symbole. Poza czasami chcemy również używać łańcuchów, więc potrzebujemy tych innych podejść – Casebash

Powiązane problemy