[ÇÖZÜLDÜ] Linux Paket Yükleme Uyarıları

Sorun: Linux’ta terminal aracılığıyla paket yüklerken “N: Geçersiz bir dosya uzantısı içerdiğinden … dizininde yoksayıldı.” uyarısının çıkması.
Çözüm:

$ sudo sh -c "echo 'Dir::Ignore-Files-Silently:: \"(.save|.distupgrade)$\";' > /etc/apt/apt.conf.d/99ignoresave"
Reklamlar

CHAOS Framework ile Payload Oluşturma ve Uzaktan Erişim

Chaos, payload oluşturarak uzak makinelere erişim sağlayan bir framework. Dosya indirme, dosya yükleme, fork bombing, işletim sistemi öğrenme gibi basit işlevleri yerine getirebiliyor. Kullanmak için önce UPX paketinin sistemde yüklü olması gerekli. Kurulum için aşağıdaki adımları takip edebilirsiniz:

# apt install golang upx-ucl -y
# git clone https://github.com/tiagorlampert/CHAOS.git
# cd CHAOS
# go run CHAOS.go

veya

# chmod +x CHAOS
./CHAOS

screenshot
Generate seçeneği ile payload oluşturun.

Screenshot from 2017-07-19 04-19-08

LHOST sorusuna kendi yerel IP adresinizi girin. LPORT seçeneğine 8080 portunu girebilirisiniz.

Screenshot from 2017-07-19 04-19-42

Payload adını da girdikten sonra Chaos size “payloadı UPX ile sıkıştırmak istiyor musunuz?” diye soracak buna [y] veya [n] ile cevap vererek devam edin.

Chaos dizinine giderek üretilen payloadı karşı makineye atın. Karşı taraf dosyayı çalıştırdıktan sonra ana menüdeki 2. seçenek yani Listen ile daha önce girdiğiniz port numarasını kullanarak dinleme yapabilirsiniz.

Screenshot from 2017-07-19 04-46-09

help komutuyla saldırı seçeneklerini görüntüleyebilirsiniz.

Screenshot from 2017-07-19 04-46-14

Chaos’un kötü yanı birçok noktada yetersiz kalması. Örneğin dosya indirme yapmak istiyorsanız indireceğiniz dosyanın nerede olduğunu bilmek zorundasınız. Chaos’ta dizin listeleme gibi bir özellik bulunmuyor. Veya kullanıcı adı gibi bilgilere sahip olmanız gerekiyor.

Arch Linux Kurulumu

Ben de sizin gibi Ubuntu’dan sıkıldım ve olaya biraz heyecan katmak için Arch kurmaya karar verdim. Ancak Arch, GUI tabanlı Linux dağıtımlarında olduğu kadar kolay bir kurulum süreci içermediğinden ve Türkçe yazılmış kaynakların yetersizliğinden dolayı kurulum aşamalarına dair aydınlatıcı olmasını umduğum bir yazı yazmaya koyuldum.

Arch, Linux alemine yeni dalanlar için iyi bir başlangıç değil ancak Linux hakkında biraz tecrübe kazandıysanız ve kendinize güveniniz tamsa oldukça iyi bir tercih olacaktır.

Linux üzerine tecrübe sahibi olmalısınız derken şaka yapmıyordum. Çünkü Arch size kurulum için GUI tabanlı bir arayüz sunmuyor. Her şey siyah ekran. Hatta Arch’ın kendisi bile! Hal böyle olunca da küçük bir destek lazım olabiliyor. Kuruluma başlamadan önce https://www.archlinux.org/download/ adresinden ISO dosyasını indirip, bir flash belleğe yazmalısınız. Bunu yaptığınızı varsayarsak flash bellek ile bilgisayarınızı başlattıktan sonra “Boot Arch Linux (i686)” seçeneğini seçerek kurulum adımlarına başlayabilirsiniz.

Öncelikle bilgisayarınızın bir internet ağına bağlı olması gerekir. Eğer kablolu bağlantı kullanıyorsanız zaten otomatik olarak bağlantı kurmuşsunuz demektir. Ancak kablosuz ağlara bağlanmak istiyorsanız şu adımları uygulamanız gerekiyor:

