הי שלומי,
אני מדביק את הקוד שלך עם סימונים שלי ואחריו פירוט על כל סימון לפי המספרים:
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] ")
ועכשיו לפי המספרים:
-
במחלקה Queue יצרת שני מערכים ואתה מחפש בהם פריטים לפי ה״שם״ - לפי שם הלקוח או לפי שם הפקיד. כשיש לנו אוסף של נתונים שתמיד אנחנו ניגשים אליהם באמצעות שם של נתון עדיף להשתמש במילון. אפשר ללמוד עוד על מילונים בשיעור הזה:
https://www.tocode.co.il/bundles/python/lessons/14-dictionaries
-
במשתנים ופונקציות אנחנו מפרידים בין מילים בפייתון עם קו תחתי, כמו שעשית ברוב התוכנית. באותו נושא שווה לקרוא את המדריך PEP 8 של פייתון לכתיבת קוד בסגנון של פייתון:
https://www.python.org/dev/peps/pep-0008/
-
שמתי לב שהשתמשת במחלקה 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. ככה אם תרצה להזיז חלק מהם למקום אחר יהיה מאוד ברור מה הם צריכים לקבל ואיך מזיזים אותם.
- בהרבה מקומות בקוד, לדוגמה איפה שסימנתי עם (4) אבל גם איפה שסימנתי עם (6) אנחנו רואים שהקוד נכנס לתוך אופן הפעולה הפנימי של אוביקט אחר. אם יש לנו אוביקט Queue כללי, אני לא רוצה להתחיל לסרוק את כל ה clients שלו כדי לזהות מי מחכה לפקיד אחר, אלא לבקש ממנו שיסרוק ויחזיר לי את המידע. משהו כמו:
find_waiting = Queue.get_client_waiting_for_other_clerk(self)
ככל שאתה נותן לכל מחלקה לטפל במשתנים שלה ובתחומי האחריות שלה, אתה עושה את זה קל יותר בעתיד להזיז קוד ממקום למקום.
- שמתי לב שבחלק מהמקומות בדקת:
if len(X) > 0:
ובמקומות אחרים ויתרת על הסימן ובדקת:
if len(X):
שניהם עובדים, לדעתי הראשון יותר קריא ובכל מקרה מומלץ להיות אחידים.
-
מה שכתבתי ב 4
-
בדיוק העברתי וובינר על פיצ׳ר חדש של פייתון 3.10 שנקרא Structural Pattern Match שיכול להפוך את הקוד שכתבת כאן להרבה יותר קצר וקריא. שווה לצפות ולנסות ליישם כאן:
https://www.tocode.co.il/past_workshops/110
תודה על השיתוף מאוד מעריך את ההשקעה וזה בהחלט צעד גדול בכיוון הנכון. מקווה שההצעות שלי לשיפורים יעזרו להפוך את הקוד אפילו ליותר מוצלח.