קורס Python 3 שיעור תרגול תחביר המחלקות

נראה הרבה יותר טוב. ירושה בפייתון רלוונטי רק למקרים בהם צריך לשתף קוד (וגם אז צריך לזכור שזה לא המנגנון היחיד ולחשוב איזה מנגנון הכי מתאים למקרה שלך). פה אין קוד משותף אז כמו שאמרת זה לא בסיפור.

רק טיפ קטן בגלל שאנחנו בפייתון אפשר להפוך את הפונקציה parse_command אפילו לעוד יותר פשוטה ועם פחות if-ים בצורה כזאת:

def parse_command(command):
    commands = {
            'wait': WaitCommand,
            'next': NextCommand,
    }

    command_type, *_ = command.split(' ')

    commands[command_type].parse(command)
לייק 1

אז אני לומד מכאן 2 דברים:

  • אם רק האיבר הראשון רלוונטי, אפשר להשתמש בכוכבית-קו-תחתון לתפוס ולתעלם מאפס או יותר איברים נוספים?
  • [שמות של] מחלקות אפשר לשמור ולהשתמש מתוך מיני collections?

זה בהחלט שידרוג לקוד.

לייק 1

כן רק שזה לא שמות של מחלקות אלא המחלקות עצמן. אותו דבר אפשר לשמור גם פונקציות בתור ערכים במילון (וזו הדרך הסטנדרטית בפייתון לכתוב switch/case)

היי @ynonp רשמתי היום את השתי התרגילים הראשונים :slight_smile: :

אני ממשיך לשתיים הבאים :smile:

Targil 1

class Summer:

    def __init__(self):

        self.total = 0

 

    def add(self, *numbers):

        self.total += sum( i for i in numbers )

 

 

    def print_total(self):

        print(self.total)

 

s = Summer()

t = Summer()

 

s.add(10, 20)

t.add(50)

s.add(30)

 

# should print 60

s.print_total()

 

# should print 50

t.print_total()

Targil 2

class MyCounter:

    count = 0

 

    def __init__(self):

        MyCounter.count += 1

 

for _ in range(10):

     c1 = MyCounter()

 

# should print 10

print(MyCounter.count)
לייק 1

משהו קטן לגבי החלק הזה בפתרון לתרגיל 1:

פקודת sum מקבלת רשימה (למעשה, כל סוג של iterable) ויצירת הלולאה הפנימית (List comprehensions) מיותרת - היא בעצם יוצרת בדיוק את אותה רשימה.
אז אפשר להסתפק ב:

self.total += sum(numbers)
לייק 1

היי, ראיתי מה שכתבתם כאן וזה עזר לי המון לכתוב את הקוד שלי בצורה מובנה (אבל חסר את כל החלק של קבלת המידע מהמשתמש, למרות שאפשר להשלים את זה די בקלות)

אשמח לשמוע מה דעתכם…

import queue
from random import choice


def add(name):
    Clerks.clerks[name] = queue.Queue()
    print(Clerks.clerks)


class Clerks:
    clerks = {}

    def wait(self, clerk, client):
        self.clerks[clerk].put(client)

    def next(self, clerk):
        if not self.clerks[clerk].empty():
            print(f'Serviced from {clerk} queue')
            print(self.clerks[clerk].get())
        else:
            listOfClerks.next(choice(list(self.clerks.keys())))


listOfClerks = Clerks()
add("dave")
add("michael")

listOfClerks.wait("dave", "yossi")
listOfClerks.wait("dave", "shoshy")
listOfClerks.wait("dave", "alma")
listOfClerks.wait("michael", "abigale")
listOfClerks.wait("michael", "roni")

listOfClerks.next("dave")
listOfClerks.next("michael")
listOfClerks.next("michael")
listOfClerks.next("michael")
listOfClerks.next("michael")

לייק 1

הי יש פה משהו קצת מבלבל - מצד אחד האוביקט Clerks שומר כשדה מידע פנימי את clerks שזו רשימה של כל הפקידים. מצד שני כשהוא צריך למצוא פקיד פנוי הוא משתמש במשתנה בשם listOfClerks שבכלל מוגדר קצת יותר למטה.

חוץ מזה נראה טוב :ok_hand:

אני מניח שהייתי צריך לשים שם self… אבדוק בערב

החלק שמדאיג אותי בדרך כלל בתרגילים הללו הוא עד כמה אני בודק ומכין את המעטפת (בדיקת נכונות קלט, try-except וכד’…) - הפעם למדתי ששווה להכין פונקציות ו/או class’ים “שעושים את העבודה” ולטפל בנפרד בנכונות הקלט

