Używanie self

Cześć, kiedy odwoływać się do atrybutu klasy self.atrybut a kiedy bez self. Nie wiem czy dobrze rozumiem ale przez self.atrybut odwołuje się do konkretnego obiektu a bez do klasy ?? Może jakieś łopatologiczne wytłumaczenie ??

self (czyli @ przed atrybutem) oznacza atrybut instancji, jest osobny dla kazdego pojedynczego obiektu. natomiast uzycie ‘@@’ oznacza atrybut klasy, czyli jest wspolny dla wszystkich obiektow


Czaje czaje. Jeszcze mam takie pytanie mam taki kawałek kodu

[code]class User

has_many :cars

def get_random_car
car = cars.find(1)
puts car.get_name
end

end[/code]
Raz otrzymuję poprawny wynik (rekord na pewno istnieje w bazie) a czasami undefined method … for nil class zrobiłem tak jak poniżej i wszystko działa tylko nie wiem czy tu był błąd czy pech tak chce że 100 razy zadziałało ?

[code]class User

has_many :cars

def get_random_car
self.car = cars.find(1) //przypadkowa wartość
puts self.car.get_name
end

end[/code]

To są błędne informacje. Self w żadnym wypadku nie jest tym samym co @ przed atrybytem. To o czym piszesz to faktycznie zmienne instancyjne (@zmienna) i klasowe (@@zmienna). A self to słowo kluczowe, które reprezentuje obiekt, na rzecz którego wywołano daną metodę.

[code=ruby]def to_s
self.name

lub

name

lub

@name
end[/code]
W powyższym przykładzie dwie pierwsze formy wywołania zadziałają tylko wtedy gdy zdefiniowany jest akcesor (attr_reader :name lub def name; @name; end) dla atrybutu name. Trzecia forma zadziała zawsze.

Co do konieczności dodania self przed wywołaniami metod - należy to robić wtedy, gdy spodziewamy się, że dane wywołanie może być potraktowane albo jako użycie zmiennej albo jako wywołanie metody. Przykładowo:

[code=ruby]def to_s
name = “Ala”
name
end

def to_s
name
end[/code]
Pierwszy wariant spowoduje zwrócenie wartości zmiennej name, a drugi wywołanie metody name (o ile istnieje). Dlatego w tego rodzaju sytuacjach zaleca się stosowanie słowa kluczowego self, (tzn. self.name), które wyraźnie wskazuje, że mamy do czynienia z wywołaniem metody, a nie odwołaniem do zmiennej.

Czyli czy dodam self czy nie to nie powinno być różnicy ?
…już mi się wydawało mi się że self to tak jak this w java mówi nam że odwołujemy się do tego konkretnego obiektu … ?

[quote=pliczek]Czyli czy dodam self czy nie to nie powinno być różnicy ?
…już mi się wydawało mi się że self to tak jak this w java mówi nam że odwołujemy się do tego konkretnego obiektu … ?[/quote]
Co do tego, że self działa podobnie do javy masz rację. Tyle tylko, że w ruby self jest w pewnych przypadkach opcjonalne. Prosty przykład:

[code=ruby]class Foo
def foo
“metoda foo”
end

def test
puts foo #=> “metoda foo”
puts self.foo #=> “metoda foo”
end
end

Foo.new.test[/code]
W powyższym przypadku ruby sprawdzi czy nie ma żadnej zmiennej o nazwie foo i dlatego, że nie ma tutaj żadnej zmiennej wykona metodę foo, czyli domyślnie wywoła tą metodę na rzecz “self”.

Jeżeli jednak istnieje zmienna o takiej samej nazwie jak metoda, to zostanie ona użyta:

[code=ruby]class Foo
def foo
“metoda foo”
end

def test
foo = “zmienna foo”

puts foo #=> "zmienna foo"
puts self.foo #=> "metoda foo"

end
end

