#class & method

class Musician():

job = "musician" #bu şekilde default bir değer atayabiliriz. bu attribute obje oluşturup çağırırken değer atamamıza gerek kalmaz çünkü default değeri var.

def __init__(self,name,age,instrument): # __init__ özel bir metoddur bir instance (nesne,obje vb) oluşturulduğunda ilk çağrılan metod budur.
self.name = name #kullanıcıdan alınan name'i alıp burda ki self'in name attribute'ne eşitle. yani __init__ metodunda ki self'i referans olarak kullanıyoruz.
self.age = age
self.instrument = instrument

#method

def sing(self): # burada ki "self" metodumuzu instance'a bağlayan bir kelime gibidir. yani bu class'a ait bir obje oluşturulduğunda bu self sayesinde o metodu kullanabilir

print(f"singing.... {self.instrument}")

my_musician = Musician("be", 44, "guitar")

print(my_musician.age)

print(my_musician.name)

my_musician.name = "ab"

print(my_musician.name)

my_musician.instrument = "piano"

print(my_musician.instrument)

print(my_musician.job)

print(my_musician.sing())

class DogYear():

year_factor = 7

def __init__(self,age=5): # age'e bir değer vererek default bir değer atamış olduk
self.age = age
self.name = "Bar" # bu şekilde kullanıcıya sormadan yani input almadan da kendimiz istediğimiz şekilde attribute verebiliriz. bu da demek oluyor ki __init__ fonksiyonu
# altında oluşturduğumuz her attribute (ister kullanıcdan alınsın ister kendimiz oluşturalım) bir şekilde mutlaka oluşturulacaktır hem de program hata vermemiş olur

def calculation(self):
return self.age * self.year_factor # "self.year_factor" yerine "DogYear.year_factor" da diyebilirdik

my_dog = DogYear(3)

print(my_dog.age)
print(my_dog.calculation())

my_dog2 = DogYear() # age'e verdiğimiz default deger olduğu için input girmemize gerek kalmadı

print(my_dog2.age)
print(my_dog2.calculation())