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 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
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.
[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
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
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 method
foo’ 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
Ach, i przypomniało mi się jak w zamierzchłych czasach napisałem nocię w powiązanym temacie