Python Recursive Try-Except [duplicate] - python

This question already has an answer here:
Recursive code returns None [duplicate]
(1 answer)
Closed 3 years ago.
I'm trying to make a recursive try-except function in order to deal with occasional errors. In pandas, we can create quantiles for dataframes, however, it might happen that the borders of two or more quantiles coincide and, thus, there isn't a border actually. Sopandas will throw an error. To circumvent this, you can just lower the number of quantiles and that's what I'm attempting to do here.
import pandas as pd
quantiled, dict_bins = recursive_lower_labels(model_quant = model_quant,
n_quantiles = n_quantiles,
reverse = reverse)
def recursive_lower_labels(model_quant,
n_quantiles,
reverse = False):
'''
Recursively lowers the number of labels until it works.
'''
if n_quantiles == 0: # base case
return 'Error: There are no Quantiles to be used.'
# Not very important...
# I'm using this to either use normal or reverse labels.
if reverse == False:
labels = range(1, n_quantiles + 1)
elif reverse == True:
labels = range(n_quantiles, 0, -1)
try:
qt, dc = pd.qcut(model_quant,
q = n_quantiles,
labels = labels,
retbins = True)
return qt, dc
except:
recursive_lower_labels(model_quant,
n_quantiles = n_quantiles - 1,
reverse = reverse)
The error I'm getting is (pointing to the function call up top):
cannot unpack non-iterable NoneType object
I suspect it's one of two mistakes I'm making:
There is a problem with scoping somewhere. Maybe n_quantiles? It doesn't seem likely, from my unexperienced debugging.
There is an issue with respect to placing a return before the function recursive call inside the except statement. I've tried a lot of combinations here, even with an extra else at the end and it didn't work either.
By the way, if not recursive, this does work.
EDIT:
My question was marked as a duplicate and this edit is to address that evaluation. Firstly, it was marked as a duplicate of a question that was also marked as such, which is strange, but not that relevant. The important and useful concept that differs those questions from mine is that they both had functions that, although recursive, did not necessarily return something all the time, while mine did return something always and, thus, made it seem that the return on the recursion was not necessary — which turns out to not be true.

All you have to do is return your recursion. With some light refactoring:
def recursive_lower_labels(model_quant,
n_quantiles,
reverse=False):
"""
Recursively lowers the number of labels until it works.
"""
if n_quantiles == 0: # base case
return 'Error: There are no Quantiles to be used.'
# Not very important...
# I'm using this to either use normal or reverse labels.
if reverse:
labels = range(n_quantiles, 0, -1)
else:
labels = range(1, n_quantiles + 1)
try:
return pd.qcut(model_quant,
q=n_quantiles,
labels=labels,
retbins=True)
except:
return recursive_lower_labels(model_quant,
n_quantiles=n_quantiles - 1,
reverse=reverse)

Related

Multiply method for a binary list

I am passing parameters x and y to this multIter class in the console as follows:
import BigInteger as bi
x =bi.BigInteger([1,0,1,1])
y= bi.BigInteger([1,0,0,1])
bi.BigInteger.multIter(x,y)
I am currently getting the wrong answers when I call this method. There is some logical error in the line answer = self.add(x), that I am not able to figure out.
def multIter(self,y):
a_bits = self._bits
b_bits = y._bits
answer = 0
for i in range(0,len(b_bits)):
if(b_bits[i] == 1):
x= self.multByPow2(i)
answer = self.add(x)
return answer
There is indeed a logical error at the statement you indicate. In each iteration that has a 1-bit, that answer = self.add(x) assignment ignores the previous value of answer, and just overwrites it. You should not add x to self, but add x to answer. For that to work, answer must be initialised as an instance of BigInteger, and not as 0.
So replace that part of the code with:
answer = bi.BigInteger([0])
for i in range(0,len(b_bits)):
if b_bits[i] == 1:
x = self.multByPow2(i)
answer = answer.add(x)
This will work fine assuming the other used methods of BigInteger have been correctly implemented.

Updating local variable within function not working correctly [duplicate]

