შიგთავსზე გადასვლა

🛡️ შეცდომები და Exception Handling Python-ში

📑 სარჩევი

  1. შესავალი
  2. შეცდომების ტიპები
  3. Try-Except
  4. Else და Finally
  5. With-As Statement
  6. Custom Exceptions
  7. პრაქტიკული ამოცანები

🔰 შესავალი

Exception Handling (შეცდომების დამუშავება) არის მექანიზმი, რომელიც საშუალებას გვაძლევს გავუმკლავდეთ პროგრამის შესრულების დროს წარმოქმნილ შეცდომებს ისე, რომ პროგრამა არ გაჩერდეს უეცრად.

🎯 რატომ არის ეს მნიშვნელოვანი?

პროგრამის სტაბილურობა - თავიდან ავიცილოთ უეცარი გაჩერება
მომხმარებლის გამოცდილება - მივცეთ გასაგები შეტყობინებები
დებაგინგი - ვიპოვოთ და გავასწოროთ შეცდომები
რესურსების მართვა - ფაილები, კავშირები სწორად დაიხუროს


⚠️ შეცდომების ტიპები

Python-ში შეცდომები იყოფა სამ ძირითად კატეგორიად:

1️⃣ Syntax Errors (სინტაქსის შეცდომები)

კოდი არასწორად არის დაწერილი და პროგრამა საერთოდ არ გაეშვება.

# ❌ არასწორი სინტაქსი
if x > 5
    print("x დიდია")  # დაკარგულია :

# ❌ დაკარგული ბრჭყალები
print(Hello World)

# ❌ არასწორი ინდენტაცია
def my_func():
print("test")

2️⃣ Runtime Errors / Exceptions (გაშვების შეცდომები)

კოდი სწორად არის დაწერილი, მაგრამ შესრულებისას ხდება შეცდომა.

# ❌ ZeroDivisionError
result = 10 / 0

# ❌ TypeError
number = "10" + 5

# ❌ IndexError
my_list = [1, 2, 3]
print(my_list[10])

# ❌ KeyError
my_dict = {"name": "Ana"}
print(my_dict["age"])

# ❌ FileNotFoundError
file = open("არსებული_ფაილი.txt")

3️⃣ Logical Errors (ლოგიკური შეცდომები)

პროგრამა მუშაობს, მაგრამ არასწორ შედეგს იძლევა.

# პროგრამა მუშაობს, მაგრამ ლოგიკა არასწორია
def calculate_average(numbers):
    return sum(numbers) / len(numbers) + 1  # ❌ +1 ზედმეტია

🔧 ძირითადი Exception-ების ტიპები

Exception აღწერა მაგალითი
SyntaxError სინტაქსის შეცდომა if x > 5 (: დაკარგული)
ZeroDivisionError ნულზე გაყოფა 10 / 0
TypeError არასწორი ტიპი "hello" + 5
ValueError არასწორი მნიშვნელობა int("abc")
IndexError ინდექსი დიაპაზონს გარეთ list[100]
KeyError key არ არსებობს dict["missing_key"]
NameError ცვლადი არ არსებობს print(x) (x არ არის განსაზღვრული)
FileNotFoundError ფაილი ვერ მოიძებნა open("nonexistent.txt")
AttributeError ობიექტს არ აქვს ატრიბუტი "hello".append()
ImportError მოდული ვერ იმპორტირდა import nonexistent_module

🛠️ Try-Except

📌 ძირითადი სტრუქტურა

try:
    # კოდი რომელიც შეიძლება გამოიწვიოს შეცდომა
    risky_code()
except:
    # რა მოხდეს შეცდომის შემთხვევაში
    print("მოხდა შეცდომა!")

💡 მარტივი მაგალითი

try:
    number = int(input("შეიყვანე რიცხვი: "))
    result = 100 / number
    print(f"შედეგი: {result}")
except:
    print("რაღაც არასწორია!")

