How to efficiently make a text-based menu? - python

I am currently making a program with a text based menu, and I was wondering if there was anyway to avoid an excessive amount of if-statements while keeping it object oriented. I thought of something like this:
class Options:
def displayOptions():
#display user options
def option1():
#do option 1
def option2():
#do option 2
def option3():
#do option 3 and so on
class Menu(Options):
options = {1: option1(), 2: option2(), 3: option3()} #and so on
def promptUser():
#prompts user to make a choice
def printHeader():
#prints header
def handleInput():
#checks if input is valid
def begin():
#initialize menu
Would something like this be convoluted? Is the Options class pointless? I'm pretty new to OOP and I was wondering how this would most efficiently be done.

This is a bit hacky but it gets the job done.
class Options:
def doOption(self, opt):
option = f'option{opt}'
func = type(self).__dict__.get(option)
if hasattr(func, '__call__'):
return func(self)
raise ValueError(f'Unsupported option: {opt}')
class Menu(Options):
def option1(self):
print('Foo!')
def option2(self):
print('Bar!')
def optionbang(self):
print('Bang!')
menu = Menu()
menu.doOption(1) # Foo!
menu.doOption(2) # Bar!
menu.doOption('bang') # Bang!
What this does is look through the the class' properties and try to find a function (i.e., hasattr(func, '__call__')) with the right name. The nice thing is that the logic doesn't even require opt to be an integer.
That being said, if this is your only use case, then I agree with #GradyPlayer in the comments that the abstraction probably isn't worth it.

Related

Switch Implementation of Object calling function in python

I am trying to implement the switch in Dictionary in Python. But when I call the method
chooices.get(x, "Ther is no program") It call all the Function in the statment rather than calling x function.
I have read this implementation
Replacements for switch statement in Python?
but it was not helpful in my case as my function has print statement
Main file contain the code
from Day1.Day1 import Day1
from Day2.TipCalculator import TipCalculator
def choose():
print("Choose From the following Program:\n")
day1 = Day1()
day1.day1()
day2 = TipCalculator()
day2.day2()
x = int(input());
chooices={
1: day1.program1(),
2: day2.program1(),
}
chooices.get(x, "Ther is no program")
choose()
Day1 Class contain code
class Day1:
def day1(self):
print('1. Band name Generator\n')
def program1(self):
print('Welcome to Band Name Generator.\n')
cityName=input('What\'s is the name of city you grew up?\n')
petName = input('What\'s your pet\'s name?\n')
print('Your Band Name could be : '+cityName+" "+petName)
Class Tip Calculator Code
class TipCalculator:
def day2(self):
print("2. Tip Calculator For Bill.\n")
def program1(self):
print('Welcome to tip calculator.\n')
I just need the implementation Of switch statement which call Requested Program just like switch. I know Its possible through If-else but Dictionary mapping seems too be good alternative of switch
Overview: the interpretor creates a closure for the variables that the child function uses that are non local. In this example the child variable, value is 22 and stored in the closure cell.
def parent(arg_1, arg_2):
value=22
my_dict = {'chocolate':'yummy'}
def child():
print(2*value)
print(my['chocolate'])
print(arg_1 + arg_2)
return child
new_function=parent(3,4)
print(cell.cell_contents for cell in new_function.__closure__])
If you don't have a lot of variants, the if/elif/else statements can be streamlined using a helper function for the switch. This is only syntactic candy but it may be sufficient for small value sets.
def switch(v): yield lambda *c: v in c
Example usage:
x = int(input())
for case in switch(x):
if case(1): day1.program1()
elif case(2): day2.program1()
else: print("there is no program")
supporting multiple values for the same method call:
x = int(input())
for case in switch(x):
if case(1,5): day1.program1()
elif case(2,3,4): day2.program1()
else: print("there is no program")
you can also use it in a more C-like style
x = int(input())
for case in switch(x):
if case(1,5):
day1.program1()
break
if case(2,3,4):
day2.program1()
break
else:
print("there is no program")
If you have python 3.10 or higher, a proper switch analogue was implmemented called "match" which should work quite well in replacing any nested if statments the other answers may have.
If you dont have 3.10, and you are okay with a pretty hacky solution, mine uses the ideas from withhacks (specifically from AnonymousBlocksInPython). I have recently created my own version of a switch statment in python that acts more like how i am used to in C#. You can expand this as much as you want, way past single line arguments or assignments.
It uses context managers so that you can treat each case as its own code block with indentation and all. It will never enter the cases if the case value does not match the switch value so for code that is extremely system taxing, you can be sure it is not running code that does not need to be.
import sys
class CaseReturn(Exception):pass
class Case:
def __init__(self, caseVal): self._caseVal_ = caseVal
def _trace_(self,frame,event,arg): raise CaseReturn
def __enter__(self):
if self._caseVal_ == Switch._singleton_._switch_: return
sys.settrace(lambda *args, **keys: None)
sys._getframe(1).f_trace= self._trace_
def __exit__(self,ExType,ExVal,ExTrace):
if ExType is None: raise CaseReturn
return ExType is CaseReturn
class Switch:
_singleton_:'Switch' = None
def __init__(self, switchVal,Default=None): self._switch_ = switchVal
def __enter__(self):Switch._singleton_ = self
def __exit__(self,ExType,ExVal,ExTrace):
Switch._singleton_ = None
return ExType is CaseReturn
with Switch(2):
with Case(1):
print('This should not make it')
with Case(2):
print('I made it')
with Case(3):
print('This should never be called')
You can easily extend this out to check multiple cases by just changing the caseVal to a list and doing if Switch._singleton_._switch_ in self._caseVal_:
One caveat is, you cannot make the Case statments one-liners like:
Case(0): print('I am one line')
That will not work and end up calling the code in that case no matter what.
I hope this is useful to you or anyone who is searching for custom Switch statments!