שאלה קטנה. מחלקות גם מקובל לכתוב רק באותיות קטנות או שמחלקות כן מתחילות באות גדולה?
מצורף תרגיל 4 שלי אשמח לקבל הערות.

class Clerk:
    def __init__(self):
        self.queue = []

    def wait(self, name):
        self.queue.append(name)

    def next(self):
        if len(self.queue) > 0:
            return self.queue.pop(0)


class Queue:
    def __init__(self):
        self.clerks = {
            'dave': Clerk(),
            'michael': Clerk(),
        }

    def wait(self, clerk, name):
        self.clerks[clerk].wait(name)

    def next(self, name):
        name = self.clerks[name].next()
        i = 0
        for o in self.clerks.values():
            name = o.next()
            if name:
                print(name)
                return

    def get_clerk(self, clerk):
        if clerk not in self.clerks:
            self.clerks[clerk] = Clerk()
        return clerk


class Command:
    error_glob = "the input must begin with : and get next / wait after it"
    error_params = "you must give name of clerk and for wait must also give name for customer"

    def __init__(self):
        self.queue = Queue()

    def input_line(self):
        while True:
            _in = input()
            while not _in[0] == ":":
                if _in == "exit":
                    return True
                print(self.error_glob)
                _in = input()
            try:
                _, func, clerk, *name = _in.split()
            except ValueError:
                print(self.error_params)
                continue
            if func not in ["next", "wait"]:
                print(self.error_glob)
                continue
            clerk = self.queue.get_clerk(clerk)
            if func == "next":
                if name:
                    print(self.error_params)
                    continue
                self.queue.next(clerk)
            else:
                if not name:
                    print(self.error_params)
                    continue
                self.queue.wait(clerk, name[0])


c = Command()

while True:
    if c.input_line():
        break

הי,

מחלקות רושמים עם אות גדולה בהתחלה. אפשר לראות את כל הקונבנציות כאן:

תרגיל 4 נראה טוב

שלום,
מצורף פתרון לתרגיל
,
אשמח לשיפורים!

    class Clerck: 
            def __init__(self, name):
                self.name = name
                self.queue = list()

        class Command:
            active_clerks = {
                "dave" : Clerck("dave"),
                "michael" : Clerck("michael")}

            def __init__(self):
                pass

            def get_command(self):
                while True:
                    command = input("""Enter a valid command, for example:
                    \"wait <clerck name> <user name>\"
                    or: \"next <clerck name>\"""").split()
                    if command[0] == "exit":
                        break
                    if self.Verify_input(command):
                        self.command_parser(command)
                    else:
                        print("The input is incorrect, please follow the instructions")

            def Verify_input(self, command_to_valid):
                if command_to_valid:
                    if len(command_to_valid) >= 2:
                        if command_to_valid[0] == "wait" and len(command_to_valid) == 3 and command_to_valid[1] in self.active_clerks.keys():
                            return True
                        elif command_to_valid[0] == "next" and command_to_valid[1] in self.active_clerks.keys():
                            return True
                return False
                
            def command_parser(self, command_user_input):
                command_array = command_user_input
                if command_array[0] == "wait":
                    self.wait(command_array[1], command_array[2])
                else:
                    self.next(command_array[1])

            def wait(self, clerck_name, customer_name):
                self.active_clerks[clerck_name].queue.append(customer_name)
                print(self.active_clerks.get(clerck_name).queue)

            def next(self, clerck_name):
                if self.active_clerks.get(clerck_name).queue:
                    print(self.active_clerks.get(clerck_name).queue.pop())
                else: print("The queue is empty")

        class Queue:
            def __init__(self):
                self = list()
            
        c = Command()
    c.get_command()

אשמח לפידבק על התרגיל…

class Queue:
    clerks = []
    clients = []
    @staticmethod
    def print_queue():
        for clerk in Queue.clerks:
            print(f"waiting to {clerk.name}")
            for client in clerk.queue:
                print(f"> {client.name}")
    @staticmethod
    def get_client_by_name(client_name):
        exist = [
            client
            for client in Queue.clients
            if client.name == client_name
        ]
        if len(exist) == 0:
            return Client(client_name)
        return exist[0]
    @staticmethod
    def get_clerk_by_name(clerk_name):
        exist = [
            clerk
            for clerk in Queue.clerks
            if clerk.name == clerk_name
        ]
        if len(exist) == 0:
            return Clerk(clerk_name)
        return exist[0]