Öncelikle kablosuz ağ kartınızın arayüzünü tespit edelim.

$ iw dev

Interface yazan yer sizin ağ arayüzünüzü gösterir ve muhtemelen wlan0 gibi bir şeydir. Arayüz ismini öğrendikten sonra

$ iw dev arayüz link

yazarak ağ arayüzünün durumunu görüntüleyebilirsiniz. Şu an herhangi bir ağa bağlı olmadığınızdan olumlu bir sonuç alamayacaksınızdır. Arayüzü kullanmak için önce aktif hale getirmemiz gerekli. Bunun için

$ ip link set arayüz up

yazarak arayüzü aktif ediyoruz. Şimdi sıra yakınlardaki kablosuz ağları aramaya geldi.

$ iw dev arayüz scan | less

Çevremizdeki ağları tarayarak uzunca bir liste elde ettik. Burada SSID yazan kısımlar bağlanmak istediğimiz ağların isimleri. Bu isimler bağlantı kurarken bize lazım olacak. WPA/WPA2 ağlara bağlanmak için

$ wpa_supplicant -i arayüz -c <(wpa_passphrase "SSID değeri" "Şifre")

WEP ağlara bağlanmak için

$ iw dev arayüz connect "SSID değeri" key 0:Anahtar

Şifresiz ağlara bağlanmak için

$ iw dev arayüz connect "SSID değeri"

komutlarını kullanabilirsiniz. Başarılı bir şekilde bağlantı sağladığınızdan emin olmak için yukarıdaki gibi

$ iw dev arayüz link

komutunu kullanarak kontrol yapabilirsiniz. Kablosuz bir ağa bağlandık ancak henüz bir IP adresimiz yok. IP adresini DHCP sunucudan otomatik olarak alabilir veya kendimiz statik bir IP belirleyebiliriz. Ben otomatik IP nasıl alınır onu göstereceğim

$ dhcpcd arayüz

Burada dikkat etmeniz gereken nokta dhcpd yazmamış olmam. İlk bakışta öyle okunuyor ama dikkatli bakarsanız dhcpcd yazıyor. Bu hatayı ben de yaptığım için hatırlatma ihtiyacı hissettim. IP adresi de aldığımıza göre ağ ayarlarımız tamamlandı. Şimdi ileri aşamalara geçelim.

Önce mevcut disk bölümlerini görüntülemek için aşağıdaki komutu çalıştıralım

$ fdisk -l

Çıktıda diskinizin bölümleri, toplam boyutu, sektör alanları gibi bilgileri görüntüleyeceksiniz. Arch için yeni disk bölümleri oluşturmak üzere

$ cfdisk

komutunu çalıştıralım. cfdisk diskiniz ile ilgili bölme, biçimlendirme, silme ve yeni bölüm oluşturma gibi işlemleri yapmanıza yarayan bir disk yönetim aracıdır.

İlk önce mevcut disk bölümleriniz sizi karşılayacaktır. Burada silmek istediğiniz disk bölümünün üzerine gelip aşağıdaki seçeneklerden “Sil” seçerek ilgili bölüm içeriğini temizleyebilirsiniz. Ben tamamen boş bir disk olduğunu varsayacağım. Buna göre 3 adet disk alanı oluşturacağız. Bunlar

  • Root (kök) alanı için /dev/sda1, 20G (primary)
  • Swap (takas) alanı için /dev/sda2, 2*RAM (primary)
  • Mantıksal alan (home dizini) için /dev/sda5, kalan alan (extended)

Yeni bir alan oluşturmak için “Free space” bölümünde iken aşağıdaki seçeneklerden “New” seçerek oluşturmak istediğiniz alanın boyutunu belirtmeniz gerekir. Burada boyut için G (Gigabyte), M (Megabyte), K (Kilobyte) kullanmalısınız. Örneğin ben root alanı için 20 gigabyte ayıracaksam 20G şeklinde belirtmeliyim.

