Zmienna instancji: self vs @

179

Oto kod:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

Co chcę wiedzieć, jest różnica między używaniem @agei self.agew age_difference_withmetodzie.

Sarunw
źródło

Odpowiedzi:

260

Pisanie @agebezpośrednio uzyskuje dostęp do zmiennej instancji @age. Pisanie self.agemówi obiektowi, aby sam wysłał wiadomość age, która zwykle zwróci zmienną instancji @age- ale może zrobić dowolną liczbę innych rzeczy w zależności od agesposobu implementacji metody w danej podklasie. Na przykład możesz mieć klasę MiddleAgedSocialite, która zawsze podaje wiek o 10 lat młodszy niż w rzeczywistości. Lub, bardziej praktycznie, klasa PersistentPerson może leniwie odczytać te dane z trwałego sklepu, buforować wszystkie swoje trwałe dane w haszu.

Głaskanie pod brodę
źródło
2
Kiedyś czytałem książkę w szynach i nie rozumiem różnicy między tym self a @, więc zawsze powinienem używać self.var_name w moich metodach (które nie ustawiają i nie pobierają), aby moje dane przy użyciu publicznego interfejsu, ja spędziłem czas na definiowaniu go w getter i seter, prawda?
sarunw
1
... angielski ... co rozumiesz przez dowolną liczbę rzeczy. Nie dostałem dwóch ostatnich przykładów.
user2167582,
23

Różnica polega na tym, że izoluje użycie metody od jej wdrożenia. Jeśli implementacja właściwości miałaby ulec zmianie - powiedzmy, aby zachować datę urodzenia, a następnie obliczyć wiek na podstawie różnicy czasu między teraz a datą urodzenia - kod w zależności od metody nie musi się zmieniać. Jeśli użyłby tej właściwości bezpośrednio, zmiana musiałaby się rozprzestrzenić na inne obszary kodu. W tym sensie bezpośrednie użycie właściwości jest bardziej kruche niż użycie interfejsu zapewnianego przez klasę.

tvanfosson
źródło
15
Ohhh, ponieważ self.age może odnosić się do zmiennej instancji lub metody instancji?
Nolan Amy,
@. @ ... smutne, że tak jest
cyc115
7

Ostrzegaj, gdy odziedziczysz klasę, z Struct.newktórej jest dobry sposób na wygenerowanie intializatora ( Jak wygenerować inicjalizator w Ruby? )

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

wróci

30
nil

Jednak po usunięciu inicjalizatora zostanie on zwrócony

nil
30

Z definicją klasy

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

Powinieneś podać konstruktor.

n2 = Node2.new(30)
n2.show()

wróci

30
30
prosseek
źródło
Dzięki za przykład @Prosseek, obecnie uczę się Ruby on Rails i jest to dokładnie takie zachowanie, które sprawia, że ​​czuję, że Ruby jest niepotrzebnie skomplikowany>. <.
cyc115
3

Pierwsza odpowiedź jest całkowicie poprawna, ale jako względny początkujący nie od razu zrozumiałem, co to sugeruje (wysyłanie wiadomości do siebie? Huh ...). Myślę, że krótki przykład pomoże:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50
kropelka
źródło
8
Ten przykład sprawił, że sprawy stały się bardziej mylące.
Oskar Holmkratz
1
Przykro mi, ale przykład nie jest wystarczająco komentowany. Nie mogę podążać za tobą.
kouty
Ktoś, kto przybył z Smalltalk, powie, że obiekt „wysyła wiadomość do siebie”. Ktoś, kto przyszedł z Pythona, powie, że obiekt „sam wywołuje metodę”. Nie mylcie się; są dokładnie tym samym. (Purystyczny semantyka może sprzeciwić się, że są one takie same dla języków z dynamicznym pisaniem i że wirtualne wywołanie metody C ++ nie jest dokładnie tym samym, co wysyłanie wiadomości. Purysta jest poprawny, ale prawdopodobnie wykracza to poza to pytanie / odpowiedź.)
GrandOpener
Podoba mi się ten przykład, ale proszę podać więcej komentarzy na temat tego, co się faktycznie dzieje. Trudne do naśladowania bez wyjaśnienia
CalamityAdam
2

Nie ma żadnej różnicy. Podejrzewam, że zrobiono to tylko dla dokumentalnej wartości widzenia self.agei other_person.agezbliżania się do siebie.

Podejrzewam, że użycie pozwala na napisanie w przyszłości faktycznego gettera, co może zrobić coś bardziej złożonego niż tylko zwrócenie zmiennej instancji, a w takim przypadku metoda nie musiałaby się zmieniać.

Jest to jednak mało prawdopodobna abstrakcja, o którą należy się martwić, jeśli zmiana implementacji obiektu jest uzasadniona, należy zmienić inne metody, w pewnym momencie proste odniesienie do samego obiektu jest całkowicie uzasadnione.

W każdym razie abstrakcja agewłasności wciąż nie wyjaśnia jednoznacznego użycia self, ponieważ zwykły ageprzywołałby również akcesora.

DigitalRoss
źródło
-3

@ wiek - to zdecydowanie wiek zmiennej instancji

self.age - odnosi się do wieku własności instancji.

LEMUEL ADANE
źródło