Foo.new.test[/code]
Jeżeli chodzi o Twój problem z “cars.find(1)”, to czy z błędem “undefined method … for nil class” na pewno spotkałeś się przy takim kodzie? I co to była za metoda? Jeżeli samochodu z id 1 nie ma w bazie, to metoda find powinna rzucić wyjątek, a nie zwrócić nil.

[code]…
has_many :cars
belongs_to :brand

def check_car
car = cars.by_brand(self.brand.name).first //bez self potrafiło sypnąć nil -em po dodaniu self problem ustał lub nie mogę na niego trafić

end[/code]
lub jeszcze takie pytanie ku zrozumieniu jak mam taką metodę

[code]…
attr_accessible :status, :new_order

def set_params
self.status = 1 //powinno być self czy nie
self.new_order = 1
end[/code]

[quote=pliczek][code]…
has_many :cars
belongs_to :brand

def check_car
car = cars.by_brand(self.brand.name).first //bez self potrafiło sypnąć nil -em po dodaniu self problem ustał lub nie mogę na niego trafić

end[/code]
lub jeszcze takie pytanie ku zrozumieniu jak mam taką metodę

[code]…
attr_accessible :status, :new_order

def set_params
self.status = 1 //powinno być self czy nie
self.new_order = 1
end[/code]
[/quote]
Powinno.
Jeśli chcesz użyć metody “zapisującej” (kończącej się na “=”), musisz użyć słowa kluczowego self, bo inaczej interpreter Ruby potraktuje taki zapis jako inicjalizację zmiennej lokalnej.

Ten pierwszy problem wygląda dość dziwnie, self nie powinno mieć tutaj żadnego znaczenia. Myślę, że mogłeś mieć inny problem i tylko przypadkowo zaczęło działać po zmianie na self. Co do samego kodu, to jeżeli masz jakieś metody, które mogą zwrócić nil, to powinieneś sprawdzić czy na pewno dostałeś coś z bazy danych.

[quote=pliczek][code]…
attr_accessible :status, :new_order

def set_params
self.status = 1 //powinno być self czy nie
self.new_order = 1
end[/code]
[/quote]
W tym przypadku musisz używać self ze względu na to, że inaczej ruby zinterpretuje to jak próbę stworzenia nowej zmiennej.

Oki dzięki za odpowiedz i pomoc :wink: myślę że zrozumiałem o co chodzi z self

Chciałbym zaznaczyć że w Rubym nie ma ‘odwoływania się do atrybutów’, chyba że odwołujesz się do zmiennych instancji przez @zmienna. self.cośtam jest wysłaniem wiadomości “cośtam” do bieżącego obiektu. Nie jest to ‘wywołanie metody’ i nie jest to równoważne z @ (tak jest w Coffeescript).

W Ruby: @cośtam - odwołujesz się bezpośrednio do zmiennej na bieżącym obiekcie.

self.cośtam - wysyłasz wiadomość ‘cośtam’ do bieżącego obiektu. Zazwyczaj odpowie metoda ‘cośtam’, ale może być np. przechwycone przez method_missing.

To subtelna różnica ale ważna, bo redefiniuje całe myślenie o obiektach i komunikacji pomiędzy nimi (mechanizmy delegacji, metaprogramming, to w jaki sposób projektujesz własne klasy będą się różnić właśnie przez inne podejście niż w Javie z tymi znanymi właśnie z Javy czy C++).

Oki bo już się pogubiłem :wink:
Jeżeli przypisuje wartość do zmiennej to używam self.zmienna = x , jeżeli chcę np wyświetlić zmienną to robię po prostu puts zmienna, jak chce wywołać metodę która nazywa się tak samo jak zmienna robię self.zmienna ?. Jeżeli nie mam metody o nazwie zmiennej i zrobię puts self.zmienna (otrzymuję wartość zmiennej) ?