Burada değinmem gereken bir nokta da root ve swap alanlarının boyutlarının ne kadar olması gerektiği ile ilgili. Çoğu kişinin kafasında bu soru vardır. Arch Linux Wiki sayfasında yazdığına göre normal bir kullanıcı için root alanı 15-20 gigabyte kadar olabilir. Bölümlendirmeyle ilgili daha ayrıntılı bilgiye buradan ulaşabilirsiniz.

Swap alanı hakkında çeşitli rivayetler mevcut ama genel olarak RAM boyutunun 2 katı şeklinde swap alanı boyutu tercih ediliyor. Benim ulaştığım kaynaklarda 2G-8G arası RAM’de 2 katı, 8G-16G RAM’de aynı boyut, 16G> RAM’de en az 4G olması gerektiği şeklinde açıklamalara rastladım. Swap alanı ile ilgili daha çok bilgiye buradan ve buradan ulaşabilirsiniz.

Alan boyutuna karar verdikten sonra “Primary” ve “Extended” diye iki adet seçenek karşımıza çıkacak. Biz root ve swap alanı için primary, home dizini için extended seçeneğini seçeceğiz. İlave olarak root alanını oluşturduktan sonra alanın üzerindeyken aşağıdaki seçeneklerden “Bootable” seçerek bu alanın önyüklenebilir olmasını sağlamalısınız. Aksi takdirde işletim sistemi yüklenemez. Bunu yaptığınızda alanın “Boot” sekmesinde bir asterisk (*) işareti görmelisiniz.

Swap alanı için de bir bölüm oluşturup “Primary” seçeneğini seçtikten sonra ev dizinine sıra geldi. Ev dizinimiz için boyut sınırlaması yok. Diskinizde kalan tüm alanı buraya ayırabilirsiniz. Tek fark bu sefer “Extended” seçeneğini seçecek olmamız. Bunu yaptığınızda alanın altında bir bölüm daha açılacak ve buraya da bir önceki girdiğiniz boyutun aynısını yazarak ev dizini alanını da tamamlamış olacaksınız.

Disk bölümlendirme işlemi bittiğinde aşağıdaki seçeneklerden “Write” seçerek yaptığınız değişikliklerin diske yazılmasını sağlamalısınız. En son “Quit” seçeneğini seçerek programı terk edebilirsiniz.

Tekrar

$ fdisk -l

komutu ile yaptığınız disk bölümlerini görüntüleyerek kontrol edebilirsiniz. Emin olduktan sonra sıra disk alanlarını formatlama işlemine geldi. Root alanını formatlamak için

$ mkfs.ext4 /dev/sda1

komutunu, benzer şekilde ev dizini alanını formatlamak için

$ mkfs.ext4 /dev/sda5

komutunu kullanın. En son da swap alanını formatlamak için

$ mkswap /dev/sda2

komutunu kullanın. Swap alanını formatladıktan sonra aktif etmek için şu komutu da ayrıca girmelisiniz:

$ swapon /dev/sda2

Gerekli alanları oluşturduk ve formatlama işlemi gerçekleştirdik. Şimdi bu alanları mount edeceğiz. Yani yerleştireceğiz. Ben root alanını /mnt ye ev alanını /mnt/home a yerleştireceğim.

$ mount /dev/sda1 /mnt
$ mkdir /mnt/home
$ mount /dev/sda5 /mnt/home

Artık Arch Linux dosyalarını yükleyebiliriz. Bu dosyaları yüklemek internet hızınıza göre değişiklik gösterebilir.

$ pacstrap /mnt base base-devel

Yükleme işlemi tamamlandıktan sonra fstab dosyası oluşturmak için şu komutu girin

$ genfstab /mnt >> /mnt/etc/fstab

Düzgün çalıştığını kontrol etmek için

$ cat /mnt/etc/fstab

komutunu kullanabilirsiniz.

Arch sistemini yükledikten sonra şimdi de basit ayarlamaları yapalım.

$ arch-chroot /mnt /bin/bash

komutu ile yeni yüklediğimiz Arch Linux sistemine geçiş yapalım. Ardından sistem dili ayarlarını yapalım. Bunun için /etc/local.gen dosyasında değişiklik yapmamız gerekiyor.

$ vi /etc/local.gen

