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()
Related
I have some code that looks like this.
condition = <expression>
while condition:
<some code>
I would like to be able to write that without having to write a separate statement to create the condition. E.g.,
while <create_condition(<expression>)>:
<some code>
Here are two possibilities that don't work, but that would scratch my itch.
with <expression> as condition:
<some code>
The problem with that is that it doesn't loop. If I embed a while inside the with I'm back where I started.
Define my own function to do this.
def looping_with(<expression>, <some code>):
<define looping_with>
The problem with this is that if <some code> is passed as a lambda expression it is limited to a single expression. None of the workarounds I've seen are attractive.
If <some code> is passed as an actual def one gets a syntax error. You can't pass a function definition as an argument to another function.
One could define the function elsewhere and then pass the function. But the point of with, while, and lambda is that the code itself, not a reference to the code, is embedded in context. (The original version of my code, which is not terrible, is better than that.)
Any suggestions would be appreciated.
UPDATE: (As Dave Beazley likes to say: You're going to hate this.)
I hesitate to offer this example, but this is something like what I'm trying to do.
class Container:
def __init__(self):
self.value = None
class Get_Next:
def __init__(self, gen):
self.gen = gen
def __call__(self, limit, container):
self.runnable_gen = self.gen(limit, container)
return self
def get_next(self):
try:
next(self.runnable_gen)
return True
except StopIteration:
return False
#Get_Next
def a_generator(limit, container):
i = 0
while i < limit:
container.value = i
yield
i += 1
container = Container()
gen = a_generator(5, container)
while gen.get_next():
print(container.value)
print('Done')
When run, the output is:
0
1
2
3
4
Done
P.S. Lest you think this is too far out, there is a very easy way to produce the same result. Remove the decorator from a_generator and then run:
for _ in a_generator(5, container):
print(container.value)
print('Done')
The result is the same.
The problem is that for _ in <something> is too ugly for me.
So, what I'm really looking for is a way to get the functionality of for _ in <something> with nicer syntax. The syntax should (a) indicate that we are establishing a context and (b) looping within that context. Hence the request for a combination of with and while.
You could a context manager class that would help in doing something like that:
class Condition:
def __init__(self, cond):
self.cond = cond
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def __call__(self, *args, **kwargs):
return self.cond(*args, **kwargs)
with Condition(lambda x: x != 3) as condition:
a = 0
while condition(a):
print('a:', a)
a += 1
Output:
a: 0
a: 1
a: 2
I have some app.py in which I do the following:
Trader = Trader(settings)
while True:
try:
Trader.analyse_buys()
Now I have the following in trader.py
def __init__(self):
self.since = self.calculate_since()
...
def analyse_buys():
dosomething()
So the analyse_buys() will run in a loop without a new calculation of the value since.
What could be a possible solution to recalculate my variables in the __init__ function again before starting the function again?
If you need to still save some state in Trader, i.e. instantiating a new one with
trader = Trader()
isn't an option, consider moving the bits that need to be reinitialized into another function, and calling that both within __init__() and from elsewhere:
class Trader:
def __init__(self):
self.state_that_shouldnt_be_re_prepared = ...
self.prepare() # (or whatever is a sensible name)
def prepare(self):
# do things
# ...
trader = Trader()
while ...:
if something:
trader.prepare()
I'm using tornado.websocket, where class-methods are overrides of the WebSocketHandler methods. Anyway, my code look like that:
class SocketHandler(tornado.websocket.WebSocketHandler):
current_ninja_pool = enumerate(return_dependency_lvl_0())
current_ninja = next(current_ninja_pool)
file_to_upload = []
def check_origin(self, origin):
return True
def open(self):
logging.info("A client connected.")
self.run()
def run(self):
if condition:
do_this()
else:
do_that()
self.current_ninja = next(self.current_ninja_pool)
self.run()
def on_message(self, message):
do_a_lot_of_stuff()
if message == 'next one':
self.current_ninja = next(self.current_ninja_pool)
def on_close(self):
logging.info("A client disconnected")
So, what I want is to be able to iterate my enumerate, so that every element can be processed in the methods run or on_message depending on how my client-websocket will answer. The problem is that I want to iterate under particular conditions, and I don't have a clue on how to do this. Since I'm not very familiar with the way you manipulate class- and instance-variables, I'm probably missing a point here.
Thank you
You need an iterator. Luckily, enumerate already returns an iterator; you just need to access that, rather than storing the current item.
I also suspect that current_ninja_pool should be an instance variable, not a class one (which would be shared across all instances of the class).
class SocketHandler(tornado.websocket.WebSocketHandler):
def __init__(self, *args, **kwargs)
self.current_ninja_pool = enumerate(return_dependency_lvl_0())
file_to_upload = []
def run(self):
item = next(self.current_ninja_pool)
do_something_with(item)
I'm writing a driver in Python 2.6 and I need it to be reverse compatible with a previous implementation (I don't have access to the source code).
class new_driver ():
def output(self, state):
if state == True:
self.set_ouput_on()
else:
self.set_output_off()
...
The odd thing is that to keep compatibility I have to pass this output using the format
nd = new_driver()
nd.output = True
How do I pass a value in this way?
Edit: To clarify: my "output" function has to receive the value True in this way in order to execute the function self.set_output_on()
Try using the #property decorator:
#property
def output(self):
return self... #not sure how you are tracking output on/off
#output.setter
def output(self, state):
if state:
self.set_output_on()
else:
self.set_output_off()
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