Chodzi mi o to żebym miał pewność że odwołuje się do atrybutu konkretnego obiektu tak jak w Javie przez this. :wink:

[code]class Foo
attr_accessible :name
self.name = “Test”

puts name #=> ‘test’
puts self.name #=> ‘test’

end[/code]
otrzymuje dwa takie same wyniki czyli nie ma znaczenia czy użyję self czy nie ?

W tym przypadku jest tak:

class Foo attr_accessor :name end
to inaczej:

[code=ruby]class Foo
def name
@name
end

def name=(name)
@name = name
end
end[/code]
Odwolujac sie do self.name (i rowniez do samego name, jesli nie masz zdefiniowanej zmiennej lokalnej) w Rubym wywolujesz metode Foo#name - w Javie wywolywalbys getName(). Tak samo jesli mowisz self.name = ‘foo’, wywolujesz metode Foo#name= (w Javie - setName()). Mozesz miec absolutna pewnosc odwolujac sie bezposrednio do zmiennej instancji @name. Jesli chcesz odwolywyac sie przez metody, to jak pisal Drogus - w pewnych przypadkach self jest opcjonalne, a w innych nie.

Koledze chyba chodziło o:

class Foo attr_accessor :name end

[quote=pski]Koledze chyba chodziło o:

class Foo attr_accessor :name end
[/quote]
Dokładnie tak. attr_accessor :name - jak się do tego pola odwołać żeby była pewność że to chodzi o konkretny obiekt i o ten atrybut tak jak w javie this.name.

Dla przypisania: jeżeli wywołasz

[code]name = “foo”

stworzysz zmienną name[/code]

[code]self.name = “foo”

wywołasz metodę name=[/code]

Oczywiscie, dzieki - pisanie postow przed poranna kawa :wink:

Jako, że wątek się trochę rozrasta i jest tutaj dużo informacji, pozwolę sobie w kilku zdaniach to wszystko podsumować, mam nadzieję, że rozjaśnię, a nie jeszcze bardziej pomieszam :slight_smile:

Pojawiały się porównania do javy, więc również tutaj się do tego odniosę.

Pierwsza sprawa jaką należy zauważyć, to fakt, że w ruby nie ma zmiennych instancji w takiej formie jak występują one w javie. W ruby mamy 3 typy zmiennych:

foo = 'foo' # zmienna lokalna, jest widoczna tylko w kontekście bloku, # w którym została zadeklarowana @foo = 'foo' # zmienna instancji, tzn. jest widoczna w #kontekście instancji, w której została zadeklarowana @@foo = 'foo' # zmienna klasowa, tzn. jest widoczna w # kontekście klasy, w której została zadeklarowana
Zmienne klasowe na razie pominę, bo są one wykorzystywane dość rzadko, więc prosty przykład (polecam się tym pobawić jak ktoś nie rozumie do końca o co chodzi):

[code=ruby]class MyClass

konstruktor

def initialize
# deklarujemy 2 zmienne, jedną instancji, drugą
@foo = ‘foo’
bar = ‘bar’
end

def test
# tutaj sprobujemy wypisać zmienne zadeklarowane w konstruktorze
puts @foo #=> ‘foo’
puts bar #=> NameError: undefined local variable or method `bar’ for #MyClass:0x1035e2c58
end
end

my_object = MyClass.new
my_object.test[/code]
W powyższym kawałku kodu definiujemy prostą klasę. W konstruktorze deklarujemy 2 zmienne, @foo i bar. Pierwsza z nich to zmienna instancji, druga to zwykła zmienn. Następnie tworzymy nowy obiekt klasy MyClass i wywołujemy na nim metodę test. W metodzie test próbujemy użyć obu zadeklarowanych wcześniej zmiennych. Ze względu na to, że zmienna @foo jest zmienną instancji, możemy jej używać wszędzie w obrębie danej instancji (obiektu). Dlatego po zadeklarowaniu jej w konstruktorze możemy jej użyć w innej metodzie. Inaczej jest ze zmienną lokalną bar, której zasięg konczy się z końcem bloku, w którym została zadeklarowana. Z tego powodu zmienna bar nie będzie dostępna poza metodą initialize.

