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.

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.


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ę.


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

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

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.


-3

@ wiek - to zdecydowanie wiek zmiennej instancji

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.