class Clerk:
    def __init__(self, name):
        self.name = name
        self.isFree = True
        self.appointment_with = None
        self.queue = []
        Queue.clerks.append(self)
    def next(self):
        if len(self.queue) > 0:
            next_client = self.queue.pop(0)
            self._call_to(next_client)
            if len(self.queue):
                self.queue[0].is_next = True
        else:
            print(
                f"no others clients waiting for {self.name} ... program will find if there are others waitings ...")
            find_waitings = [
                client
                for client in Queue.clients
                if client.is_next == True
            ]
            has_waitings = len(find_waitings) > 0
            if not has_waitings:
                return print(f"{self.name}, you can take a break , nobody is waiting")
            next_client = find_waitings[0]
            self._call_to(next_client)
            next_client.clerk.queue.remove(next_client)
    def _call_to(self, next_client):
        self.appointment_with = next_client
        next_client.is_next = False
        next_client.is_wait = False
        self.isFree = False
        print(f"{next_client.name} , please go to -> {self.name}")
    def get_break(self, client):
        self.queue.remove(client)
        self.appointment_with(client)
        self.isFree = False
    def finish_break(self):
        self.isFree = True
class Client:
    def __init__(self, name):
        self.name = name
        self.is_wait = False
        self.is_next = False
        Queue.clients.append(self)
    def remove_from_queue(self):
        pass
    def wait(self, clerk):
        clerk.queue.append(self)
        self.is_wait = True
        self.clerk = clerk
        self.is_next = self.clerk.queue[0] == self
        print(
            f"hi {self.name}... please wait for {clerk.name} , your number in queue is => {len(clerk.queue)}")
def prompt(msg):
    while True:
        try:
            cmd = input(msg)
            commands = cmd.split()
            if len(commands) != 2 and len(commands) != 3:
                raise Exception(
                    "Invalid number off words in command , command should either [wait clerk client_name] or [next clerk]")
            if(commands[0] != "wait" and commands[0] != "next"):
                raise Exception(
                    "command should be either [wait clerk client_name] or [next clerk] -> first word must be either wait or next")
            if(commands[0] == "wait"):
                if len(commands) == 2:
                    raise Exception(
                        f"command should be[wait clerk client_name] but got [wait {commands[1]}]")
                _, clerk_name , client_name = commands
                clerkObj = Queue.get_clerk_by_name(clerk_name)
                clientObj = Queue.get_client_by_name(client_name)
                clientObj.wait(clerkObj)
            if(commands[0] == "next"):
                if len(commands) == 3:
                    raise Exception(
                        f"command should be[next clerk] but got [wait {commands[1]} {commands[2]}]")
                _, clerk_name = commands
                clerkObj = Queue.get_clerk_by_name(clerk_name)
                clerkObj.next()
        except Exception as e:
            print(e)
while True:
    prompt(
        "please enter your command! hint -> [wait clerk_name client_name] | [next clerk] ")

הי שלומי,

אני מדביק את הקוד שלך עם סימונים שלי ואחריו פירוט על כל סימון לפי המספרים:

class Queue:
    clerks = []
    clients = []

    @staticmethod
    def print_queue():
        for clerk in Queue.clerks:
            print(f"waiting to {clerk.name}")
            for client in clerk.queue:
                print(f"> {client.name}")

    # 1. would be easier to use a dictionary here
    @staticmethod
    def get_client_by_name(client_name):
        exist = [
            client
            for client in Queue.clients
            if client.name == client_name
        ]
        if len(exist) == 0:
            return Client(client_name)
        return exist[0]

    @staticmethod
    def get_clerk_by_name(clerk_name):
        exist = [
            clerk
            for clerk in Queue.clerks
            if clerk.name == clerk_name
        ]
        if len(exist) == 0:
            return Clerk(clerk_name)
        return exist[0]

class Clerk:
    def __init__(self, name):
        self.name = name
        # 2. in python it's: is_free
        self.isFree = True
        self.appointment_with = None
        self.queue = []
        # 3. global variables
        Queue.clerks.append(self)

    def next(self):
        if len(self.queue) > 0:
            next_client = self.queue.pop(0)
            self._call_to(next_client)
            # 5. Here you check > 0 implicitly
            if len(self.queue):
                self.queue[0].is_next = True
        else:
            print(
                f"no others clients waiting for {self.name} ... program will find if there are others waitings ...")
            # 4. Better to use a Queue method
            find_waitings = [
                client
                for client in Queue.clients
                if client.is_next == True
            ]
    
            # 5. but here explicitly. Better to select one style
            has_waitings = len(find_waitings) > 0
            if not has_waitings:
                return print(f"{self.name}, you can take a break , nobody is waiting")
            next_client = find_waitings[0]
            self._call_to(next_client)
            # 6. Too many dots
            next_client.clerk.queue.remove(next_client)

    def _call_to(self, next_client):
        self.appointment_with = next_client
        next_client.is_next = False
        next_client.is_wait = False
        self.isFree = False
        print(f"{next_client.name} , please go to -> {self.name}")

    def get_break(self, client):
        self.queue.remove(client)
        self.appointment_with(client)
        self.isFree = False
    def finish_break(self):
        self.isFree = True