W tym kontekście ciekawy jest komunikat jaki dostainemy: “undefined local variable or method `bar’”. Pokazuje to, że ruby nie wie czy chcieliśmy użyć metody czy zmiennej, bo “bar” mogłoby być i jednym i drugim, w zależności od kontekstu. Dzieje się tak dlatego, że w ruby nawiasy i self są opcjonalne, zobaczmy podobny przykład:

[code=ruby]class MyClass
def test
puts foo #=> NameError: undefined local variable or method foo' for #<MyClass:0x1035e2c58> puts foo() #=> NoMethodError: undefined methodfoo’ for #MyClass:0x1034ff2f0
puts self.foo #=> NoMethodError: undefined method `foo’ for #MyClass:0x1034ea968
end
end

my_object = MyClass.new
my_object.test[/code]
Widzimy tutaj, że w pierwszym przypadku ruby nie wie czy chodziło nam o zmienną, ale już w drugim i trzecim jest inaczej. Przy foo() wiadomo, że to jest metoda, ponieważ nie pominięte zostały nawiasy, przy “self.foo” również wiadomo, że to będzie metoda, bo do zmiennej nie można się odwołać poprzez self.

I tutaj dochodzimy do kolejnej rzeczy, która może powodować problemy, szczególnie kiedy przychodzi się z javy. W ruby nie można się odwołać do zmiennej instancji z zewnątrz instancji (a przynajmniej nie w standardowy sposób). Niemożliwe jest zrobienie czegoś takiego:

[code=ruby]class MyClass
def initialize
@foo = ‘foo’
end
end

my_object = MyClass.new
my_object.foo # NoMethodError[/code]
W javie byloby to możliwe, ponieważ istnieje coś takiego jak publiczna zmienna instancji, jeżeli w ciele klasy użyjemy np. “public int foo;”, to można następnie zrobić “object.foo” i “object.foo = 1”.

No i nareszcie dochodzimy do “accessorów”. Jak już wspomnialem powyżej, w ruby nie możemy odwołać się do zmiennej instancji spoza tej instancji, więc wszelki dostęp do zmiennych instancji realizowany jest przez wykonanie metod. Tak jak zauważyła squil, attr_accessor jest równoważny zdefiniowaniu 2 metod:

class MyClass attr_accessor :foo end
znaczy to samo co:

[code=ruby]class MyClass
def foo
@foo
end

def foo=(value)
@foo = value
end
end[/code]
Tzn. pod spodem trzymamy zmienną instancji, ale odwołujemy się do niej poprzez zdefiniowane metody. Myślę, że z tego faktu wynika niezrozumienie jak kod rubiego odnieść do javy (np. this.name).

Wracając teraz do self:

  • self jest opcjonalne przy wykonaniu metod danego obiektu (dlatego jeżeli masz metodę foo albo attr_accessor :foo, to self.foo będzie tym samym co foo, o ile nie ma żadnej zmiennej lokalnej o tej samej nazwie)
  • self nie jest opcjonalne przy self.foo = ‘foo’, zakładając, że chodzi nam o użycie accessora (tzn. metody), bez self ruby pomyśli, że chcemy zadeklarować zmienną foo, a nie odwołać się do metody “foo=”
  • self nie używa się przy zmiennych - tu jest dość duża różnica w stosunku do javy, gdzie możemy użyć this.foo, gdzie foo to zmienna

Mam nadzieję, że nie zagmatwałem jeszcze bardziej :wink:

Ach, i przypomniało mi się jak w zamierzchłych czasach napisałem nocię w powiązanym temacie :wink:

http://piotrsarnacki.com/2008/11/15/to-self-or-not-to-self/