This question already has answers here:
Static variable in Python?
(6 answers)
Closed 1 year ago.
I'm trying to write a function that updates its local variable each time it is run but it is not working for some reason.
def max_equity(max_equity=0):
if current_equity() > max_equity:
max_equity = current_equity()
print(max_equity)
return max_equity
else:
print(max_equity)
return max_equity
and the function which it is calling
def current_equity():
for n in range(len(trade_ID_tracker)-1):
equity_container = 0
if (trade_ID_tracker[n,2]) == 0:
break
else:
if (trade_ID_tracker[n, 1].astype(int) == long):
equity_container += (df.loc[tbar_count,'Ask_Price'] - trade_ID_tracker[n, 2]) * trade_lots * pip_value * 1000
elif (trade_ID_tracker[n, 1].astype(int) == short):
equity_container += 0 - (df.loc[tbar_count,'Ask_Price'] - trade_ID_tracker[n, 2]) * trade_lots * pip_value * 10000
return (current_balance + equity_container)
but for some reason the max_equity() function prints current_equity() which I can only imagine means that either:
if current_equity() > max_equity:
is not doing it's job and is triggering falsely
or
max_equity = current_equity()
is not doing its job and max_equity starts at zero every time it is run.
In other words if I put max_equity() in a loop where current_equity() is
[1,2,3,4,5,4,3,2,1]
then max_equity() should return
[1,2,3,4,5,5,5,5,5]
But instead it returns
[1,2,3,4,5,4,3,2,1]
Here's a quick example test
ar = [1,2,3,4,5,4,3,2,1]
def stuff(max_equity=0):
if ar[n] > max_equity:
max_equity = ar[n]
print(max_equity)
else:
print(max_equity)
for n in range(len(ar)):
stuff()
Either way I'm kind of stumped.
Any advice?
local function variables are reset at each function call. This is essential for the behavior of functions as idempotent, and is a major factor in the success of the procedural programming approach: a function can be called form multiple contexts, and even in parallel, in concurrent threads, and it will yield the same result.
A big exception, and most often regarded as one of the bigger beginner traps of Python is that, as parameters are reset to the default values specified in the function definition for each call, if these values are mutable objects, each new call will see the same object, as it has been modified by previous calls.
This means it could be done on purpose by, instead of setting your default value as 0 you would set it as a list which first element was a 0. At each run, you could update that value, and this change would be visible in subsequent calls.
This approach would work, but it is not "nice" to depend on a side-effect of the language in this way. The official (and nice) way to keep state across multiple calls in Python is to use objects rather than functions.
Objects can have attributes tied to them, which are both visible and writable by its methods - which otherwise have their own local variables which are re-started at each call:
class MaxEquity:
def __init__(self):
self.value = 0
def update(max_equity=0):
current = current_equity()
if current > self.value:
self.value = current
return self.value
# the remainder of the code should simply create a single instance
# of that like ]
max_equity = MaxEquity()
# and eeach time yoiu want the max value, you should call its "update"
# method

Pythonic way to detect which optional parameters are present

Still fairly new to python.
I was wondering what would be a good way of detecting what output response a python program were to choose.
As an example, if you were to make a speed/distance/time calculator, if only 2 input were ever given, how would you detect which was the missing input and therefore the output? I can think of some fairly crude ways but I was wondering if there was anything else if more complex tasks were to come into play.
I guess something like:
def sdf(speed=0, distance=0, time=0):
# detect which parameter has no input / equals 0
# calculate result
# return result
sdf(speed=10, distance=2)
Any ideas?
Python allows you to change types of variables on the fly. Since you are working with integers and 0 could be a useful value in your calculations, your default 'not present' value should be None:
def sdf(speed=None, time=None, distance=None):
if speed is None:
return calculate_speed(time, distance), time, distance
if time is None:
return speed, calculate_time(speed, distance), distance
if distance is None:
return speed, time, calculate_distance(speed, time)
# All paramters have been set! Maybe check if all three are correct
return speed, time, distance
speed, time, distance = sdf(speed=1, distance=2)
This way you don't have to find out what happened afterwards. This function will give you all three values, given you gave it at least 2 out of the 3.
If your program flow allows for multiple values be None, your functions calculate_XY should throw an exception if they detect it. So in this case:
def calculate_distance(speed, time)
return speed * time
It will throw an unsupported operand exception(TypeError), so no need to clutter your code with useless asserts.
If you really don't know how many parameters will be set, do something like this:
try:
retval = sdf(None, None, x)
except TypeError as e:
print(e)
handle_exception(e)
Also just a heads up: the is operator in Python checks if the objects are the same object, not their value. Since objects that are assigned to None are just a 'pointer to the global None object'(simplification), checking whether a value 'contains' None with is is preferred. However be aware of this:
a = b = list()
a is b
True
# a and b are 'pointers' to the same list object
a = list()
b = list()
a is b
False
a == b
True
# a and b contain 2 different list objects, but their contents are identical
Just be aware that to compare values use == and to check if they are the same object, use is.
HTH
This is what I would do :
def sdf(distance=None, speed=None, time=None):
"""Calculate the missing speed, distance time value
returns a 3-tuple (speed, distance, time)
raises ValueError if more than one or no unknowns are given"""
if (distance, speed,time).count(None) > 1:
raise ValueError('Error - more than one unknown provided')
if (distance, speed,time).count(None) == 0:
raise ValueError('Not sure what to calculate - all paramaters provided')
if speed is None:
return distance/time, distance, time
if time is None:
return speed, distance, distance/speed
if distance is None:
return speed, speed*time, time
You should use multiple functions and call the one needed.
def CalculateTravelTime(distance, speed)
def CalculateTravelSpeed(distance, time)
def CalculateTravelDistance(speed, time)