class Client:
    def __init__(self, name):
        self.name = name
        self.is_wait = False
        self.is_next = False
        Queue.clients.append(self)

    def remove_from_queue(self):
        pass

    def wait(self, clerk):
        clerk.queue.append(self)
        self.is_wait = True
        self.clerk = clerk
        self.is_next = self.clerk.queue[0] == self
        print(
            f"hi {self.name}... please wait for {clerk.name} , your number in queue is => {len(clerk.queue)}")

def prompt(msg):
    # 7. Should be fun to re-write this using match/case
    while True:
        try:
            cmd = input(msg)
            commands = cmd.split()
            if len(commands) != 2 and len(commands) != 3:
                raise Exception(
                    "Invalid number off words in command , command should either [wait clerk client_name] or [next clerk]")
            if(commands[0] != "wait" and commands[0] != "next"):
                raise Exception(
                    "command should be either [wait clerk client_name] or [next clerk] -> first word must be either wait or next")
            if(commands[0] == "wait"):
                if len(commands) == 2:
                    raise Exception(
                        f"command should be[wait clerk client_name] but got [wait {commands[1]}]")
                _, clerk_name , client_name = commands
                clerkObj = Queue.get_clerk_by_name(clerk_name)
                clientObj = Queue.get_client_by_name(client_name)
                clientObj.wait(clerkObj)
            if(commands[0] == "next"):
                if len(commands) == 3:
                    raise Exception(
                        f"command should be[next clerk] but got [wait {commands[1]} {commands[2]}]")
                _, clerk_name = commands
                clerkObj = Queue.get_clerk_by_name(clerk_name)
                clerkObj.next()
        except Exception as e:
            print(e)

while True:
    prompt(
        "please enter your command! hint -> [wait clerk_name client_name] | [next clerk] ")

ועכשיו לפי המספרים:

  1. במחלקה Queue יצרת שני מערכים ואתה מחפש בהם פריטים לפי ה״שם״ - לפי שם הלקוח או לפי שם הפקיד. כשיש לנו אוסף של נתונים שתמיד אנחנו ניגשים אליהם באמצעות שם של נתון עדיף להשתמש במילון. אפשר ללמוד עוד על מילונים בשיעור הזה:
    https://www.tocode.co.il/bundles/python/lessons/14-dictionaries

  2. במשתנים ופונקציות אנחנו מפרידים בין מילים בפייתון עם קו תחתי, כמו שעשית ברוב התוכנית. באותו נושא שווה לקרוא את המדריך PEP 8 של פייתון לכתיבת קוד בסגנון של פייתון:
    https://www.python.org/dev/peps/pep-0008/

  3. שמתי לב שהשתמשת במחלקה Queue אבל כל הגישה אליו היא דרך מתודות סטטיות. בעצם מה שיצרת פה זה משתנה גלובאלי כי ניגשים אליו מכל מקום ביישום. אם תרצה בעתיד לשנות את המבנה או להשתמש במחלקה Clerk במקום אחר יהיה לך קשה כי היא קוראת לפונקציות הגלובאליות של Queue, והקשר בין השניים מוחבא עמוק בקוד.
    במצבים כאלה יותר מומלץ ליצור את המחלקה Queue בלי מתודות סטטיות, ליצור אוביקט אחד ממנה בתחילת התוכנית ולהשתמש בו כדי ליצור את ה Clerk וה Client. לדוגמה אתחול של Clerk יכול להיות:

class Clerk:
  def __init__(self, name, queue):
    self.queue = queue

ואז הפונקציה get_clerk_by_name תוכל להחזיר משהו כזה:

return Clerk(clerk_name, self)

כלומר ה Queue מעביר את עצמו כפרמטר כשהוא יוצר את ה Clerk ואת ה Client. ככה אם תרצה להזיז חלק מהם למקום אחר יהיה מאוד ברור מה הם צריכים לקבל ואיך מזיזים אותם.

  1. בהרבה מקומות בקוד, לדוגמה איפה שסימנתי עם (4) אבל גם איפה שסימנתי עם (6) אנחנו רואים שהקוד נכנס לתוך אופן הפעולה הפנימי של אוביקט אחר. אם יש לנו אוביקט Queue כללי, אני לא רוצה להתחיל לסרוק את כל ה clients שלו כדי לזהות מי מחכה לפקיד אחר, אלא לבקש ממנו שיסרוק ויחזיר לי את המידע. משהו כמו:
find_waiting = Queue.get_client_waiting_for_other_clerk(self)

ככל שאתה נותן לכל מחלקה לטפל במשתנים שלה ובתחומי האחריות שלה, אתה עושה את זה קל יותר בעתיד להזיז קוד ממקום למקום.

  1. שמתי לב שבחלק מהמקומות בדקת:
if len(X) > 0:

ובמקומות אחרים ויתרת על הסימן ובדקת:

if len(X):

שניהם עובדים, לדעתי הראשון יותר קריא ובכל מקרה מומלץ להיות אחידים.

  1. מה שכתבתי ב 4

  2. בדיוק העברתי וובינר על פיצ׳ר חדש של פייתון 3.10 שנקרא Structural Pattern Match שיכול להפוך את הקוד שכתבת כאן להרבה יותר קצר וקריא. שווה לצפות ולנסות ליישם כאן:
    https://www.tocode.co.il/past_workshops/110

תודה על השיתוף מאוד מעריך את ההשקעה וזה בהחלט צעד גדול בכיוון הנכון. מקווה שההצעות שלי לשיפורים יעזרו להפוך את הקוד אפילו ליותר מוצלח.

היי ינון ותודה על ההערות המחכימות.
מצרף פה את הקוד לאחר שיפורים. אעריך מאוד לקבל פידבק במידה ואני בכיוון הנכון.

כותב פה כמה הערות שקפצו לי:

  1. השימוש בmatch אכן הרבה יותר אלגנטי (אני בא מjavascript כך שזה מוכר לי , אבל השיטה בפייתון הרבה יותר מגניבה) , תהיתי לעצמי אם יש אפשרות לעשות דילוג לבלוק הבא ( כמו בjavascript ברירת מחדל בלי break).

  2. יישמתי הנחת עבודה לפיה ניתן לאפס את התור במידת הצורך ובכך בעצם לאתחל את הqueue אבל השיטה הייתה לעשות את זה בלי לאפס את התור הקיים אלא ליצור תור חדש (נניח לצורך שמירת סטטיסטיקות) השאלה האם זה נכון להשתמש בreturn של class במקרה כזה או שיש דרך יותר אלגנטית.

class Queue:
    num: int = 0
    history = []

    def __init__(self) -> None:
        Queue.num += 1
        self.clerks = {}
        self.clients = {}

    def reset(self, password: str):
        if password == 1234:
            Queue.history.append(self)
            return Queue()
        else:
            print(f"wrong password... hint [1**4]")
            return self

    def print_queue(self):
        for clerkName, clrerkObj in self.clerks:
            print(f"waiting to {clerkName}")
            for client in clrerkObj.queue:
                print(f"> {client.name}")

    def get_client_by_name(self, client_name):
        if client_name in self.clients:
            return self.clients[client_name]
        return Client(client_name, self)

    def get_clerk_by_name(self, clerk_name):
        if clerk_name in self.clerks:
            return self.clerks[clerk_name]
        return Clerk(clerk_name, self)

    def get_waitings_list(self):
        find_waitings = [
            client
            for client in self.clients.values()
            if client.is_next == True
        ]
        has_waitings = len(find_waitings) > 0
        return [has_waitings, find_waitings]


class Client:
    def __init__(self, name, queue: Queue):
        self.name = name
        self.is_wait = False
        self.is_next = False
        self.clerk = None
        queue.clients[name] = self

    def remove_from_queue(self):
        if self.clerk != None:
            try:
                self.clerk.waitings.remove(self)
            except ValueError:
                print(
                    f"cannot remove {self.name} from ${self.clerk.name} , cannot find in waiting-list")

    def wait(self, clerk):
        clerk.waitings.append(self)
        self.is_wait = True
        self.clerk = clerk
        self.is_next = self.clerk.waitings[0] == self
        print(
            f"hi {self.name}... please wait for {clerk.name} , your number in queue is => {len(clerk.waitings)}")


class Clerk:
    def __init__(self, name, queue: Queue):
        self.name = name
        self.is_free = True
        self.appointment_with = None
        self.waitings = []
        queue.clerks[name] = self

    def next(self, queue: Queue):
        if len(self.waitings) > 0:
            self._call_to_next(self.waitings[0])
            if len(self.waitings) > 0:
                self.waitings[0].is_next = True
        else:
            print(
                f"no others clients waiting for {self.name} ... program will find if there are others waitings ...")
            has_waitings, waiting_list = queue.get_waitings_list()
            if not has_waitings:
                return print(f"{self.name}, you can take a break , nobody is waiting")
            next_client = waiting_list[0]
            self._call_to_next(next_client)

    def _call_to_next(self, next_client: Client):
        next_client.remove_from_queue()
        self.appointment_with = next_client
        next_client.is_next = False
        next_client.is_wait = False
        self.is_free = False
        print(f"{next_client.name} , please go to -> {self.name}")

    def get_break(self, client):
        self.waitings.remove(client)
        self.appointment_with(client)
        self.is_free = False

    def finish_break(self):
        self.is_free = True