komutu ile dosyayı açın ve kullanmak istediğiniz dil satırının önündeki yorum işaretini kaldırın. Ben Türkçe kullanmak istediğimden dolayı #tr_TR.UTF-8 UTF-8 satırındaki sharp (#) işaretini kaldıracağım. Dosyayı kaydedip çıkın ve

$ locale-gen

ile yeni locale oluşturun. Bir /etc/locale.conf dosyası oluşturun ve dosyaya LANG=tr_TR.UTF-8 satırını ekleyin.

Diğer bir ayar da

$ passwd

ile root kullanıcısı için parola oluşturmaktır. Bunu da yaptıktan sonra kurulumun son aşamasına gelmiş bulunuyoruz. Buraya kadar yaptığımız küçük ayarlamalara hostname belirleme, saat ayarları gibi şeyler de eklenebilir ancak ben bunlara ihtiyaç duymadığımdan yazıya bunları almadım.

Sistemimizin önyüklemesi için GRUB kurulumu yapacağım. Bunun için

$ pacman -S grub os-prober
$ grub-install /dev/sda
$ grub-mkconfig -o /boot/grub/grub.cfg

komutlarını ayrı ayrı çalıştırın. Son olarak chroot’tan çıkın, alanları çözün (unmount) ve sistemi tekrar başlatın

$ exit
$ umount /mnt
$ umount /mnt/home
$ reboot

Kurulum işlemi tamamlandı. GRUB ekranından Arch Linux’a giriş yapabilirsiniz. Kurulum sırasında yaşadığım bir problemi ve çözümünü aşağıda bulabilirsiniz. Bunun dışında eksik veya yanlış bilgi varsa yorum yazarak veya yukarıdaki sosyal medya hesaplarından bana ulaşarak yazıya katkıda bulunabilirsiniz.

Grub Yüklemesinde “Failed to connect to lvmetad” Uyarısı

Bu gerçekten bir problem mi bilmiyorum ancak ben görmezden gelip geçmek yerine çözüm arayışına girdim ve İngilizce bir kaynakta bir şeyler buldum. Uyarının tamamı şu şekilde:

/run/lvm/lvmetad.socket: connect failed: No such file or directory
WARNING: Failed to connect to lvmetad. Falling back to internal scanning.

Çözüm: Chroot ortamından çıkın

$ exit
$ mkdir /mnt/hostrun
$ mount --bind /run /mnt/hostrun
$ arch-chroot /mnt /bin/bash
$ mkdir /run/lvm
$ mount --bind /hostrun/lvm /run/lvm

Alternatif olarak lvmetad ı /etc/lvm/lvm.conf dosyasından use_lvmetad=0 yaparak etkisiz hale getirerek grub yükleme aşamalarını tekrar edebilirsiniz.

Python OOP #3 – Classmethod ve Staticmethod

Nesne yerine sınıfa bağlı olan metodlara “class method” yani sınıf metodu denir. Parametreleri sınıflardan oluşur. Programımıza bir classmethod ekleyelim:

@classmethod
def set_raise_amount(cls, amount):
    cls.raise_amount = amount

Fark ettiyseniz metodu tanımlarken @classmethod etiketi kullandım. Bu kullanımı daha iyi anlamak için decoratorlarla ilgili olan yazımı inceleyebilirsiniz. Metodumu çağırırken normal bir fonksiyon çağırır gibi çağırıyorum.

Employee.set_raise_amount(1.05)

print(Employee.raise_amount)
print(emp1.raise_amount)

Çıktı iki tane 1.05 şeklinde olur çünkü artık sınıflarla çalışıyoruz. Bu nedenle sınıf değişkeninde yaptığım bir değişiklik nesneleri de etkileyecektir. Daha önceki yazımda bundan bahsetmiştim.

Fonksiyonun tanımını incelersem bizim alışık olduğumuz self yerine cls parametresine sahip olduğunu görürüm. Bu argüman olarak sınıf aldığını belirtmek için kullanılır. cls yerine başka bir şey de yazabilirdim ancak geleneksel kullanım bu şekilde.

Fonksiyonu çağırdığımda sınıf adı otomatik olarak cls parametresine verilir. İkinci parametre olan amount değişkeni sınıfa ait raise_amount değişkenine atanır.