Python: Multiple instances seem to be sharing properties (or something?!)

Python is my first language, and I am very new to it, so the answer may be very clear, but after many hours looking and experimenting, I am not sure what is causing the problem.
An overview of the module:
The DicePool module is meant to manage collections of "dice", stored as dictionary items. Each dictionary key (herein poolKey) has a list containing information about one "type" of dice, most importantly, a tuple describing its faces and an integer representing the quantity of "dice" type 'x' in the "pool."
My specific question regards the Transfer method, which used to be two methods (send and receive, basically), but which I thought I could combine into one method. When the test code at the bottom runs, I'd like it to leave dp.dictPool[poolKey][1] == 0 and dp2.dictPool[poolKey][1] == 2. But in every attempt I've made, the values come out the same. Sorry I can't categorize this question better . . . I don't really know what the problem is.
Anyway, one half of the Transfer method is supposed to run for the "sender" instance, and one half is supposed to run for the "receiver" instance.
import random
class DicePool(object):
def __init__(self):
self.dictPool = {}
def AddDice(self, poolKey, faces = 6, quant = 1, color = "white"):
'''faces must be int or items 'a,b,c'; count must be int or def to 1'''
try: #if count is not an integer, it defaults to 1
quant = int(quant)
except:
print("Quant is not an integer, defaulting to 1")
quant = 1
try: #if faces is can be int, a list is built of numbers 1 to faces
faces = int(faces)
if faces < 2: #a 1 or 0-sided die breaks the program
faces = 2
tempList = []
for i in range(1, faces+1):
tempList.append(i)
faces = tempList
except: #if faces is not an integer, it is split into list items by ","
faces = faces.split(",")
if poolKey in self.dictPool.keys(): #if the key already exists in pool
self.dictPool[poolKey][1] += quant #add to the quantity,
else: #if the key does not already exist, set all attributes
self.dictPool[poolKey] = [faces, quant, color]
def Transfer(self, poolKey, targetPool, sendQuant, senderPool = None):
'''targetPool must be DicePool instance'''
if targetPool:
self.dictPool[poolKey][1] -= sendQuant
targetPool.Transfer(poolKey, None, sendQuant, self)
else:
try:
self.dictPool[poolKey][1] -= sendQuant
except:
self.dictPool[poolKey] = senderPool.dictPool[poolKey]
self.dictPool[poolKey][1] = sendQuant
dp = DicePool()
dp2 = DicePool()
dp.AddDice("d6")
dp.AddDice("d6")
dp.Transfer("d6",dp2,2)
print(dp.dictPool,dp2.dictPool)
The problem is in this line:
self.dictPool[poolKey] = senderPool.dictPool[poolKey]
The values in your dictPools are lists. Here, you set one object's dictPool value to the same list as in the other one. Not a copy of the list, but the same list. So later if you add or subtract from that list, it will affect the other as well, because they are sharing one list object.
Try doing self.dictPool[poolKey] = senderPool.dictPool[poolKey][:]. The [:] grabs the contents of the list rather than the list object iself.
In both parts of the if/else in Transfer you're subtracting the quantity. Don't you want to subtract it in one case but add it in the other?
Is else: try: self.dictPool[poolKey][1] -= sendQuant supposed to be += sendQuant instead?
Might be a semantic issue, not a Python syntax issue.
Here is a version of the Transfer() method that seems to work as intended (more or less), although it's very clumsy looking:
def Transfer(self, poolKey, targetPool, sendQuant, senderPool = None):
'''targetPool must be DicePool instance'''
if isinstance(targetPool, DicePool):
#valid target, so subtract dice here, then call Transfer on target
temp = self.dictPool[poolKey][1:2][0] - sendQuant
print("sent",sendQuant,"leaving",temp)
targetPool.Transfer(poolKey, "Receiver", sendQuant, self)
elif targetPool == "Receiver":
#this executes if a DicePool is named as the targetPool of Transfer
if poolKey in self.dictPool.keys():
self.dictPool[poolKey][1:2][0] += sendQuant
else:
self.dictPool[poolKey] = senderPool.dictPool[poolKey]
self.dictPool[poolKey][1:2] = [sendQuant]
print("now have",self.dictPool[poolKey][1:2][0])
pass
else:
print("Not a valid targetPool, apparently")

Matching Binary operators in Tuples to Dictionary Items