def prompt(msg, q: Queue):
    while True:
        try:
            cmd = input(msg)
            commands = cmd.split()
            match commands:
                case ["next", clerk_name]:
                    clerkObj = q.get_clerk_by_name(clerk_name)
                    clerkObj.next(q)
                case ["wait", clerk_name, client_name]:
                    clerkObj = q.get_clerk_by_name(clerk_name)
                    clientObj = q.get_client_by_name(client_name)
                    clientObj.wait(clerkObj)
                case ["print"]:
                    q.print_queue()
                case ["reset", password]:
                    q.reset(password)
                case [cmd,*_]:
                    if not cmd in ["print","wait","next","reset"]:
                        print(f"command {cmd} not found!")
                    else:
                        match cmd:
                            case "wait":
                                print("wait command should include 2 arguments : [clerk client_name]")
                            case "next":
                                print("next command should include 1 argument : [clerk]")
                            case "reset":
                                print("reset command should include 1 argument : [password]")
                            case "print":
                                print("reset command should  not include any arguments")
                case _:
                    print(
                        f"command should be either [wait clerk client_name] or [next clerk]")
        except Exception as e:
            print(e)


q1 = Queue()

while True:
    prompt(
        f"[{q1.num}] > Queue is waiting for command! hint -> [wait clerk_name client_name] | [next clerk] ", q1)

הי,

לא הבנתי את הקטע הזה:

                case [cmd,*_]:
                    if not cmd in ["print","wait","next","reset"]:
                        print(f"command {cmd} not found!")
                    else:
                        match cmd:
                            case "wait":
                                print("wait command should include 2 arguments : [clerk client_name]")
                            case "next":
                                print("next command should include 1 argument : [clerk]")
                            case "reset":
                                print("reset command should include 1 argument : [password]")
                            case "print":
                                print("reset command should  not include any arguments")

למה לא להמשיך את ה case-ים עם הפקודות מהרשימה השניה? לדוגמה לגבי ה wait:

                case [cmd,*_]:
                    if not cmd in ["print","wait","next","reset"]:
                        print(f"command {cmd} not found!")
                    else:
                        match cmd:
                            case "wait":
                                print("wait command should include 2 arguments : [clerk client_name]")
                            case "next":
                                print("next command should include 1 argument : [clerk]")
                            case "reset":
                                print("reset command should include 1 argument : [password]")
                            case "print":
                                print("reset command should  not include any arguments")

לא הבנתי מה הכוונה ״לדלג״ לבלוק הבא

ממש אהבתי איך שאיפסת את התור זה רעיון מדליק. הרבה פעמים אנחנו דווקא כן רוצים ״לאפס״ את הדבר שיש לנו ביד כי יש אחרים שמסתכלים עליו, לדוגמה בתור אם נתתי לשתי פונקציות את אוביקט ה queue ואז אחת מהן מפעילה reset, הרבה פעמים אנחנו מצפים שגם מבחינת הפונקציה השניה ה queue יתאפס. כלומר לא לגמרי ברור מה המשמעות של Queue.history

שלום לכולם אשמח לקבל פיידבק עבור הקוד שלי לתרגיל 4.
השתדלתי לכתוב את הקוד בצורה הפשוטה והקצרה ביותר ובשלב חסרים לו אקסטרה פיצ’רים כמו איפוס התור, הדפסת סטטיסטיקה וכד’.
השתמשתי רק בסוג אחד של קלאס אחד עבור פקיד, מילון המרכז את כל הפקידים בצורה של clerk_name : clerk_object, ולולאה המקבלת ושולחת פקודות.
יש לי כמה תהיות בנוגע למבנה הקוד שלי ואשמח לקבל פיידבקים:

  1. האם השימוש במילון עבור הפקידים כאשר המילון עצמו מוגדר מחוץ לקלאס הוא נכון? האם זה יכול ליצור בעיות בהמשך?
  2. בתרגיל לא נדרש לשמור מידע על לקוחות או לעשות איתם פעולות ולכן הסתפקתי בקלאס מסוג אחד עבור פקידים, האם יש בזה בעיה? (אני מבין שעבור פיצ’רים נוספים הייתי צריך ליצור קלאס נוסף עבור כל לקוחות)
  3. לא ראיתי צורך בשימוש בקלאס או פונקציה עבור הלולאה שמקבלת פקודות. האם אני טועה? מה אני מפספס?
