I have a class which exposes two methods. These methods are actually visual syntax sugar, they ultimately point to the same code which behaves differently, depending on the payload it receives:
class Action:
def on(self):
self.action(1)
def off(self):
self.action(2)
def action(self, payload):
# a long function which does many things and uses the payload from on() and off()
print(payload)
a = Action()
a.on()
a.off()
Is there a way to define on() and off() so that they point to the same code (the one in action()), which would behave differently depending on the name of the method which was called?
I could of course pass the action in the call:
class Action:
def action(self, what):
payload = 1 if what == 'on' else 0
# a long function which does many things and uses the payload from on() and off()
print(payload)
a = Action()
a.action('on')
a.action('off')
but I want to keep an on/off structure of the class methods.
This is very similar to your first example, but are you looking for functools.partial?
You can use it to automatically set some arguments into a function, and the return value is a function object as well. Using it, you could do this:
import functools
class Abc(object):
def action(self, payload):
print(payload)
def __init__(self):
self.off = functools.partial(self.action, payload=1)
self.on = functools.partial(self.action, payload=2)
Then, you have your action, on, and off function, all working as expected:
foo = Abc()
foo.on()
>>> 2
foo.off()
>>> 1
foo.action(9)
>>> 9
Using partial is a semantically stronger way to say that this is syntactic sugar for calling another function. A function declaration such as def on(self): ... could be anything, while self.on = functools.partial(action,...) explicitly states that your tying one function to the other.
def _action_maker(payload):
def _action(self):
return self.action(payload)
return _action
class Action:
on = _action_maker(1)
off = _action_maker(2)
def action(self, payload):
# a long function which does many things and uses the payload from on() and off()
print(payload)
But personally I don't like this because my editor doesn't understand it and gives me false warnings.
Try inspect
import inspect
class Action:
def on(self):
self.action(1)
def off(self):
self.action(2)
def action(self, payload):
# a long function which does many things and uses the payload from on() and off()
currentframe = inspect.currentframe()
callername = inspect.getouterframes(currentframe, 2)[1][3]
if callername =='on':
print('on')
elif callername == 'off':
print('off')
a = Action()
a.on() #on
a.off() #off
Or simply pass hidden argument
class Action:
def on(self):
self.action(1,'on')
def off(self):
self.action(2, 'off')
def action(self, payload, callername):
# a long function which does many things and uses the payload from on() and off()
if callername =='on':
print('on')
elif callername == 'off':
print('off')
a = Action()
a.on() #on
a.off() #off
Related
I declared 3 functions earlier, this is just a goofy text based cookie clicker-esque game.
dostuff={"" : turn() , "help" : helpf() , "invest" : invest() }
while done != True:<br>
do = input("What do you want to do? ")
do = do.lower()
if do == "" or do == "help" or do == "invest":
dostuff[do]
elif do == "quit":
done = True
So when I use dostuff["turn"] it does nothing (the function is supposed to print some things). I have the same problem with the other options.
Your parentheses must be omitted in the dict, and then put at the end of the dict call. You define a function, which becomes a python object. You reference the object with the dict, and then you call the function with the object reference followed by parentheses:
def one():
print("one")
def two():
print("two")
do_stuff = {
"one": one,
"two": two
}
do_stuff["one"]()
prints:
"one"
You can take this concept of executing calls with string inputs a lot farther by familiarizing yourself with the builtin functions of python.
https://docs.python.org/2/library/functions.html
For example, you can create a class and call its methods or properties using text based input with the getattr method:
class do_stuff():
def __init__(self):
pass
def one(self):
print("one")
def two(self):
print("two")
doer = do_stuff()
inp = "one"
getattr(doer, inp)()
prints->
"one"
I have a very long function func which takes a browser handle and performs a bunch of requests and reads a bunch of responses in a specific order:
def func(browser):
# make sure we are logged in otherwise log in
# make request to /search and check that the page has loaded
# fill form in /search and submit it
# read table of response and return the result as list of objects
Each operation require a large amount of code due to the complexity of the DOM and they tend to grow really fast.
What would be the best way to refactor this function into smaller components so that the following properties still hold:
the execution flow of the operations and/or their preconditions is guaranteed just like in the current version
the preconditions are not checked with asserts against the state, as this is a very costly operation
func can be called multiple times on the browser
?
Just wrap the three helper methods in a class, and track which methods are allowed to run in an instance.
class Helper(object):
def __init__(self):
self.a = True
self.b = False
self.c = False
def funcA(self):
if not self.A:
raise Error("Cannot run funcA now")
# do stuff here
self.a = False
self.b = True
return whatever
def funcB(self):
if not self.B:
raise Error("Cannot run funcB now")
# do stuff here
self.b = False
self.c = True
return whatever
def funcC(self):
if not self.C:
raise Error("Cannot run funcC now")
# do stuff here
self.c = False
self.a = True
return whatever
def func(...):
h = Helper()
h.funcA()
h.funcB()
h.funcC()
# etc
The only way to call a method is if its flag is true, and each method clears its own flag and sets the next method's flag before exiting. As long as you don't touch h.a et al. directly, this ensures that each method can only be called in the proper order.
Alternately, you can use a single flag that is a reference to the function currently allowed to run.
class Helper(object):
def __init__(self):
self.allowed = self.funcA
def funcA(self):
if self.allowed is not self.funcA:
raise Error("Cannot run funcA now")
# do stuff
self.allowed = self.funcB
return whatever
# etc
Here's the solution I came up with. I used a decorator (closely related to the one in this blog post) which only allows for a function to be called once.
def call_only_once(func):
def new_func(*args, **kwargs):
if not new_func._called:
try:
return func(*args, **kwargs)
finally:
new_func._called = True
else:
raise Exception("Already called this once.")
new_func._called = False
return new_func
#call_only_once
def stateA():
print 'Calling stateA only this time'
#call_only_once
def stateB():
print 'Calling stateB only this time'
#call_only_once
def stateC():
print 'Calling stateC only this time'
def state():
stateA()
stateB()
stateC()
if __name__ == "__main__":
state()
You'll see that if you re-call any of the functions, the function will throw an Exception stating that the functions have already been called.
The problem with this is that if you ever need to call state() again, you're hosed. Unless you implement these functions as private functions, I don't think you can do exactly what you want due to the nature of Python's scoping rules.
Edit
You can also remove the else in the decorator and your function will always return None.
Here a snippet I used once for my state machine
class StateMachine(object):
def __init__(self):
self.handlers = {}
self.start_state = None
self.end_states = []
def add_state(self, name, handler, end_state=0):
name = name.upper()
self.handlers[name] = handler
if end_state:
self.end_states.append(name)
def set_start(self, name):
# startup state
self.start_state = name
def run(self, **kw):
"""
Run
:param kw:
:return:
"""
# the first .run call call the first handler with kw keywords
# each registered handler should returns the following handler and the needed kw
try:
handler = self.handlers[self.start_state]
except:
raise InitializationError("must call .set_start() before .run()")
while True:
(new_state, kw) = handler(**kw)
if isinstance(new_state, str):
if new_state in self.end_states:
print("reached ", new_state)
break
else:
handler = self.handlers[new_state]
elif hasattr(new_state, "__call__"):
handler = new_state
else:
return
The use
class MyParser(StateMachine):
def __init__(self):
super().__init__()
# define handlers
# we can define many handler as we want
self.handlers["begin_parse"] = self.begin_parse
# define the startup handler
self.set_start("begin_parse")
def end(self, **kw):
logging.info("End of parsing ")
# no callable handler => end
return None, None
def second(self, **kw):
logging.info("second ")
# do something
# if condition is reach the call `self.end` handler
if ...:
return self.end, {}
def begin_parse(self, **kw):
logging.info("start of parsing ")
# long process until the condition is reach then call the `self.second` handler with kw new keywords
while True:
kw = {}
if ...:
return self.second, kw
# elif other cond:
# return self.other_handler, kw
# elif other cond 2:
# return self.other_handler 2, kw
else:
return self.end, kw
# start the state machine
MyParser().run()
will print
INFO:root:start of parsing
INFO:root:second
INFO:root:End of parsing
You could use local functions in your func function. Ok, they are still declared inside one single global function, but Python is nice enough to still give you access to them for tests.
Here is one example of one function declaring and executing 3 (supposedly heavy) subfunctions. It takes one optional parameter test that when set to TEST prevent actual execution but instead gives external access to individual sub-functions and to a local variable:
def func(test=None):
glob = []
def partA():
glob.append('A')
def partB():
glob.append('B')
def partC():
glob.append('C')
if (test == 'TEST'):
global testA, testB, testC, testCR
testA, testB, testC, testCR = partA, partB, partC, glob
return None
partA()
partB()
partC()
return glob
When you call func, the 3 parts are executed in sequence. But if you first call func('TEST'), you can then access the local glob variable as testCR, and the 3 subfunctions as testA, testB and testC. This way you can still test individually the 3 parts with well defined input and control their output.
I would insist on the suggestion given by #user3159253 in his comment on the original question:
If the sole purpose is readability I would split the func into three "private" > or "protected" ones (i.e. _func1 or __func1) and a private or protected property > which keeps the state shared between the functions.
This makes a lot of sense to me and seems more usual amongst object oriented programming than the other options. Consider this example as an alternative:
Your class (teste.py):
class Test:
def __init__(self):
self.__environment = {} # Protected information to be shared
self.public_stuff = 'public info' # Accessible to outside callers
def func(self):
print "Main function"
self.__func_a()
self.__func_b()
self.__func_c()
print self.__environment
def __func_a(self):
self.__environment['function a says'] = 'hi'
def __func_b(self):
self.__environment['function b says'] = 'hello'
def __func_c(self):
self.__environment['function c says'] = 'hey'
Other file:
from teste import Test
t = Test()
t.func()
This will output:
Main function says hey guys
{'function a says': 'hi', 'function b says': 'hello', 'function c says': 'hey'}
If you try to call one of the protected functions, an error occurs:
Traceback (most recent call last):
File "C:/Users/Lucas/PycharmProjects/testes/other.py", line 6, in <module>
t.__func_a()
AttributeError: Test instance has no attribute '__func_a'
Same thing if you try to access the protected environment variable:
Traceback (most recent call last):
File "C:/Users/Lucas/PycharmProjects/testes/other.py", line 5, in <module>
print t.__environment
AttributeError: Test instance has no attribute '__environment'
In my view this is the most elegant, simple and readable way to solve your problem, let me know if it fits your needs :)
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()
In the following class the property wheels has a cached value.
import time
class Car:
#property
def wheels(self):
if not hasattr(self, '_wheels'):
self._count_wheels()
return self._wheels
def _count_wheels(self):
time.sleep(10) # simulate a long calculation
self._wheels = 4
if __name__ == "__main__":
c = Car()
print(c.wheels) # calls _count_wheels() once
print(c.wheels) # no calls to _count_wheels()
I want to test that the first call to c.wheels calls once the method _count_wheels(); while the second call to c.wheels doesn't call the method _count_wheels()
I'd like to use unittest.mock
One simple solution is to mock the object yourself:
if __name__ == "__main__":
count = 0
to_mock = Car._count_wheels
def mocked(self):
global count
count +=1
if count>1:
raise ValueError("Called twice")
to_mock(self)
Car._count_wheels = mocked
c = Car()
print(c.wheels) # calls _count_wheels() once
try:
print(c.wheels) # no calls to _count_wheels()
except ValueError as e:
print e
You can try it with this modified Car class:
class Car:
#property
def wheels(self):
#if not hasattr(self, '_wheels'):
self._count_wheels()
return self._wheels
def _count_wheels(self):
#time.sleep(10) # simulate a long calculation
self._wheels = 4
And you will see it raises the exception. Since python is so dynamic this approach is always valid and sometimes is very useful, but of course you can use a third party tool as well ;)
I have a nested function that I'm using as a callback in pyglet:
def get_stop_function(stop_key):
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
return stop_on_key
pyglet.window.set_handler('on_key_press', get_stop_function('ENTER'))
But then I run into problems later when I need to reference the nested function again:
pyglet.window.remove_handler('on_key_press', get_stop_function('ENTER'))
This doesn't work because of the way python treats functions:
my_stop_function = get_stop_function('ENTER')
my_stop_function is get_stop_function('ENTER') # False
my_stop_function == get_stop_function('ENTER') # False
Thanks to two similar questions I understand what is going on but I'm not sure what the workaround is for my case. I'm looking through the pyglet source code and it looks like pyglet uses equality to find the handler to remove.
So my final question is: how can I override the inner function's __eq__ method (or some other dunder) so that identical nested functions will be equal?
(Another workaround would be to store a reference to the function myself, but that is duplicating pyglet's job, will get messy with many callbacks, and anyways I'm curious about this question!)
Edit: actually, in the questions I linked above, it's explained that methods have value equality but not reference equality. With nested functions, you don't even get value equality, which is all I need.
Edit2: I will probably accept Bi Rico's answer, but does anyone know why the following doesn't work:
def get_stop_function(stop_key):
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
stop_on_key.__name__ = '__stop_on_' + stop_key + '__'
stop_on_key.__eq__ = lambda x: x.__name__ == '__stop_on_' + stop_key + '__'
return stop_on_key
get_stop_function('ENTER') == get_stop_function('ENTER') # False
get_stop_function('ENTER').__eq__(get_stop_function('ENTER')) # True
You could create a class for your stop functions and define your own comparison method.
class StopFunction(object):
def __init__(self, stop_key):
self.stop_key = stop_key
def __call__(self, symbol, _):
if symbol == getattr(pyglet.window.key, self.stop_key):
pyglet.app.exit()
def __eq__(self, other):
try:
return self.stop_key == other.stop_key
except AttributeError:
return False
StopFunciton('ENTER') == StopFunciton('ENTER')
# True
StopFunciton('ENTER') == StopFunciton('FOO')
# False
the solution is to keep a dictionary containing the generated functions around,
so that when you make the second call, you get the same object as in the first call.
That is, simply build some memoization logic, or use one of the libraries
existing with memoizing decorators:
ALL_FUNCTIONS = {}
def get_stop_function(stop_key):
if not stop_key in ALL_FUNCTIONS:
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
ALL_FUNCTIONS[stop_key] = stop_on_key
else:
stop_on_key = ALL_FUNCTIONS[stop_key]
return stop_on_key
You can generalize Bi Rico's solution to allow wrapping any functions up with some particular equality function pretty easily.
The first problem is defining what the equality function should check. I'm guessing for this case, you want the code to be identical (meaning functions created from the same def statement will be equal, but two functions created from character-for-character copies of the def statement will not), and the closures to be equal (meaning that if you call get_stop_function with two equal but non-identical stop_keys the functions will be equal), and nothing else to be relevant. But that's just a guess, and there are many other possibilities.
Then you just wrap a function the same way you'd wrap any other kind of object; just make sure __call__ is one of the things you delegate:
class EqualFunction(object):
def __init__(self, f):
self.f = f
def __eq__(self, other):
return (self.__code__ == other.__code__ and
all(x.cell_contents == y.cell_contents
for x, y in zip(self.__closure__, other.__closure__)))
def __getattr__(self, attr):
return getattr(self.f, attr)
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
If you want to support other dunder methods that aren't required to go through getattr (I don't think any of them are critical for functions, but I could be wrong…), either do it explicitly (as with __call__) or loop over them and add a generic wrapper to the type for each one.
To use the wrapper:
def make_f(i):
def f():
return i
return EqualFunction(f)
f1 = f(0)
f2 = f(0.0)
assert f1 == f2
Or, notice that EqualFunction actually works as a decorator, which may be more readable.
So, for your code:
def get_stop_function(stop_key):
#EqualFunction
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
return stop_on_key