Sınıf metodları çoklu nesne oluşturmak için de kullanılabilir. Bu örnekte string olarak verilen işçilere ait özellikler ile tek tek örnek oluşturmak yerine bir classmethod yardımıyla bu işlemi tek seferde yapıyoruz.

@classmethod
def from_string(cls, emp_str):
    fname, lname, pay = emp_str.split("-")
    return cls(fname, lname, pay)

emp3 = Employee.from_string("John-Doe-7000")

Eğer bu metodu kullanmasaydım şöyle bir tanımlama yapmam gerekecekti ve her bir string için ayrı ayrı örnekleme yapacaktım.

emp_str1 = "John-Doe-7000"
emp_str2 = "Steve-Smith-6000"
emp_str3 = "Jane-Doe-9000"

first, last, pay = emp_str1.split("-")

new_emp1 = Employee(first, last, pay)

Normal metodlar örnekleri ilk argüman olarak alır (self), sınıf metodları sınıfları ilk argüman olarak alır (cls), static metodlar hiçbirini ilk argüman olarak almaz. Yani oluşturduğunuz metod ne sınıf ne de nesne kullanmıyor sadece aldığı argümanlar ile ilgileniyorsa bu durumda static metodları kullanmalısınız.

Sınıf ve örneklerle hiçbir ilgisi olmayan is_weekday() metodu oluşturacağım:

@staticmethod
def is_workday(day):
    if day.weekday() == 5 or day.weekday() == 6:
        return False
    return True

Bu metod çalışmak için herhangi bir örneğe veya sınıfa ihtiyaç duymuyor. Sadece argüman olarak aldığı tarih değerinin çalışma günü olup olmadığını kontrol ediyor ve ona göre True veya False döndürüyor. Kodumuzun tamamı şu şekilde:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

class Employee():

    number_of_emps = 0
    raise_amount = 1.04

    def __init__(self, fname, lname, pay):
        self.fname = fname
        self.lname = lname
        self.pay = pay
        self.mail = fname + "." + lname + "@company.com"

        Employee.number_of_emps += 1

    def fullname(self):
        return self.fname + " " + self.lname

    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)

    @classmethod
    def set_raise_amount(cls, amount):
        cls.raise_amount = amount

    @classmethod
    def from_string(cls, emp_str):
        fname, lname, pay = emp_str.split("-")
        return cls(fname, lname, pay)

    @staticmethod
    def is_workday(day):
        if day.weekday() == 5 or day.weekday() == 6:
            return False
        return True

emp1 = Employee("Bob", "Hawkes", 5000)
emp2 = Employee("Alice", "Banas", 3500)

Employee.set_raise_amount(1.05)

print(Employee.raise_amount)
print(emp1.raise_amount)

emp3 = Employee.from_string("John-Doe-7000")
print(emp3.mail)

import datetime
date = datetime.date(2017, 6, 30)

print(Employee.is_workday(date))

Serinin diğer yazıları:
1 – Sınıflar ve Örnekleme
2 – Sınıf Değişkenleri

First-Class Function, Closure ve Decorator

Bu yazıyı hazırlarken Corey Schafer‘ın video derslerinden yararlandım.

Bir fonksiyonu değişkene atarsak ne olur? First class function (kaba bir çeviri olacak ama) -birinci sınıf fonksiyonlar- terimi bu işe yarıyor. Yani fonksiyonları değişkenlere atayıp değişkeni fonksiyon olarak kullanabiliyoruz. Şu örneği inceleyelim:

def square(num):
    print (num * num)

x = square
x(5)

Burada kendisine argüman olarak verilen sayının karesini ekrana basan bir fonksiyonum var. Bu fonksiyonu x değişkenine atıyorum. Ancak atama yaparken dikkat ettiyseniz parantez kullanmadım. Çünkü parantez kullanırsam atama işlemi yerine fonksiyonu koşmaya çalışır. Ben burada fonksiyonu çalıştırmadan bir nevi fonksiyonu değişkene kopyaladım.