from collections import deque

debug = False
clerk_dic = {}

class clerk:
    def __init__(self, clerk_name):
        self.name = clerk_name
        self._queue = deque()
        self.l_queue = len(self._queue)

    def wait(self, *clint):
        self._queue.extend(*clint)
        self.l_queue = len(self._queue)
        if debug: print(self._queue)

    def next(self):
        if self.l_queue > 0:
            nclinet = self._queue.popleft()
            print('clint', nclinet, 'pleas go to to clerk', self.name)
            self.l_queue = len(self._queue)
        else: #if nobody in line for the current clerk the program checks which clerk have the longest line
            maxq = 0
            for k in clerk_dic.keys():
                if clerk_dic[k].l_queue > maxq:
                    maxq = clerk_dic[k].l_queue
                    bigger = k

            if maxq > 0:
                self.wait(str(clerk_dic[bigger]._queue.popleft()))
                self.next()
            elif maxq == 0: #if so there is no people in the line
                print(f"there is no more clints in the queue")


while True:
    cmd = input(f'Please enter the next Queue command.\n'
                f'Valid command can be in the form of:\n'
                f'1. "wait <clerk_name> <clint_name\s>"\n'
                f'2. "next <clerk_name>"\n'
                f'3. "exit"\n'
                f'\n')
    command = cmd.split()
    match command:
        case["wait", clerk_name, *clint_name]:
            if clerk_name not in clerk_dic.keys():
                clerk_dic[clerk_name] = clerk(clerk_name)
            clerk_dic[clerk_name].wait(clint_name)
        case["next", clerk_name]:
            if clerk_name not in clerk_dic.keys():
                print(f"clerk {clerk_name} doesn't exist")
                continue
            clerk_dic[clerk_name].next()
        case["exit"]:
            exit()
        case[cmd, *_]:
            if cmd not in["wait", "next", "exit"]:
                print(f"{cmd} is not a valid command")
            else:
                print("you enter wrong numbers of variables")

    print('\n')


    if debug: print(clerk_dic)

הי ותודה על הפיתרון!, כמה נקודות מקריאה שלו שאני מקווה שיעזרו לשפר לעתיד-

  1. שמות מחלקות בפייתון מתחילות באות גדולה, אז עדיף להשתמש ב Clerk

  2. המשתנה self.l_queue נראה לי מיותר, אפשר פשוט להפעיל את len כל פעם שצריך את האורך

  3. המבנה של if debug: print עובד, אבל אני חושב שיהיה יותר יפה להגדיר פונקציית הדפסה נפרדת, משהו כמו:

def debug(text):
  if debug: print(text)

ואז להפעיל אותה בכל מקום.

  1. השימוש במילון גלובאלי, או במשתנים גלובאליים בכלל, בהחלט יכול לסבך אותך כשהתוכנית תגדל. הבעיה היא שלדוגמה עכשיו אי אפשר להעביר את הקוד של פקיד לקובץ אחר כי הוא צריך את המילון, וקשה יותר לבדוק את הקוד או למצוא בו בעיות כי יכול להיות שהתקלה נובעת ממשהו שנמצא או לא נמצא באותו מילון גלובאלי. במינימום הייתי מעביר את המילון הזה בתור פרמטר ל init של Clerk, ואז לפחות אפשר להעביר את הקוד לקובץ נפרד.

תכנות עוד יותר טוב של המערכת יבנה ממש קלאס של ״מרכז שירות״ שיחזיק את המילון של הפקידים ויחשוף פונקציות כמו ״צור פקיד״, ״מצא את הפקיד עם התור הארוך ביותר״ ו ״מצא פקיד לפי שם״. הגישה דרך פונקציות למנגנונים האלה מאפשרת בקרה יותר טובה על המצב שלהם.

  1. אהבתי מאוד את השימוש ב Pattern Matching בלולאה שקוראת קלט. מבנה מאוד ברור.

  2. יהיה נחמד להוסיף תיעוד לפונקציות, או אפילו Type Hints:
    ToCode

סך הכל פיתרון טוב והתמודדות מצוינת עם התרגיל! נהניתי לקרוא.

היי ינון, תודה רבה על הפיידבק!
הוספתי קלאס של מערכת ניהול תורים Queue ובו פונקציה ליצירת פקיד חדש, מציאת פקיד לפי שם(רק בודקת אם קיים) ומציאת התור הארוך ביותר.
אכן ראיתי שכשאני מבצע פעולות מחוץ לפונקציות מוגדרות זה יכול לגרום לבאגים ולכן הוספתי פונקציה להסרת פקיד מהתור בתוך הקלאס של Clerk אשר בתרגיל שלנו תפקידה לתמוך בפעולת העברת לקוח מפקיד עמוס לפקיד פנוי.