🎯 კონკრეტული Exception-ების დაჭერა

try:
    number = int(input("შეიყვანე რიცხვი: "))
    result = 100 / number
    print(f"შედეგი: {result}")

except ValueError:
    print("❌ შეიყვანე რიცხვი, არა ტექსტი!")

except ZeroDivisionError:
    print("❌ ნულზე გაყოფა შეუძლებელია!")

except Exception as e:
    print(f"❌ უცნობი შეცდომა: {e}")

📋 შეცდომის ინფორმაციის მიღება

try:
    x = 10 / 0
except ZeroDivisionError as error:
    print(f"შეცდომა: {error}")
    print(f"შეცდომის ტიპი: {type(error).__name__}")

🔗 რამდენიმე Exception-ის დაჭერა ერთდროულად

# მეთოდი 1: ცალ-ცალკე except
try:
    # რისკიანი კოდი
    pass
except ValueError:
    print("Value Error!")
except TypeError:
    print("Type Error!")
except KeyError:
    print("Key Error!")

# მეთოდი 2: ერთ except-ში (tuple)
try:
    # რისკიანი კოდი
    pass
except (ValueError, TypeError, KeyError):
    print("მოხდა ერთ-ერთი ამ შეცდომებიდან!")

# მეთოდი 3: კონკრეტული + ზოგადი
try:
    # რისკიანი კოდი
    pass
except ValueError:
    print("ValueError!")
except Exception as e:
    print(f"სხვა შეცდომა: {e}")

🔄 Else და Finally

📌 სრული სტრუქტურა

try:
    # კოდი რომელიც შეიძლება გამოიწვიოს შეცდომა
    risky_code()

except SomeException:
    # თუ მოხდა შეცდომა
    handle_error()

else:
    # თუ შეცდომა არ მოხდა
    success_code()

finally:
    # ყოველთვის შესრულდება (შეცდომა იყო თუ არა)
    cleanup_code()

💡 Else - წარმატების შემთხვევაში

else ბლოკი მხოლოდ მაშინ შესრულდება, თუ try ბლოკში არანაირი შეცდომა არ მოხდა.

try:
    number = int(input("შეიყვანე რიცხვი: "))
    result = 100 / number

except ValueError:
    print("❌ არასწორი ფორმატი!")

except ZeroDivisionError:
    print("❌ ნულზე გაყოფა არ შეიძლება!")

else:
    # ეს მხოლოდ წარმატების შემთხვევაში შესრულდება
    print(f"✅ შედეგი: {result}")
    print("✅ ოპერაცია წარმატებით დასრულდა!")

🔒 Finally - დასუფთავების კოდი

finally ბლოკი ყოველთვის შესრულდება, შეცდომა მოხდა თუ არა.

try:
    file = open("data.txt", "r")
    content = file.read()
    print(content)

except FileNotFoundError:
    print("❌ ფაილი ვერ მოიძებნა!")

finally:
    # ეს კოდი ყოველთვის შესრულდება
    try:
        file.close()
        print("✅ ფაილი დაიხურა")
    except:
        print("ℹ️ ფაილი უკვე დახურულია ან არ არსებობს")

🎯 სრული მაგალითი - ყველა ბლოკი