Need to have a string contain code

So I'm trying to make a simple text based game from scratch, and I'm running into a problem right away. I'm trying to create a class for events that will happen in-game to make writing the code easier. Here's what I mean:
class event(object):
def __init__(self, text, trigger1, trigger2, outcome1, outcome2):
print(text)
time.sleep(1)
choice= input("What will you do?")
if choice == trigger1:
(Somehow execute outcome 1)
if choice == trigger2:
(Somehow execute outcome 2)
but I don't know how to make the outcomes contain code that I will write later, so any help would be appreciated!
The pythonic way is to use a dictionary with function objects:
def outcome1():
print("Outcome 1")
def outcome2():
print("Outcome 2")
def event(text, triggers):
print(text)
time.sleep(1)
choice= input("What will you do?")
triggers[choice]()
event("You can got west or east.", {
"go west": outcome1,
"go east": outcome2,
})
I do hope I'm understanding your question appropriately, so feel free to correct me if this isn't on target.
When you create a new instance of the class Event (and it's convention to capitalize the first character of classes). Assign a function to the value of outcome1 and outcome2, which will be executed according to their respective triggers.
def outcomeFunction1():
#Whatever you'd like to do for first outcome
print("Hello")
def outcomeFunction2():
#Whatever you'd like to do for second outcome
print("Bye")
Event('text', 'trigger1', 'trigger2', outcomeFunction1(), outcomeFunction2())
In your class definition write:
if choice == trigger1:
outcome1
elif choice == trigger2:
outcome2
Hope this helps!
Why is this in init? Are you really going to create new instance on every new event? To your question, if I got your question correctly, then this is code you may want to have.
def outcome1(text):
print("outcome1: %s" % text)
def outcome2(text):
print ("outcome2: %s" % text)
trigger_map = {
trigger1 : outcome1,
trigger2 : outcome2,
}
class event(object):
def __init__(self, trigger_map):
sefl._tmap = trigger_map
def onEvent (self, text):
print(text)
time.sleep(1)
choice= input("What will you do?")
return self._tmap [choice] (text)
Sorry if I misunderstood your question, but here I go...
The answer here depends on whether you want to have to make a new instance for every event. If you do, the code might look something like this (and as Jeremy E. pointed out, it's a good idea to capitalize the first character of classes):
class Event:
def __init__(self, text, question, trigger1, trigger2, outcome1, outcome2):
self.text, self.question = text, question
self.trigger1, self.trigger2 = trigger1, trigger2
self.outcome1, self.outcome2 = outcome1, outcome2
def get_choice(self):
print(self.text)
self.choice = input(self.question)
def execute(self):
if self.choice == self.trigger1:
exec(self.outcome1)
if self.choice == self.trigger2:
exec(self.outcome2)
I used the exec function to execute the outcomes. I moved some of the code to different functions, leaving the __init__ function to save the variables for the other functions. Then, you can make an instance for each event, like this:
room = Event("You are in a room with exits to the north and east.", "Where will you go?", "north", "east", "print('You went north.')", "print('You went east.'")
Notice I also added a question parameter, as to not force the What will you do? question on every event.
Now, if you wanted to only need one instance of the Event class, the code is a bit different:
class Event:
def make_event(self, text, question, trigger1, trigger2, outcome1, outcome2):
print(text)
choice = input(question)
if choice == trigger1:
exec(outcome1)
if choice == trigger2:
exec(outcome2)
You can see that the code is much shorter. To create an event with this, you would do something like this (with the same example as before):
event = Event() # This is only needed once, at the start of the program
event.make_event("You are in a room with exits to the north and east.", "Where will you go?", "north", "east", "print('You went north.')", "print('You went east.')")
EDIT: You may want to remove the class definition entirely, leaving just the make_event function I wrote for you above.
You can choose between either of these class examples, or use their ideas to make a customized one. I hope I answered your question.

Tips on using the with statement in Python

Two questions regarding the with statement in Python. They come from a real-life problem so I keep it real. I am dealing with a pinch valve in order to control the flow in a pipe.
The program drives the valve with a class looking like that:
class Valve(object):
"""This class drives a pinch valve."""
def __init__(self):
self.close()
def open(self):
print('Open the valve.')
self.state = 'opened'
def close(self):
print('Close the valve.')
self.state = 'closed'
def print_state(self):
print('The valve is '+self.state+'.')
For some operations, I need exactly what the with statement does with a file (the file is closed at the end or if an error is raised), so I added a function to the class Valve and another class:
def opened(self):
return ContextManagerOpenedValve(self)
class ContextManagerOpenedValve(object):
def __init__(self, valve):
self.valve = valve
def __enter__(self):
self.valve.open()
return self.valve
def __exit__(self, type, value, traceback):
self.valve.close()
Then these lines seem to work as I expected:
def do_something():
print('For the sake of simplicity, this function does nothing.')
valve = Valve()
valve.print_state()
with valve.opened():
valve.print_state()
do_something()
valve.print_state()
My first question: what is the right way to obtain such result? Am I right to use the with statement for that? Can't I do that in a smarter way without defining the class ContextManagerOpenedValve?
Then I need to do something like this:
USE_VALVE = False # or USE_VALVE = True
if USE_VALVE:
with valve.opened():
do_something()
else:
do_something()
I dislike this solution since the function do_something is run anyway so it would be much nicer to avoid the repetition of "do_something()".
My second question: Is there a way to obtain the same result without
repeating do_something() twice ?
Sure, you can do it like so:
def valve_control(valve, use_value=False):
if use_value:
return ContextManagerOpenedValve(valve)
else:
return SomeFakeContextManager()
Then your call looks like:
with valve_control(value, USE_VALVE):
do_something()

Any tips for a more pythonic way to implement state machine behavior?

For brevity, I'm just showing what can/must occur in states. I haven't run into any oddities in the state machine framework itself.
Here is a specific question:
Do you find it confusing that we have to return StateChange(...) and StateMachineComplete(...) whereas some of the of the other actions like some_action_1(...) and some_action_2(...) need not be returned - they're just direct method invocations?
I think that StateChange(...) needs to return because otherwise code beyond the StateChange(...) call will be executed. This isn't how a state machine should work! For example see the implementation of event1 in the ExampleState below
import abc
class State(metaclass=abc.ABCMeta):
# =====================================================================
# == events the state optionally or must implement ====================
# =====================================================================
# optional: called when the state becomes active.
def on_state_entry(self): pass
# optional: called when we're about to transition away from this state.
def on_state_exit(self): pass
#abc.abstractmethod
def event1(self,x,y,z): pass
#abc.abstractmethod
def event2(self,a,b): pass
#abc.abstractmethod
def event3(self): pass
# =====================================================================
# == actions the state may invoke =====================================
# =====================================================================
def some_action_1(self,c,d,e):
# implementation omitted for brevity
pass
def some_action_2(self,f):
# implementation omitted for brevity
pass
class StateChange:
def __init__(self,new_state_type):
# implementation omitted for brevity
pass
class StateMachineComplete: pass
class ExampleState(State):
def on_state_entry(self):
some_action_1("foo","bar","baz")
def event1(self,x,y,z):
if x == "asdf":
return StateChange(ExampleState2)
else:
return StateChange(ExampleState3)
print("I think it would be confusing if we ever got here. Therefore the StateChange calls above are return")
def event2(self,a,b):
if a == "asdf":
return StateMachineComplete()
print("As with the event above, the return above makes it clear that we'll never get here.")
def event3(self):
# Notice that we're not leaving the state. Therefore this can just be a method call, nothing need be returned.
self.some_action_1("x","y","z")
# In fact we might need to do a few things here. Therefore a return call again doesn't make sense.
self.some_action_2("z")
# Notice we don't implement on_state_exit(). This state doesn't care about that.
When I need a state machine in Python, I store it as a dictionary of functions. The indices into the dictionary are the current states, and the functions do what they need to and return the next state (which may be the same state) and outputs. Turning the crank on the machine is simply:
state, outputs = machine_states[state](inputs)
By putting the outgoing state changes in code you're obfuscating the whole process. A state machine should be driven by a simple set of tables. One axis is the current state, and the other is the possible events. You have two or three tables:
The "next-state" table that determines the exit state
The "action" table that determines what action to take
The "read" table that determines whether you stay on the current input event or move on to the next.
The third table may or may not be needed depending on the complexity of the input "grammar".
There are more esoteric variations, but I've never found a need for more than this.
I also struggled to find a good state_machine solution in python. So I wrote state_machine
It works like the following
#acts_as_state_machine
class Person():
name = 'Billy'
sleeping = State(initial=True)
running = State()
cleaning = State()
run = Event(from_states=sleeping, to_state=running)
cleanup = Event(from_states=running, to_state=cleaning)
sleep = Event(from_states=(running, cleaning), to_state=sleeping)
#before('sleep')
def do_one_thing(self):
print "{} is sleepy".format(self.name)
#before('sleep')
def do_another_thing(self):
print "{} is REALLY sleepy".format(self.name)
#after('sleep')
def snore(self):
print "Zzzzzzzzzzzz"
#after('sleep')
def big_snore(self):
print "Zzzzzzzzzzzzzzzzzzzzzz"
person = Person()
print person.current_state == person.sleeping # True
print person.is_sleeping # True
print person.is_running # False
person.run()
print person.is_running # True
person.sleep()
# Billy is sleepy
# Billy is REALLY sleepy
# Zzzzzzzzzzzz
# Zzzzzzzzzzzzzzzzzzzzzz
print person.is_sleeping # True

Instantiating a unique object every time when using object composition?

As an example, just a couple of dummy objects that will be used together. FWIW this is using Python 2.7.2.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)
That's probably enough code, you see where I'm going with this. If I call Hammer.break(), I'm calling it on the same instance of the object; if Billy's hammer is broken, so is Tommy's (it's really the same Hammer after all).
Now obviously if the program were limited to just Billy and Tommy as instances of Students, the fix would be obvious - instantiate more Hammers. But clearly I'm asking because it isn't that simple, heh. I would like to know if it's possible to create objects which show up as unique instances of themselves for every time they're called into being.
EDIT: The kind of answers I'm getting lead me to believe that I have a gaping hole in my understanding of instantiation. If I have something like this:
class Foo(object):
pass
class Moo(Foo):
pass
class Guy(object):
def __init__(self, thing):
self.thing = thing
Bill = Guy(Moo())
Steve = Guy(Moo())
Each time I use Moo(), is that a separate instance, or do they both reference the same object? If they're separate, then my whole question can be withdrawn, because it'll ahve to make way for my mind getting blown.
You have to create new instances of the Tool for each Student.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
# Instead of instance, make it a callable that returns a new one
def Hammer():
return Tool('hammer')
# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
I'll try to be brief. Well.. I always try to be brief, but my level of success is pretty much random.randint(0, never). So yeah.
Lol. You even failed to be brief about announcing that you will try to be brief.
First, we need to be clear about what "called into being" means. Presumably you want a new hammer every time self.tool = object happens. You don't want a new instance every time, for example, you access the tool attribute, or you'd always a get a new, presumably unbroken, hammer every time you check self.tool.broken.
A couple approaches.
One, give Tool a copy method that produces a new object that should equal the original object, but be a different instance. For example:
class Tool:
def __init__(self, kind):
self.kind = kind
self.broken = False
def copy(self):
result = Tool(self.kind)
result.broken = self.broken
return result
Then in Student's init you say
self.tool = tool.copy()
Option two, use a factory function.
def makehammer():
return Tool(hammer)
class Student:
def __init__(self, factory):
self.tool = factory()
Billy = Student(makehammer)
I can't think any way in Python that you can write the line self.tool = object and have object automagically make a copy, and I don't think you want to. One thing I like about Python is WYSIWYG. If you want magic use C++. I think it makes code hard to understand when you not only can't tell what a line of code is doing, you can't even tell it's doing anything special.
Note you can get even fancier with a factory object. For example:
class RealisticFactory:
def __init__(self, kind, failurerate):
self.kind = kind
self.failurerate = failurerate
def make(self):
result = Tool(self.kind)
if random.random() < self.failurerate:
result.broken = True
if (self.failurerate < 0.01):
self.failurerate += 0.0001
return result
factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
You could change your lines like this:
Billy = Student(Tool('hammer'))
Tommy = Student(Tool('hammer'))
That'll produce a distinct instance of your Tool class for each instance of the Student class. the trouble with your posted example code is that you haven't "called the Tool into being" (to use your words) more than once.
Just call Tool('hammer') every time you want to create a new tool.
h1 = Tool('hammer')
h2 = Tool('hammer')
Billy = Student(h1)
Tommy = Student(h2)
Oh wait, I forgot, Python does have magic.
class Student:
def __setattr__(self, attr, value):
if attr == 'tool':
self.__dict__[attr] = value.copy()
else:
self.__dict__[attr] = value
But I still say you should use magic sparingly.
After seeing the tenor of the answers here and remembering the Zen of Python, I'm going to answer my own dang question by saying, "I probably should have just thought harder about it."
I will restate my own question as the answer. Suppose I have this tiny program:
class Item(object):
def __init__(self):
self.broken = False
def smash(self):
print "This object broke."
self.broken = True
class Person(object):
def __init__(self, holding):
self.holding = holding
def using(self):
if self.holding.broken != True:
print "Pass."
else:
print "Fail."
Foo = Person(Item())
Bar = Person(Item())
Foo.holding.smash()
Foo.using()
Bar.using()
The program will return "Fail" for Foo.using() and "Pass" for Bar.using(). Upon actually thinking about what I'm doing, "Foo.holding = Item()" and "Bar.holding = Item()" are clearly different instances. I even ran this dumpy program to prove it worked as I surmised it did, and no surprises to you pros, it does. So I withdraw my question on the basis that I wasn't actually using my brain when I asked it. The funny thing is, with the program I've been working on, I was already doing it this way but assuming it was the wrong way to do it. So thanks for humoring me.

Categories

Resources