אשמח לקבל פיידבק נוסף.
תודה :slight_smile:

from collections import deque

Debug = True

class Queue:
    def __init__(self):
        self.clerks = {}

    def new_clerk(self, clerk_name):
        self.clerks[clerk_name] = Clerk(clerk_name)

    def find_clerk_by_name(self, clerk_name):
        if clerk_name in q1.clerks.keys(): return True
        else: return False

    def longest_line(self):
        maxq = 0
        bigger = None
        for k in self.clerks.keys():
            if len(self.clerks[k]._queue) > maxq:
                maxq = len(self.clerks[k]._queue)
                bigger = k
        return bigger





class Clerk:
    def __init__(self, clerk_name):
        self.name = clerk_name
        self._queue = deque()

    def wait(self, *clint):
        self._queue.extend(*clint)
        debug(self._queue)

    def next(self, queue):
        if len(self._queue) > 0:
            nclinet = self._queue.popleft()
            print('clint', nclinet, 'pleas go to to clerk', self.name)
        else: #if nobody in line for the current clerk the program checks which clerk have the longest line
            longest = queue.longest_line()

            if longest != None:
                self.wait(queue.clerks[longest].remove_clint())
                self.next(queue)
            elif longest == None: #if so there is no people in the line
                print(f"there is no more clints in the queue")

    def remove_clint(self):
        return str(self._queue.popleft())

def debug(text):
    if Debug: print(text)


q1 = Queue()

while True:
    cmd = input(f'Please enter the next Queue command.\n'
                f'Valid command can be in the form of:\n'
                f'1. "wait <clerk_name> <clint_name\s>"\n'
                f'2. "next <clerk_name>"\n'
                f'3. "exit"\n'
                f'\n')
    command = cmd.split()
    match command:
        case["wait", clerk_name, *clint_name]:
            if not q1.find_clerk_by_name(clerk_name): # check if "clerk_name" exists, if not creating new clerk
                q1.new_clerk(clerk_name)
            q1.clerks[clerk_name].wait(clint_name) # pupin clint's into the requested clerk queue
        case["next", clerk_name]:
            if not q1.find_clerk_by_name(clerk_name): # check if the clerk exists amd the input is correct
                print(f"clerk {clerk_name} doesn't exist")
                continue
            q1.clerks[clerk_name].next(q1)
        case["exit"]:
            exit()
        case[cmd, *_]:
            if cmd not in["wait", "next", "exit"]:
                print(f"{cmd} is not a valid command")
            else:
                print("you enter wrong numbers of variables")

    print('\n')
    debug(q1.clerks)

כן זה נראה מעולה. תחנה הבאה תהיה להוסיף לו בדיקות אוטומטיות, אבל זה יהיה רק אחרי צפיה בשיעור:
https://www.tocode.co.il/bundles/python/lessons/27-unit-testing?tab=video

שלום,
ראשית, רציתי להודות על השיעורים, נהנית לשמוע.
מצרפת כאן קוד לתרגיל 4, כגרסה ראשונית, אודה למשוב

from collections import deque


class Clerc:

    clients = deque()

    def __init__(self, clerc_name):
        self.name = clerc_name

    def wait(self, client_name):
        self.clients.append(client_name)

    def get_clients_for_clerc(self):
        return self.clients

    def next(self):
        try:
            return self.clients.popleft()
        except IndexError:
            return '---'


class Queue:

    list = {}

    def __init__(self):
        self.list = {}

    def add_queue(self, clerc_name, client_name):
        if clerc_name not in self.list.keys():
            self.list[clerc_name] = Clerc(clerc_name)
        self.list[clerc_name].wait(client_name)

    def next_queue(self, clerc_name):
        try:
            next_client = self.list[clerc_name].next()
            if next_client != '---':
                print(next_client)
            else:
                while True:
                    next_client = [self.list[c].next() for c in self.list]
                    if next_client != '---':
                        print(next_client)
                        break
        except KeyError:
            print('Clerc is not found')

    def get_clients(self, clerc_name):
        print(self.list[clerc_name].get_clients_for_clerc())


q1 = Queue()

while True:
    inp = input('Please enter one command: wait <clerc_name> <client_name> or next <clerc_name> ')
    if inp == 'exit':
        break
    data = inp.split()
    if inp.split(' ')[0] == 'wait':
        q1.add_queue(data[1], data[2])
    elif data[0] == 'next':
        q1.next_queue(data[1])
    elif data[0] == 'get':
        q1.get_clients(data[1])