So, I'm working on a Pybrain-type project and I'm stuck on part of it.
So far the program takes in a tuple and assigns a variable to it using 'one of them fancy vars()['string'] statements. Specifically, it takes in a tuple of numbers and assigns it to a 'layerx' value, where x is the number of the layer (in order, layer 1, 2, 3, etc), such that the numbers are the dimensions of that layer.
The part of the program I desperately and humbly come to you for help in is what should be the next step in the program; it takes in a tuple of tuples (the number of tuples must = the number of layers), and the tuples contain 1/0's.
It is supposed to determine what type of Pybrain Layer to use in what layer, and then plugs in that layer's dimension value and, essentially, creates that layer-variable. I've...played with it for a while, and I've gotten a really...twisted...confusing block of code.
Please pardon the convoluted variable names, I thought I was being smart by making them somewhat specific:
moduleconbuff = 0
modulebuffer = 'module'
correspondinglayerbuff = 0
moduleconfigcopy = tuple(moduleconfig)
try: #Always triggers except, but it's pretty screwed up
while correspondinglayerbuff <= len(self.layers): #keeps track of how many layer/module pairs have been assigned
for elm in moduleconfigcopy:
for x in elm:
if x == 1:
moduledimmension = [layerbuff+'%s'%(correspondinglayerbuff)]
modulesdict = {1: pybrain.GaussianLayer(moduledimmension), 2: pybrain.LinearLayer(moduledimmension),\
3: pybrain.LSTMLayer(moduledimmension),4: pybrain.SigmoidLayer(moduledimmension),5: pybrain.TanhLayer(moduledimmension)} #this dict pairs integers with pybrain modules
vars()[modulebuffer +'%s'%(correspondinglayerbuff)]=modulesdict(moduleconbuff) #should return something like 'Module1 = pybrain.GaussianLayer(5) when complete
print vars()[modulebuffer+'%s'%(correspondinglayerbuff)]
moduleconbuff=0
correspondinglayerbuff+=1
print 'Valid: ', moduleconfigcopy, elm
continue
else:
elm = elm[1:]
print 'Invalid: ', moduleconfigcopy, elm
moduleconbuff+=1
except:
print 'Invalid!!!'
I honestly lost track of what was going on in it. The tuple "moduleconfig" in the beginning
was supposed to be a tuple of tuples (nested tuples) with binary operators, it was supposed to stop when one of the tuples has a 1, match that operator with the right module in Pybrain, and then plug this in so the corresponding layer = that module with the dimmensions already listed.
Obviously something went terribly wrong, and it's so fargone that my brain can't make any sense of it...it's lost all it's reason and every time I look at it I get scared...please help me or tell me I created an abomination or something, I guess...
One huge hindrance that's affecting code readability for you is variable naming and style. I've tried to clean it up a little bit for you. It still might not work, but now it's a LOT easier to see what's going on. Please refer to PEP 8, the Python style guide
For starters, I renamed some variables, below. Note that in python, variables should be all lowercase, with separate words connected by an underscore. Constants should be ALL_UPPERCASE:
assigned_layers = correspondinglayerbuff = 0
tuple_of_tuples = moduleconfigcopy = ((0, 1), (0, 0, 1), (0, 1))
dimension = moduledimension
MOD_BUFFER = modulebuffer = 'buffer'
c_buff = moduleconbuff = 0
And here is the while loop (with variable names replaced, and properly indented, with the try... except block removed:
while assigned_layers <= len(self.layers):
for element_tuple in tuple_of_tuples:
for item in element_tuple:
if item: # in python, 0 is treated as boolean False, 1 or any other value is treated as boolean True.
dimension = [layerbuff + str(assigned_layers)] #what is layerbuff?
modules_dict = {
1: pybrain.GaussianLayer(dimension),
2: pybrain.LinearLayer(dimension),
3: pybrain.LSTMLayer(dimension),
4: pybrain.SigmoidLayer(dimension),
5: pybrain.TanhLayer(dimension)
} # Notice how this dict is much easier to read.
vars()[MOD_BUFFER + str(assigned_layers)] = modules_dict[c_buff] #modules_dict is a dict and not a callable object
c_buff = 0
assigned_layers +=1
#No need for continue here, since that's what the if...else does here.
else:
element_tuple = element_tuple[1:] #what is this for?
print 'Invalid: ', tuple_of_tuples, element_tuple
I'm not sure exactly what you are trying to do in this line:
vars()[MOD_BUFFER + str(assigned_layers)] = modules_dict[c_buff] #modules_dict is a dict and not a callable object
Also, you originally had modules_dict(moduleconbuff) which will raise a TypeError as a dict is not a callable object. I'm assuming you meant to retrieve a value by key.
As I said, I'm not quite sure what your trying to do here (probably because I haven't seen the rest of your code), but renaming your variables and using good style should go a long way towards you being able to debug your code. I will continue to edit if you answer my questions/comment.

Categories