2013-03-07 13 views
5

Jestem całkiem nowy w Pythonie i muszę zadeklarować własną strukturę danych, jestem nieco zdezorientowany, jak to zrobić. Obecnie mam:Niestandardowe struktury danych w Pythonie

class Particle: 

    def __init__(self, mass, position, velocity, force): 

     self.mass = mass 
     self.position, self.velocity, self.force = position, velocity, force 

    def __getitem__(self, mass): 
     return self.mass 

    def __getitem__(self, position): 
     return self.position 

    def __getitem__(self, velocity): 
     return self.velocity 

    def __getitem__(self, force): 
     return self.force 

ten nie działa, jednak gdy próbuję zdefiniować instancję klasy z:

p1 = Particle(mass, position, velocity, force) 

Każda wartość właśnie kończy się jako (0.0, 0.0) (która jest wartością prędkości i siły).

Ktoś może wyjaśnić, gdzie idę źle, wszystko, czego potrzebuję od struktury danych, to móc wyciągnąć dane z tego, nic więcej. (Edit: faktycznie, przepraszam, będę musiał je zmienić nieco później)

Dzięki

+2

to działa na mnie jak jest. – askewchan

+2

Wszystkie twoje definicje "__getitem__" są tą samą metodą. Musisz sprawdzić, który element jest faktycznie pobierany. –

+3

@askewchan Nie powinno. –

Odpowiedz

15

Po pierwsze, powinieneś zrozumieć, że __getitem__ jest cukrem syntaktycznym. Fajnie jest mieć, ale jeśli go nie potrzebujesz, nie używaj go. __getitem__ i __setitem__ są w zasadzie, jeśli chcesz mieć dostęp do pozycji z Twojego obiektu za pomocą notacji nawiasu jak:

p= Particle(foo) 
bar = p[0] 

jeśli nie trzeba do tego, nie martw się o to.

Teraz wszystko inne. Wygląda na to, że masz główne cechy, które obiekt ma nosić w swojej definicji __init__, co jest w porządku. Teraz trzeba rzeczywiście wiążą te wartości na Twój obiekt za pomocą self:

class Particle: 
    def __init__(self, mass, position, velocity, force): 
     self.mass = mass 
     self.position = position 
     self.velocity = velocity 
     self.force = force 

To naprawdę on. Możesz teraz uzyskać dostęp do tych wartości za pomocą notacji kropki, tak jak poniżej:

mass,pos,vel,f = 0,0,0,0 # just for readability 
p = Particle(mass,pos,vel,f) 
print p.mass, p.position, p.velocity, p.force 

Jedną z miłych rzeczy możemy wydostać się z tego, że jeśli pytamy pytona co p jest, powie to, że jest to instancja typ Particle, tak:

in [1]: p 
out[1]: <__main__.Particle instance at 0x03E1fE68> 

teoretycznie podczas pracy z obiektami, jak to chcesz tam być „warstwę abstrakcji” pomiędzy użytkownikiem, a dane w taki sposób, że nie korzysta lub manipulować dane bezpośrednio. Aby to zrobić, tworzysz funkcje (jak próbujesz zrobić z __getitem__) w celu pośredniczenia w interakcjach między użytkownikiem a danymi za pomocą metod klasy. To jest miłe, ale często nie jest konieczne.

W swojej prostszej przypadku aktualizacji wartości tych atrybutów, można po prostu zrobić to bezpośrednio w ten sam sposób możemy je dostępnego z notacji dot:

in [2]: p.mass 
out[2]: 0 

in [3]: p.mass = 2 
in [4]: p.mass 
out[4]: 2 

Mogłeś zdobione to już, ale nie ma nic magicznego w funkcji __init__, ani nawet w definicji class (gdzie ogólnie powinieneś/powinnaś definiować większość atrybutów i metod twojej klasy). Niektóre rodzaje obiektów są dość permisywne, jeśli chodzi o umożliwienie dodawania atrybutów, kiedy tylko chcesz. Może to być wygodne, ale generalnie jest bardzo hackowskie i nie jest dobrą praktyką. Nie sugeruję, że to robisz, tylko pokazujesz, że to możliwe.

in [5]: p.newattr ='foobar!' 
in [6]: p.newattr 
out[6]: 'foobar!' 

Dziwne, prawda? Jeśli to sprawi, że twoja skóra się czołgnie ... cóż, może powinna. Ale jest to możliwe i kim jestem, aby powiedzieć, co możesz i czego nie możesz zrobić. To jest sposób na to, jak działają zajęcia.