def safe_divide(a, b):
    """უსაფრთხო გაყოფა დეტალური ლოგირებით"""

    try:
        print(f"🔄 ვცდილობ გავყოფო {a} / {b}")
        result = a / b

    except ZeroDivisionError:
        print("❌ შეცდომა: ნულზე გაყოფა!")
        return None

    except TypeError:
        print("❌ შეცდომა: არასწორი ტიპი!")
        return None

    else:
        print("✅ გაყოფა წარმატებით შესრულდა")
        return result

    finally:
        print("🏁 ოპერაცია დასრულდა
")

# ტესტები
print(safe_divide(10, 2))      # ✅ მუშაობს
print(safe_divide(10, 0))      # ❌ ZeroDivisionError
print(safe_divide("10", 2))    # ❌ TypeError

გამოსავალი:

🔄 ვცდილობ გავყოფო 10 / 2
✅ გაყოფა წარმატებით შესრულდა
🏁 ოპერაცია დასრულდა

5.0

🔄 ვცდილობ გავყოფო 10 / 0
❌ შეცდომა: ნულზე გაყოფა!
🏁 ოპერაცია დასრულდა

None


📁 With-As Statement

📌 რა არის With-As?

with სტეიტმენტი ავტომატურად უზრუნველყოფს რესურსების სწორ მართვას (ფაილები, კავშირები და ა.შ.).

🔑 ძირითადი უპირატესობები:

✅ ავტომატურად ხურავს ფაილს
✅ მუშაობს შეცდომის შემთხვევაშიც
✅ უფრო სუფთა და წაკითხვადი კოდი
✅ არ სჭირდება finally ბლოკი


📖 ფაილებთან მუშაობა - ძველი vs ახალი

❌ ძველი მეთოდი (try-finally)

# რთული და გრძელი
try:
    file = open("data.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("ფაილი ვერ მოიძებნა")
finally:
    try:
        file.close()
    except:
        pass

✅ ახალი მეთოდი (with-as)

# მარტივი და უსაფრთხო
try:
    with open("data.txt", "r") as file:
        content = file.read()
        print(content)
    # ფაილი ავტომატურად დაიხურა!
except FileNotFoundError:
    print("ფაილი ვერ მოიძებნა")

💡 With-As - პრაქტიკული მაგალითები

1️⃣ ფაილის წაკითხვა

# ტექსტური ფაილის წაკითხვა
try:
    with open("students.txt", "r", encoding="utf-8") as file:
        for line in file:
            print(line.strip())
except FileNotFoundError:
    print("❌ ფაილი students.txt ვერ მოიძებნა")

2️⃣ ფაილში ჩაწერა

# ახალი ფაილის შექმნა და ჩაწერა
try:
    with open("output.txt", "w", encoding="utf-8") as file:
        file.write("გამარჯობა, მსოფლიო!
")
        file.write("Python-ით ვწერთ ფაილში
")
    print("✅ ფაილი წარმატებით ჩაიწერა")
except IOError as e:
    print(f"❌ ფაილში ჩაწერის შეცდომა: {e}")

3️⃣ ფაილში დამატება (append)

# არსებულ ფაილში ინფორმაციის დამატება
try:
    with open("log.txt", "a", encoding="utf-8") as file:
        file.write("ახალი ლოგი: ოპერაცია შესრულდა
")
    print("✅ ლოგი დაემატა")
except Exception as e:
    print(f"❌ შეცდომა: {e}")

4️⃣ ორი ფაილი ერთდროულად

# ერთი ფაილიდან მეორეში კოპირება
try:
    with open("source.txt", "r") as source, \
         open("destination.txt", "w") as dest:

        content = source.read()
        dest.write(content)

    print("✅ ფაილი გადაკოპირდა")

except FileNotFoundError:
    print("❌ წყარო ფაილი ვერ მოიძებნა")
except IOError as e:
    print(f"❌ I/O შეცდომა: {e}")

5️⃣ JSON ფაილებთან მუშაობა

import json

# JSON-ის წაკითხვა
try:
    with open("data.json", "r", encoding="utf-8") as file:
        data = json.load(file)
        print(f"მონაცემები: {data}")
except FileNotFoundError:
    print("❌ JSON ფაილი ვერ მოიძებნა")
except json.JSONDecodeError:
    print("❌ არასწორი JSON ფორმატი")

# JSON-ში ჩაწერა
student = {
    "name": "ანა",
    "age": 20,
    "grades": [95, 88, 92]
}

try:
    with open("student.json", "w", encoding="utf-8") as file:
        json.dump(student, file, ensure_ascii=False, indent=4)
    print("✅ JSON ფაილი შეიქმნა")
except Exception as e:
    print(f"❌ შეცდომა: {e}")

🎯 With-As სხვა რესურსებთან

# მონაცემთა ბაზასთან კავშირი
import sqlite3

try:
    with sqlite3.connect("database.db") as connection:
        cursor = connection.cursor()
        cursor.execute("SELECT * FROM users")
        results = cursor.fetchall()
        # კავშირი ავტომატურად დაიხურება
except sqlite3.Error as e:
    print(f"❌ ბაზის შეცდომა: {e}")

# HTTP მოსაზრება
import requests

try:
    with requests.get("https://api.example.com") as response:
        data = response.json()
        print(data)
except requests.RequestException as e:
    print(f"❌ მოთხოვნის შეცდომა: {e}")

🚀 Raise - შეცდომის გენერირება

📌 რას ნიშნავს Raise?

raise საშუალებას გვაძლევს ჩვენ თავად "გავაგდოთ" შეცდომა.

def check_age(age):
    if age < 0:
        raise ValueError("❌ ასაკი არ შეიძლება იყოს უარყოფითი!")
    if age > 150:
        raise ValueError("❌ ასაკი ძალიან დიდია!")
    return f"✅ ასაკი სწორია: {age}"

# ტესტი
try:
    print(check_age(25))   # ✅ მუშაობს
    print(check_age(-5))   # ❌ ValueError
except ValueError as e:
    print(e)

🔄 Re-raise - შეცდომის გადაგდება

def process_data(data):
    try:
        result = 100 / data
        return result
    except ZeroDivisionError as e:
        print("⚠️ ვლოგავ შეცდომას...")
        raise  # შეცდომას გადავაგდებთ მაღლა

# გამოყენება
try:
    process_data(0)
except ZeroDivisionError:
    print("❌ ვიპოვე შეცდომა მთავარ კოდში!")

🎨 Custom Exceptions - საკუთარი შეცდომები

📌 რატომ გვჭირდება?

როცა Python-ის სტანდარტული exception-ები არ არის საკმარისი.

# საკუთარი exception-ის შექმნა
class NegativeNumberError(Exception):
    """შეცდომა უარყოფითი რიცხვისთვის"""
    pass

class TooLargeNumberError(Exception):
    """შეცდომა ძალიან დიდი რიცხვისთვის"""
    def __init__(self, number, max_value):
        self.number = number
        self.max_value = max_value
        super().__init__(f"რიცხვი {number} აღემატება მაქსიმუმს {max_value}")

# გამოყენება
def validate_score(score):
    if score < 0:
        raise NegativeNumberError("ქულა არ შეიძლება იყოს უარყოფითი!")
    if score > 100:
        raise TooLargeNumberError(score, 100)
    return f"✅ ქულა: {score}"

# ტესტი
try:
    print(validate_score(95))   # ✅
    print(validate_score(150))  # ❌
except NegativeNumberError as e:
    print(f"❌ {e}")
except TooLargeNumberError as e:
    print(f"❌ {e}")
    print(f"   მოცემული: {e.number}, მაქსიმუმი: {e.max_value}")

🏗️ Exception-ების იერარქია

class ValidationError(Exception):
    """ზოგადი ვალიდაციის შეცდომა"""
    pass

class EmailValidationError(ValidationError):
    """ემაილის ვალიდაციის შეცდომა"""
    pass

class PhoneValidationError(ValidationError):
    """ტელეფონის ვალიდაციის შეცდომა"""
    pass

# გამოყენება
def validate_email(email):
    if "@" not in email:
        raise EmailValidationError(f"არასწორი ემაილი: {email}")

def validate_phone(phone):
    if not phone.isdigit():
        raise PhoneValidationError(f"არასწორი ტელეფონი: {phone}")

# დაჭერა
try:
    validate_email("invalid_email")
except ValidationError as e:  # დაიჭერს ყველა ValidationError-ს
    print(f"❌ ვალიდაციის შეცდომა: {e}")

🎯 Best Practices - საუკეთესო პრაქტიკები

✅ რა უნდა გავაკეთოთ

# 1. დაჭირეთ კონკრეტული exception-ები
try:
    value = int(input("რიცხვი: "))
except ValueError:  # ✅ კონკრეტული
    print("არასწორი ფორმატი")

# 2. გამოიყენეთ with-as ფაილებისთვის
with open("data.txt") as file:  # ✅ ავტომატური დახურვა
    content = file.read()

# 3. მოკლე try ბლოკები
try:
    risky_operation()  # ✅ მხოლოდ რისკიანი კოდი
except Exception:
    handle_error()

# 4. ლოგირება
import logging
try:
    risky_code()
except Exception as e:
    logging.error(f"შეცდომა: {e}")  # ✅ ლოგავთ შეცდომას

❌ რა არ უნდა გავაკეთოთ

# 1. ცარიელი except
try:
    something()
except:  # ❌ ძალიან ზოგადი
    pass

# 2. except Exception as e და მისი დაიგნორება
try:
    something()
except Exception as e:  # ❌ ვიჭერთ მაგრამ არაფერს ვაკეთებთ
    pass

# 3. ბევრი კოდი try-ში
try:  # ❌ ძალიან გრძელი try
    code1()
    code2()
    code3()
    # ... 50 სტრიქონი
except:
    pass

# 4. ყველაფრის დამალვა
try:
    important_operation()
except:  # ❌ შეცდომა გაქრა, არ ვიცით რა მოხდა
    print("რაღაც არ გამოვიდა")

🧩 პრაქტიკული ამოცანები

📝 ამოცანები (1-30)

A. ძირითადი Try-Except (1-10)

  1. მარტივი დაცვა: შექმენი პროგრამა რომელიც ითხოვს რიცხვს და იჭერს ValueError-ს
  2. გაყოფა: დაწერე ფუნქცია რომელიც ორ რიცხვს ყოფს და იჭერს ZeroDivisionError-ს
  3. List Index: სიიდან ელემენტის ამოღება try-except-ით (IndexError)
  4. Dictionary Key: dictionary-დან value-ის ამოღება try-except-ით (KeyError)
  5. Type Conversion: str → int კონვერტაცია შეცდომის დაჭერით
  6. Multiple Exceptions: კოდი რომელიც იჭერს ValueError და TypeError-ს
  7. Exception Info: დაბეჭდე შეცდომის ტიპი და შეტყობინება
  8. Safe Input: შექმენი ფუნქცია safe_input რომელიც განმეორდება სანამ მომხმარებელი სწორ რიცხვს არ შეიყვანს
  9. Calculator: უსაფრთხო კალკულატორი რომელიც ყველა შესაძლო შეცდომას დაიჭერს
  10. List Access: ფუნქცია რომელიც დაიჭერს IndexError-ს და დააბრუნებს None

B. Else და Finally (11-15)

  1. Else Usage: გამოიყენე try-except-else სტრუქტურა წარმატების შეტყობინებით
  2. Finally Cleanup: გამოიყენე finally რათა დარწმუნდე რომ კოდი შესრულდა
  3. File + Finally: ფაილის წაკითხვა finally ბლოკით დასახურად
  4. Complete Structure: გამოიყენე try-except-else-finally ერთდროულად
  5. Resource Management: სიმულაცია გაუკეთე რესურსის გახსნას და დახურვას

C. With-As Statement (16-20)

  1. File Reading: წაიკითხე ფაილი with-as-ით
  2. File Writing: ჩაწერე მონაცემები ფაილში with-as-ით
  3. Multiple Files: წაიკითხე ერთი ფაილი და ჩაწერე მეორეში
  4. JSON Read: წაიკითხე JSON ფაილი with-as და try-except-ით
  5. JSON Write: შექმენი dictionary და ჩაწერე JSON ფაილად

D. Raise და Custom Exceptions (21-25)

  1. Raise ValueError: ფუნქცია რომელიც raise-ით გააგდებს ValueError-ს არასწორი input-ისთვის
  2. Age Validator: ასაკის ვალიდატორი რომელიც გააგდებს შეცდომას უარყოფითი რიცხვისთვის
  3. Custom Exception: შექმენი NegativeNumberError exception
  4. Password Validator: შექმენი WeakPasswordError და გამოიყენე ვალიდაციისთვის
  5. Re-raise: დაწერე კოდი რომელიც დაიჭერს შეცდომას, დაილოგავს და გადააგდებს
# 1. მარტივი დაცვა
try:
    number = int(input("შეიყვანე რიცხვი: "))
    print(f"შენ შეიყვანე: {number}")
except ValueError:
    print("❌ ეს არ არის რიცხვი!")

# 2. უსაფრთხო გაყოფა
def safe_divide(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("❌ ნულზე გაყოფა შეუძლებელია!")
        return None

print(safe_divide(10, 2))  # 5.0
print(safe_divide(10, 0))  # None

# 3. List Index
def get_list_item(lst, index):
    try:
        return lst[index]
    except IndexError:
        print(f"❌ ინდექსი {index} დიაპაზონს გარეთაა!")
        return None

my_list = [1, 2, 3]
print(get_list_item(my_list, 1))   # 2
print(get_list_item(my_list, 10))  # None

# 4. Dictionary Key
def get_dict_value(dictionary, key):
    try:
        return dictionary[key]
    except KeyError:
        print(f"❌ Key '{key}' არ არსებობს!")
        return None

student = {"name": "ანა", "age": 20}
print(get_dict_value(student, "name"))   # "ანა"
print(get_dict_value(student, "grade"))  # None

# 5. Type Conversion
def safe_int_convert(value):
    try:
        return int(value)
    except ValueError:
        print(f"❌ '{value}' ვერ გარდაიქმნა რიცხვად!")
        return None

print(safe_int_convert("123"))   # 123
print(safe_int_convert("abc"))   # None

# 6. Multiple Exceptions
def process_input(value, divisor):
    try:
        number = int(value)
        result = 100 / number
        return result
    except ValueError:
        print("❌ ValueError: არასწორი ფორმატი!")
    except ZeroDivisionError:
        print("❌ ZeroDivisionError: ნულზე გაყოფა!")
    except TypeError:
        print("❌ TypeError: არასწორი ტიპი!")
    return None

# 7. Exception Info
def show_exception_info():
    try:
        x = 10 / 0
    except Exception as e:
        print(f"შეცდომის ტიპი: {type(e).__name__}")
        print(f"შეტყობინება: {e}")

show_exception_info()

# 8. Safe Input
def safe_input(prompt):
    while True:
        try:
            value = int(input(prompt))
            return value
        except ValueError:
            print("❌ შეიყვანე მხოლოდ რიცხვი! სცადე თავიდან.")

# გამოყენება:
# age = safe_input("შენი ასაკი: ")

# 9. უსაფრთხო კალკულატორი
def calculator():
    try:
        num1 = float(input("პირველი რიცხვი: "))
        operator = input("ოპერატორი (+, -, *, /): ")
        num2 = float(input("მეორე რიცხვი: "))

        if operator == "+":
            result = num1 + num2
        elif operator == "-":
            result = num1 - num2
        elif operator == "*":
            result = num1 * num2
        elif operator == "/":
            result = num1 / num2
        else:
            print("❌ არასწორი ოპერატორი!")
            return

        print(f"✅ შედეგი: {result}")

    except ValueError:
        print("❌ შეიყვანე რიცხვები!")
    except ZeroDivisionError:
        print("❌ ნულზე გაყოფა შეუძლებელია!")

# 10. უსაფრთხო List წვდომა
def safe_list_access(lst, index):
    try:
        return lst[index]
    except IndexError:
        return None
    except TypeError:
        print("❌ ინდექსი უნდა იყოს integer!")
        return None

numbers = [10, 20, 30]
print(safe_list_access(numbers, 1))    # 20
print(safe_list_access(numbers, 10))   # None


#B. Else და Finally (11-15)


# 11. Else Usage
def divide_with_success(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("❌ ნულზე გაყოფა!")
    else:
        print(f"✅ წარმატებით გაიყო: {result}")
        return result
    return None

divide_with_success(10, 2)  # ✅ წარმატებით გაიყო: 5.0
divide_with_success(10, 0)  # ❌ ნულზე გაყოფა!

# 12. Finally Cleanup
def process_with_cleanup():
    try:
        print("🔄 დამუშავება იწყება...")
        result = 10 / 2
        print(f"✅ შედეგი: {result}")
    except Exception as e:
        print(f"❌ შეცდომა: {e}")
    finally:
        print("🧹 დასუფთავება დასრულდა")

process_with_cleanup()

# 13. File + Finally
def read_file_with_finally(filename):
    file = None
    try:
        file = open(filename, "r", encoding="utf-8")
        content = file.read()
        print(content)
    except FileNotFoundError:
        print(f"❌ ფაილი {filename} ვერ მოიძებნა!")
    finally:
        if file:
            file.close()
            print("✅ ფაილი დაიხურა")

# 14. სრული სტრუქტურა
def complete_example(filename):
    try:
        with open(filename, "r") as file:
            data = file.read()
            number = int(data)
    except FileNotFoundError:
        print("❌ ფაილი ვერ მოიძებნა!")
    except ValueError:
        print("❌ ფაილში არ არის რიცხვი!")
    else:
        print(f"✅ წარმატებით წაიკითხა რიცხვი: {number}")
    finally:
        print("🏁 ოპერაცია დასრულდა")

# 15. Resource Management სიმულაცია
class Resource:
    def __init__(self, name):
        self.name = name
        self.is_open = False

    def open(self):
        print(f"🔓 {self.name} გაიხსნა")
        self.is_open = True

    def close(self):
        print(f"🔒 {self.name} დაიხურა")
        self.is_open = False

def use_resource():
    resource = Resource("Database Connection")
    try:
        resource.open()
        # რაღაც ოპერაციები
        print("💾 ვმუშაობ რესურსთან...")
    finally:
        resource.close()

use_resource()

#C. With-As Statement (16-20)

# 16. File Reading
try:
    with open("data.txt", "r", encoding="utf-8") as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print("❌ ფაილი ვერ მოიძებნა!")

# 17. File Writing
data = "გამარჯობა Python!
ეს არის ტესტი."

try:
    with open("output.txt", "w", encoding="utf-8") as file:
        file.write(data)
    print("✅ ფაილი წარმატებით ჩაიწერა!")
except IOError as e:
    print(f"❌ ჩაწერის შეცდომა: {e}")

# 18. Multiple Files - კოპირება
try:
    with open("source.txt", "r", encoding="utf-8") as source:
        with open("destination.txt", "w", encoding="utf-8") as dest:
            content = source.read()
            dest.write(content)
    print("✅ ფაილი გადაკოპირდა!")
except FileNotFoundError:
    print("❌ წყარო ფაილი ვერ მოიძებნა!")
except IOError as e:
    print(f"❌ I/O შეცდომა: {e}")

# 19. JSON Read
import json

try:
    with open("config.json", "r", encoding="utf-8") as file:
        config = json.load(file)
        print(f"✅ კონფიგურაცია: {config}")
except FileNotFoundError:
    print("❌ config.json ვერ მოიძებნა!")
except json.JSONDecodeError:
    print("❌ არასწორი JSON ფორმატი!")

# 20. JSON Write
student_data = {
    "name": "გიორგი",
    "age": 21,
    "grades": [95, 88, 92],
    "faculty": "Computer Science"
}

try:
    with open("student.json", "w", encoding="utf-8") as file:
        json.dump(student_data, file, ensure_ascii=False, indent=4)
    print("✅ JSON ფაილი შეიქმნა!")
except IOError as e:
    print(f"❌ შეცდომა: {e}")


#D. Raise და Custom Exceptions (21-25)

# 21. Raise ValueError
def validate_positive(number):
    if number < 0:
        raise ValueError("რიცხვი უნდა იყოს დადებითი!")
    return number

try:
    print(validate_positive(10))   # ✅ 10
    print(validate_positive(-5))   # ❌ ValueError
except ValueError as e:
    print(f"❌ {e}")

# 22. Age Validator
def validate_age(age):
    if age < 0:
        raise ValueError("ასაკი არ შეიძლება იყოს უარყოფითი!")
    if age > 150:
        raise ValueError("ასაკი ძალიან დიდია!")
    if not isinstance(age, int):
        raise TypeError("ასაკი უნდა იყოს integer!")
    return f"✅ ასაკი სწორია: {age}"

try:
    print(validate_age(25))    # ✅
    print(validate_age(-5))    # ❌
except (ValueError, TypeError) as e:
    print(f"❌ {e}")

# 23. Custom Exception
class NegativeNumberError(Exception):
    """შეცდომა უარყოფითი რიცხვისთვის"""
    pass

def check_positive(number):
    if number < 0:
        raise NegativeNumberError(f"რიცხვი {number} არის უარყოფითი!")
    return number

try:
    check_positive(10)   # ✅
    check_positive(-5)   # ❌
except NegativeNumberError as e:
    print(f"❌ {e}")

# 24. Password Validator
class WeakPasswordError(Exception):
    """სუსტი პაროლის შეცდომა"""
    def __init__(self, message, issues):
        super().__init__(message)
        self.issues = issues

def validate_password(password):
    issues = []

    if len(password) < 8:
        issues.append("პაროლი უნდა იყოს მინიმუმ 8 სიმბოლო")
    if not any(c.isupper() for c in password):
        issues.append("პაროლი უნდა შეიცავდეს დიდ ასოს")
    if not any(c.isdigit() for c in password):
        issues.append("პაროლი უნდა შეიცავდეს ციფრს")

    if issues:
        raise WeakPasswordError("პაროლი ძალიან სუსტია!", issues)

    return "✅ პაროლი ძლიერია!"

try:
    print(validate_password("MyPass123"))  # ✅
    print(validate_password("weak"))       # ❌
except WeakPasswordError as e:
    print(f"❌ {e}")
    print("პრობლემები:")
    for issue in e.issues:
        print(f"  - {issue}")

# 25. Re-raise
def process_data(data):
    try:
        result = 100 / data
        return result
    except ZeroDivisionError as e:
        print("⚠️ ვლოგავ შეცდომას database-ში...")
        # აქ შეიძლება database-ში ჩაწერა
        raise  # შეცდომას გადავაგდებთ

try:
    process_data(0)
except ZeroDivisionError:
    print("❌ მთავარ პროგრამაში დავიჭირე შეცდომა!")

🎯 შეჯამება

💡 როდის რა გამოვიყენოთ

სიტუაცია გადაწყვეტა
ფაილებთან მუშაობა with open() as file:
მომხმარებლის input try-except ValueError
მონაცემთა ვალიდაცია raise ValueError / Custom Exceptions
რესურსების დასუფთავება finally ბლოკი
წარმატების ლოგიკა else ბლოკი
კომპლექსური სისტემა Custom Exceptions

📚 დამატებითი რესურსები

  • Python Official Docs – Errors and Exceptions
  • Real Python – Exception Handling
  • Real Python – Context Managers
  • PEP 8 – Exception Naming

🚀

გახსოვდეს: კარგი პროგრამისტი არ არის ის, ვინც შეცდომებს არ უშვებს, არამედ ის, ვინც მათ სწორად ამუშავებს!