I have a food class, who's objects either get picked up for delivery or decay and are trashed. I have used the threading module to start two timers to see which of the two happens first and based on that the code proceeds.
The class definition is as below :
import threading
import random
class food():
def __init__(self, name) :
self._name = name
self.isInKitchen = True
def notifyDelivery(self):
print ("order has been picked for delivery")
self.isInKitchen = False
def notifyDecay(self):
print ("object has decayed")
self.isInKitchen = False
I then create an object from this class and for the purpose of this question create two variables dictating the delivery pickup time and the expected decay time, as below
order = food("pasta")
decayPeriod = random.randint(5,10)
pickupPeriod = random.randint(2,10)
I then write the threads for tracking delivery and decay as below :
def deliver(order):
if order.isInKitchen == False :
order.notifyDelivery()
deliveryTimer = threading.Timer(pickupPeriod, deliver, [order])
deliveryTimer.start()
def decay(order):
if order.isInKitchen == False :
order.notifyDecay()
decayTimer = threading.Timer(decayPeriod, decay, [order])
decayTimer.start()
So my expectation is that whichever function triggers first should set the order.isInKitchen variable as False and then the other function will not be triggered. While I do get the output i hope for, the second thread keeps running till the decayPeriod or pickupPeriod is reached. At that point it checks that the isInKitchen property of the object is False and does nothing before terminating the thread.
I am trying to find out if there is a way to stop the second thread as soon as the function in the first thread is triggered. Is there a more efficient way to do this?
Related
Let's say we have a simulation of taxi movements. Each car at any given time, can be only in one of idle, rebalancing, serving states (just as an example, there are many more in my simulation). At any time, I have to check the state of each of the vehicles and act accordingly. For example, if a vehicle was idle and then picked up a passenger, its state should change from idle to serving. Importantly, at any given time the vehicle can only be in one of those states and its action set is also dependent on that state.
Currently, I am doing this with a long list of if-else checks, but feels very naive and it's very difficult to debug.
For example, the main logic checks the state of a car first:
if self.should_move():
_ = _make_a_decision(t)
self.update_rebalancing(WARMUP_PHASE)
if self.is_busy():
self.keep_serving()
elif self.is_waiting_to_be_matched():
# it's sitting somewhere
self.keep_waiting()
elif self.rebalancing: # and not self.busy:
self.update_rebalancing(WARMUP_PHASE)
Then any of those functions update its state accordingly, all including statements like this
self.idle = False
self.rebalancing = False
self.serving = True
self.time_idled = 0
There is a lot of repetition and easy to make mistakes.
I am wondering if
there is a programming pattern for this situation
If Python specifically has functionalities to handle this case
This is a really broad question and there's not any correct answer to it..
But to try to help, why not use a class?
class Taxi:
# One variable to hold an enum of the states.
self.state = 'idle'
# function to update the state
def setState(self, state):
self.state = state
# Functions to check the state (could have one to get the state and check later
def isIdle(self):
return self.state == 'idle'
Then any functionality the taxi needs to do can be put in the class like:
def pickupPassengers():
if self.state != 'idle':
return
self.passengers += 1
Then you create your taxis and manage them through the class
taxi1 = Taxi()
taxi1.getPassengers()
There are a few ways to accomplish what you want. Since it can get confusing if you have to manually change multiple variables every time you have a state change... just have one variable in charge of the state! If you want to still be able to refer to Taxi.idle, Taxi.rebalancing, etc, then you can create #property methods. They look like functions when you define them but are called like properties.
See the example below for a Taxi class which has only one state variable, self._state and uses #property methods to return the status.
class Taxi:
def __init__(self, initial_state = "idle"):
self._state = initial_state
#property
def idle(self):
return self._state == "idle"
#property
def rebalancing(self):
return self._state == "rebalancing"
#property
def serving(self):
return self._state == "serving"
def serve(self):
print("\nChanging state to serving\n")
self._state = "serving"
T = Taxi() # Initialize taxi
print(f"Taxi is idle: {T.idle}")
print(f"Taxi is rebalancing: {T.rebalancing}")
print(f"Taxi is serving: {T.serving}")
T.serve()
print(f"Taxi is idle: {T.idle}")
print(f"Taxi is rebalancing: {T.rebalancing}")
print(f"Taxi is serving: {T.serving}")```
Output:
Taxi is idle: True
Taxi is rebalancing: False
Taxi is serving: False
Changing state to serving
Taxi is idle: False
Taxi is rebalancing: False
Taxi is serving: True
Your design problem is that you're trying to use a series of Booleans to embody one-hot encoding of a discrete variable (state). If you want only one value at a time, the natural way to do this is with a single variable. Enumeration types are what most advanced languages use for this. For instance, you can encode "idle" as 0, "rebalancing" as 1, etc.
Python code would look something like this:
from enum import Enum, unique, auto
#unique
class TaxiState(Enum):
IDLE = auto()
REBAL = auto()
SERVE = auto()
class Taxi():
def __init__(self):
self.state = TaxiState.IDLE
def is_busy(self):
return self.state != TaxiState.IDLE
You don't worry about the coding; auto handles that. All you do is use the enumeration name as a value. You get to code just as you designed that aspect of the model.
I'm making a game where I can gather resources or build when I send Workers, but I can't think of a way to receive those resources or finish building depending on the turn and the time(turns) it takes to finish those actions.
I've already made a Worker class, and it has a method to gather and it gives a random value that I save in a Player class. Also, my Game class keeps track of the turn I and the computers are.
class Game:
def __init__(self, player = None):
self.player = player
self.turn = 1
class Player:
def __init__(self):
self.workers = [Worker(), Worker(), Worker()]
self.resourcers = 0
class Worker:
def __init__(self):
self.hp = 100
def gather(self):
return randint(MIN_CANTIDAD_RECURSO, MAX_CANTIDAD_RECURSO)
player = Player()
game = Game()
game.player = player
for worker in player.workers:
player.resources += worker.gather
game.turn +=1
Gathering should give the result the next turn and build should give it depending on the building.
In a general sense, you store the values you need in the relevant object and pass them as parameters to whatever method requires those values. For example, you would need to store the turn duration of an action in the return value of that action, e.g in class Worker
def gather(self):
# Some code that determines gather_value and duration...
return [gather_value, duration]
and then the resource usage would look something like
def use_gather(gather, turn): # Pass in (return value from gather, game.turn)
# Use parameters...
With such a vague question, it's hard to say anything more.
I attempting to design a simple choice based video game. Essentially what I want is a recursive loop that will continue to call the new levels based off the results of the previous. For example, in level 1 based off choice made it will either trigger level 2 or 3. This is the code I have so far:
class Levels:
def __init__(self, Next = 1):
self.Next = Next
def Call(self):
VarLevel = "Level" + "{}".format(self.Next)
return ("{}".format(VarLevel))
This is the super class, it returns the VarLevel which equals Level1 to trigger the subclass Level1. This is the code I have for the levels, I've excluded the context of the game because I don't think it is necessary.
class Level1(Levels):
def __init__(self,):
# this just includes information about the level to show the user
# (objective etc.)
def Action(self):
# this will include the content of the level. based off the choices
# made I want it to return to the super with VarLevel as Level2 or
# Level3 and then to trigger the next level running and repeat
# throughout the program to run the game. For the purpose of testing
# the program the only content of Level1 is setting the return to 2 so
# that Level2 is called. I'm having trouble actually getting it to
# recognize my return and to trigger the next level. This is the
# actual method I want to do the calling with
class LevelCall():
def __init__(self, Levels = Levels):
self.Levels = Levels
def Calling(self):
result = (Levels, "{}".format(Levels()))()
it gives me the error TypeError: 'tuple' object is not callable. I have been doing a lot of different attempts to get it to work so I'm not certain that this is even the real problem with the code. Also of note I am decent in Java and am now transitioning to Python (this is my first attempt in Python other then basic tests to read/write etc.) Any help is greatly appreciated to help figure out how to format the game and I apologize in advance because I know this is a long question, I've never posted here before so if you need more info or clarification please feel free to ask.
Edit:
This is the full error message
Traceback (most recent call last):
line 54, in <module>
Tester.Calling()
line 50, in Calling
result = (Levels, "{}".format(Levels()))()
TypeError: 'tuple' object is not callable
Another Edit:
I think I am getting closer. I made the following changes
class LevelCall():
def __init__(self, Levels = Levels):
self.Levels = Levels
def Calling(self):
Hold = Levels()
result = (getattr(Levels, "{}".format(Hold.Call()))())
It now gives the following error message.
Traceback (most recent call last):
line 55, in <module>
Tester.Calling()
line 51, in Calling
result = (getattr(Levels, "{}".format(Hold.Call()))())
AttributeError: type object 'Levels' has no attribute 'Level1'
If I understand correctly it is now attempting to do what I want but isn't finding the class "Level1". Again all help is much appreciated.
Edit______________________
I would like to thank all who replied and attempted to help, I am truly grateful for the support. With the clarification you were able to help me with as well as a fresh start today and mapping it out in java first to make the transition easier I was able to solve my problem. Once again thank you all very much I will add the solution I found beneath this edit.
global Stop
class Level1 :
def __init__(self):
self
def Action(self):
print ("1")
global Stop
Stop = input("Would you like to advance to the next level?")
if (Stop == "yes"):
# Lev = Level2()
# return Lev.Action()
return Level2
if (Stop == "no"):
return "stop"
class Level2:
def __init__(self):
self
def Action(self):
print("2")
global Stop
Stop = input("Would you like to advance to the next level?")
if (Stop == "yes"):
# Lev = Level3()
# return Lev.Action()
return Level3
if (Stop == "no"):
return "stop"
class Level3 :
def __init__(self):
self
def Action(self):
print ("3")
global Stop
Stop = input ("Next level??")
if (Stop == "yes"):
# Lev = Level4()
# return Lev.Action()
return Level4
if (Stop == "no"):
return "stop"
class Level4:
def __init__(self):
self
def Action(self):
print ("Complete")
return "Done"
def Runner (Level):
if (Level == "Done"):
print ("Bye")
else :
if (Level != "stop"):
Lev = Level()
Next = Lev.Action()
Runner(Next)
if (Level == "stop"):
print ("you chose to stop")
Runner(Level1)
(a,b,c) is tuple syntax. (a,b,c)() is a tuple being called like a function. That is what the error is referring to.
If we break the offending code down you can tell. What does it look like when you replace the call to format with an arg placeholder:
(Levels, "{}".format(Levels()))() becomes...
(Levels, arg)() # this is now clearly a tuple and you're treating it like a function.
Not really sure how fixing that will help you with your levels problem tho.
If you want to call a function, do so like: func(args).
If you want to define a tuple, do so like: (a, b, ..., z).
But don't call a tuple like a function.
So I am making a text based adventure game. I am working on the engine right now and I am stuck after long hours searching for a solution for this problem.
I have a class called use_action. One of the arguments for that class is a name of a function. I would like to be able to create this action and have a possible custom function incase the item that calls this use_action does something specific.
The custom function I am working with right now is where the player is hurt and is losing 5 HP every so many seconds.
This should start when he uses a specific item and then stops when he uses the medicine that will link to the stop function. The problem I have is that the function gets called immediately. Even though I am trying to call it at the end of a long if else statement. And then when i get to where i am trying to call it it doesn't call.
I am not posting the whole class as it along with its functions are about 150 lines of code.
class use_action(object):
def __init__(self, function = None):
self.function = function
pizza_act = use_action(function = mechanics.tmr.start())
#This is located at the end of an if else statement after the player types use . . .
if self.function != None:
self.function
else:
pass
From Mechanics:
thread_list = []
class TimerClass(threading.Thread):
def __init__(self, function, time):
threading.Thread.__init__(self)
self.event = threading.Event()
self.function = function
self.time = time
thread_list.append(self)
def run(self):
while not self.event.is_set():
self.event.wait( self.time )
self.function()
def stop(self):
self.event.set()
def blank_current_readline():
# Next line said to be reasonably portable for various Unixes
(rows,cols) = struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,'1234'))
text_len = len(readline.get_line_buffer())+2
# ANSI escape sequences (All VT100 except ESC[0G)
sys.stdout.write('\x1b[2K') # Clear current line
sys.stdout.write('\x1b[1A\x1b[2K'*(text_len/cols)) # Move cursor up and clear line
sys.stdout.write('\x1b[0G') # Move to start of line
def pizza_poisoned_action():
# threading.Timer(10, pizza_poisoned_action).start()
blank_current_readline()
print "You lost 5 hp."
initialization.gamer.hp -= 5
sys.stdout.write('> ' + readline.get_line_buffer())
sys.stdout.flush() # Needed or text doesn't show until a key is pressed
tmr = TimerClass(pizza_poisoned_action, 5)
Sorry about the length, I tried to only post the relevant stuff for this. If you think i should post some other piece of code that may be relevant let me know!
If you want to pass a function, don't call it. Or else, you'll be passing the return value.
pizza_act = use_action(function = mechanics.test()) #Wrong!
pizza_act = use_action(function = mechanics.test) #Right
I have the folowing code:
from random import randint
from medical_room import *
from Library import *
from basement import *
from End import *
class start_Game(object):
def __init__(self):
print "You landed on planet and see three rooms."
print "You approach and see that you need to enter password..."
self.door=raw_input("Pick number of door>>>")
self.password=('%d')%(randint(1,9))
self.entered_password=int(raw_input("Enter password of one digit>>>"))
self.ROOMs={'1':Medical_room,'2':Library,'3':basement,'4':End}
while True:
# break
room=self.ROOMs[self.door]
# print room()
self.door=room()
a=start_Game()
When asked about door number I pick '1' and class Medical_room is launched (code of class is below):
class Medical_room(object):
def __init__(self):
self.play()
def play(self):
print "Medical_room plays"
return '2'
But I can't switch to Library class since get error:
room=self.ROOMs[self.door]
KeyError: <medical_room.Medical_room object at 0x0000000002906978>
For me everything is ok, but Python doesn't like my 'great logic'. Please help.
Before the loop runs, self.door is a string. On the first iteration of the loop, you set self.door as a reference to an object on the first iteration. On the second iteration, you try to use that object as a key on self.ROOMS, but that dictionary has only strings for keys.
You need to set self.door to the string returned by play, I believe:
while True:
room=self.ROOMs[self.door]
self.door=room().play()
However, this doesn't allow you to choose a new door in each room (unless you change the definition of play).