+0

To świetnie, dzięki! Pytanie o szybką kontynuację, w jaki sposób mogę zmienić wartości po utworzeniu instancji Particle? – djcmm476

+0

@Incredidave Assignment, podobnie jak w przypadku zmiennych (są pewne subtelne różnice, ale większość ludzi dogaduje się przez miesiące bez ich zrozumienia, więc powinieneś być w porządku^^): 'p.mass = nowa wartość' – delnan

+0

@delnan Cool, dzięki los. – djcmm476

7
class Particle: 
    def __init__(self, mass, position, velocity, force): 
     self.mass = mass 
     self.position = position 
     self.velocity = velocity 
     self.force = force 

particle = Particle(1, 2, 3, 4) 
print(particle.mass) # 1 

Jeśli chcesz udawać klasa ma właściwości, można użyć @property dekorator:

class Particle: 
    def __init__(self, mass, position, velocity, force): 
     self.mass = mass 
     self.position = position 
     self.velocity = velocity 
     self.force = force 

    @property 
    def acceleration(self): 
     return self.force/self.mass 

particle = Particle(2, 3, 3, 8) 
print(particle.acceleration) # 4.0 
+0

To świetnie, dzięki. Jestem całkiem nowy w Pythonie i starałem się znaleźć coś przyzwoitego, żeby to opisać. Jak zwykle w Pythonie, odpowiedź jest prostsza, niż myślałem. – djcmm476

+0

Ciekawe, dlaczego * udajemy *, że mamy własności, skoro faktyczne posiadanie właściwości jest takie proste? – delnan

+0

@delnan Załóżmy, że właściwość jest zdefiniowana przez formułę opierającą się na dwóch innych właściwościach. Jeśli zmienisz jedną z nich (jak masa), musisz również zaktualizować przyspieszenie. To prawdopodobnie oznacza buggy lub niechlujny kod. –

4

wydaje się collections.namedtuple jest co jesteś po:

from collections import namedtuple 

Particle = namedtuple('Particle', 'mass position velocity force') 
p = Particle(1, 2, 3, 4) 
print p.velocity 
+1

Tylko wtedy, gdy atrybuty nie muszą się zmieniać kiedykolwiek podczas życia obiektu, a wszystko to w porządku, ponieważ jest to sekwencja. Spokój nazwanych kciuków także odczuwalny jest w momencie, gdy trzeba dodać metody lub coś w stylu konstruktora (nadal są użyteczne, ale wymagają bardziej ostrożnej obsługi i brzydkiego kodu, aby działało dobrze). – delnan

+1

@delnan Prawda, ale idę przez oświadczenie OP, że "wszystko, czego potrzebuję ze struktury danych, to móc wyciągnąć dane z tego, nic więcej." –

+0

Tak, a twoja odpowiedź jest w porządku. Po prostu prawie nigdy nie widziałem tych ograniczeń, nawet jeśli są one ważne, więc zrobiłem zwyczaj mówienia o nich. – delnan

0

Jeśli tylko trzeba przechowywać niektóre wartości atrybutów (podobny do języka C struct), można po prostu zrobić:

class myContainer(object): 
    pass # Do nothing 

myContainerObj = myContainer() 
myContainerObj.storedAttrib = 5 
print myContainerObj.storedAttrib 
+0

Nie, to tylko okropny sposób na napisanie słownika. Jestem przeciwny używaniu możliwości dodawania i usuwania atrybutów do obiektów bez bardzo mocnych powodów. Powoduje to, że błędy są trudniejsze do wykrycia (czasami ta rzecz ma atrybut, czasami nie, w zależności od przepływu sterowania). – delnan

+0

@delnan Zgadzam się ogólnie, ale dla kogoś nowego w Pythonie jest to proste rozwiązanie i nie wymaga zrozumienia słowników, mieszania i metody "get()". (Jest to również w dokumentach samouczka w języku Python: http://docs.python.org/3/tutorial/classes.html#odds-and-ends) – neilr8133

+0

edit: (_insert_: "' get() 'metoda określania wartości jeśli atrybut nie istnieje lub inna metoda do sondowania, jeśli została zdefiniowana "). Osobiście wolałbym zobaczyć pełną klasę z '__init__' i właściwe metody get/set dla wszystkich właściwości, ale w mojej (prawdopodobnie niepoprawnej) opinii spełnia to potrzeby PO). – neilr8133