Artık x değişkenini fonksiyon çağırır gibi kullanabilirim. Kodu çalıştırdığınızda ekrana 25 yazdırdığını görebilirsiniz.

Temelde closure ve decorator kavramları da buradan çıkıyor. Bunların hepsi birbiriyle ilişkili yapılar. Closure (çev. kapatma!) iç içe fonksiyonlarda içteki fonksiyonun dıştaki fonksiyon değişkenlerine erişimini sağlayan bir özellik. Burada da birinci sınıf fonksiyonları kullanacağız. İşte örnek:

def outer_func():
    message = "Penguen"
    
    def inner_func():
        print(message)

    return inner_func()

outer_func()

Kodu çalıştırdığımda neler oluyor bakalım. outer_func() ile dıştaki fonksiyonu çağırıyorum. Bu fonksiyonun içinde message adında bir değişken ve inner_func fonksiyonu yer alıyor. Geriye ise inner_func fonksiyonunu döndürüyor. inner_func fonksiyonunun içeriğine bakarsak message değişkenini ekrana yazdırdığını görürüz. Ancak aslında message değişkeni dıştaki fonksiyona ait.

Gerçekten de kodu çalıştırdığımda ekrana Penguen yazdığını görüyorum. Burada eğer return inner_func() satırını kullanmasaydım programım çalışmazdı. Closure yapısı bu açıdan bize büyük avantajlar sağlayabiliyor. Bir de şuna bakın:

def outer_func(msg):
    message = msg

    def inner_func():
        print(message)

    return inner_func

my_msg = outer_func("Penguen")
my_msg()

Bu örnekte de first-class yapısını kullandım. Sonuç aynı.

Decorator (Türkçe karşılığını bulamadım), argüman olarak fonksiyon alan ve aldığı fonksiyonu çalıştırıp geriye fonksiyon döndüren yapıdır. Biraz karışık oldu. Örnek kodla göstermeliyim.

def decorator_func(original_func):
    def wrapper_func():
        return original_func()
    return wrapper_func

def display():
    print("display function run")

my_display = decorator_func(display)
my_display()

display fonksiyonunu decorator_func fonksiyonuna argüman olarak verdim. wrapper_func fonksiyonu içinde display fonksiyonumu çalıştırdım ve geriye döndürdüm. Son olarak da wrapper_func fonksiyonumu geri döndürerek my_display değişkenine atadım. my_display değişkenini fonksiyon gibi çağırarak ekrana display function run yazısı yazdırdım. Bu kullanımı yazının başında göstermiştim.

Ancak bu kullanım şekli çok tercih edilmez. Bunun yerine decorator fonksiyonlar için “@” işareti kullanılır. Yani

def decorator_func(original_func):
    def wrapper_func():
        print("wrapper executed")
        return original_func()
    return wrapper_func

@decorator_func
def display():
    print("display function run")

my_display = decorator_func(display)
my_display()

Burada ek olarak wrapper_func fonksiyonuna bir işlev daha ekledim. Bunu @decorator_func tanımlamasınının farkını anlamak için test edeceğiz. Bu tanımlamayı yaptıktan sonra artık display fonksiyonunu kendi başına çağırabilirim. Yani şöyle bir kullanım da aynı sonucu verecektir:

def decorator_func(original_func):
    def wrapper_func():
        print("wrapper executed")
        return original_func()
    return wrapper_func

@decorator_func
def display():
    print("display function run")

display()

Burada çıktı

wrapper executed
display function run

şeklinde olur. Görüldüğü üzere @decorator_func tanımlamasını çıkarırsam herhangi bir ilişkilendirme olmaycağından ekrana sadece display function run yazılacaktır. Yani display fonksiyonu sıradan bir şekilde tek başına koşulur.

Decorator yapısı fonksiyonlar yerine sınıflar için de kullanılabilir.

class decorator_class(object):
    def __init__(self, original_func):
        self.original_func = original_func

    def __call__(self, *args, **kwargs):
        print("__call__ method executed")
        return self.original_func()

@decorator_class
def display():
    print("display function run")

display()

Farklı olarak burada __call__ metodunu kullandım. Bu metod önceki örnekte yer alan wrapper_func fonksiyonu gibi düşünülebilir. İsimler ve ufak değişikliklerle beraber bildiğimiz sınıf yapısıyla aynı.

Akılda kalan son soru işaretini cevaplayıp örnek bir kod vererek yazımı sonlandıracağım. Biz bunu nerede kullanacağız? Genelde loglama işlemleri, stream gerektiren uygulamalar, fonksiyon zamanlaması, erişim kontrolü gibi konularda decorator yapısı kullanılır. İşte örnek kod:

def my_logger(orig_func):
    import logging
    logging.basicConfig(filename = "{}.log".format(orig_func.__name__), level = logging.INFO)

    def wrapper(*args, **kwargs):
        logging.info("Ran with args: {}, and kwargs: {}".format(args, kwargs))
        return orig_func(*args, **kwargs)

    return wrapper

@my_logger
def display_info(name, age):
    print("display_info ran with arguments ({},{})".format(name, age))

display_info("John", 25)

Basit bir loglama işlemi yapan bu kodu çalıştırdığımda display_info.log adında bir dosya oluşturacak. Ve içine parametre olarak verdiğim argümanları kaydedecek. display_info() fonksiyonuna verdiğim argümanları değiştirip tekrar çalıştırdığımda yeni argümanları log dosyasına ekleyecektir. Log dosyası çıktısı şu şekilde olur:

INFO:root:Ran with args: ('John', 25), and kwargs: {}
INFO:root:Ran with args: ('Hank', 30), and kwargs: {}

Eksik veya hatalı bir bilgi varsa üstteki sosyal paylaşım linklerinden bana ulaşıp bildirim yapabilirsiniz.

Python OOP #2 – Sınıf Değişkenleri

apply_raise() adında yeni bir fonksiyon tanımlayalım. Bu fonksiyon işçilerin maaşlarını artıracak. Artırma miktarını raise_amount değişkeninde saklayacağım.

class Employee():

    number_of_emps = 0
    raise_amount = 1.04

    def __init__(self, fname, lname, pay):
        self.fname = fname
        self.lname = lname
        self.pay = pay
        self.mail = fname + "." + lname + "@company.com"

        Employee.number_of_emps += 1

    def fullname(self):
        return self.fname + " " + self.lname

    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)

emp1 = Employee("Bob", "Hawkes", 5000)
emp2 = Employee("Alice", "Banas", 3500)

Eğer

print(emp1.pay)
emp1.apply_raise()
print(emp1.pay)

dersem çıktı 5000 ve 5200 şeklinde olacaktır. Burada tanımladığımız fonksiyon yardımıyla pay değişkenini değiştirdik. apply_raise() fonksiyonu tanımlamasını şu şekilde de yapabilirdik:

def apply_raise(self):
        self.pay = int(self.pay * Employee.raise_amount)

Burada raise_amount değişkenine erişmek için sınıf adını kullandık. Çünkü sınıf değişkenlerine sınıf veya nesne ile erişilebilir. Her iki kullanımda doğrudur.

Peki

Employee.raise_amount=1.05

print(emp1.raise_amount)
print(Employee.raise_amount)

yazdığımda çıktı ne olurdu? Burada açıkça görüldüğü gibi değişkeni sınıfla kullandığım zaman değişkenin yeni değeri o sınıftan türeyen tüm nesneleri etkileyecektir. Yani çıktı iki tane 1.05 olur. Ancak

emp1.raise_amount=1.05

print(emp1.raise_amount)
print(Employee.raise_amount)

yazdığımda bu çağırım sadece emp1 nesnesini etkileyecektir. Yani çıktı 1.05 ve 1.04 olur.

Sınıfımıza bir de number_of_emps değişkeni ekledim. Başlangıç fonksiyonunu her çağırdığımda bu değer bir artırılacak böylece toplam işçi sayısını saklamız olacağız. Bunu test etmek için şöyle bir kod parçacığı yazılabilir:

print(Employee.number_of_emps)

emp1 = Employee("Bob", "Hawkes", 5000)
emp2 = Employee("Alice", "Banas", 3500)

print(Employee.number_of_emps)

Çıktıda ne elde ettiniz?