TypeError: unsupported operand type(s) for +=: 'float' and 'NoneType' in Python 3 - python-3.x

Does anyone know why I keep getting this error? I'm really new and I'd appreciate someone's help. This is my code:
import turtle as t
import math as m
import random as r
raindrops = int(input("Enter the number of raindrops: "))
def drawSquare():
t.up()
t.goto(-300,-300)
t.down()
t.fd(600)
t.lt(90)
t.fd(600)
t.lt(90)
t.fd(600)
t.lt(90)
t.fd(600)
t.lt(90)
def location():
x = (r.randint(-300, 300))
y = (r.randint(-300, 300))
t.up()
t.goto(x, y)
return x, y
def drawRaindrops(x, y):
t.fillcolor(r.random(), r.random(), r.random())
circles = (r.randint(3, 8))
radius = (r.randint(1, 20))
newradius = radius
area = 0
t.up()
t.rt(90)
t.fd(newradius)
t.lt(90)
t.down()
t.begin_fill()
t.circle(newradius)
t.end_fill()
t.up()
t.lt(90)
t.fd(newradius)
t.rt(90)
while circles > 0:
if x + newradius < 300 and x - newradius > -300 and y + newradius < 300 and y - newradius > -300:
t.up()
t.rt(90)
t.fd(newradius)
t.lt(90)
t.down()
t.circle(newradius)
t.up()
t.lt(90)
t.fd(newradius)
t.rt(90)
newradius += radius
circles -= 1
area += m.pi * radius * radius
else:
circles -= 1
return area
def promptRaindrops(raindrops):
if raindrops < 1 or raindrops > 100:
print ("Raindrops must be between 1 and 100 inclusive.")
if raindrops >= 1 and raindrops <= 100:
x, y = location()
area = drawRaindrops(x, y)
area += promptRaindrops(raindrops - 1)
return x, y, area
def main():
t.speed(0)
drawSquare()
x, y, area = promptRaindrops(raindrops)
print('The area is:', area, 'square units.')
main()
t.done()
I'm assuming something is wrong with the "+=" but I have no idea what. I'm fairly certain that the area is correctly being returned. Help please. :)

Two things I noticed:
1. promptRaindrops returns a tuple
I am sure you didn't intend this, but when you say area += promptRaindrops(raindrops - 1), you are adding a tuple to area, which is an integer. To fix this, you should say area += promptRaindrops(raindrops - 1)[2] to get the area returned. However, your error is generated by
2. Your base case doesn't return a value
In promptRaindrops, you return a recursive call of the function whenever 1 <= raindrops <= 100. But, when it is outside that range, it returns nothing, only prints a message. Your function will always be outside of that range, because if you keep decreasing the value passed in to promptRaindrops, it will eventually go below 1. When it does, you return None (since you didn't return anything). That None bubbles up through every single recursion call made to that point, and you will inevitably be adding None to area. Add a return statement returning a tuple, and your error should vanish.

In promptRaindrops() you perform a += operation with a recursive call to promptRaindrops() which will not return anything (NoneType) if raindrops is outside the given range.
Depending on how the program should behave, either something should be returned there or it should not be called with values outside the given range.

Related

Why does my function with complex numbers return as a NoneType?

I've been trying to code something to draw the mandelbrot-set, but my function doesnt seem to work.
the 'point' in my code is a complex number that is defined somewhere else in the code.
def mandelbrot(point, gen):
z = point
if gen > 0:
mandelbrot(z**2 + c, gen-1)
else:
return (z.real**2 + z.imag**2)**(1/2)
I got a grid of points that get colored in based on the result of this function. It would start with a complex number that i define in a loop later, and the 'gen' is just an integer that determines how often the function is used so i can do quicker tests in case it works. I thought it should have returned the length of the vector, but it gave an error that it was a NoneType.
For context, here is the full code:
import turtle
import cmath
Pen = turtle.Turtle()
Pen.speed(0)
Pen.penup()
size = 800
resolution = 16
accuracy = 3
c = complex(0,0)
z = complex(0,0)
Pen.goto(-size/2, -size/2)
def mandelbrot(point, gen):
z = point
if gen > 0:
mandelbrot(z**2 + c, gen-1)
else:
return (z.real**2 + z.imag**2)**(1/2)
def pixel(point):
if mandelbrot(point, accuracy) > 2:
Pen.fillcolor(1,1,1)
else:
Pen.fillcolor(0,0,0)
Pen.begin_fill()
for i in range (0, 4):
Pen.forward(size/resolution)
Pen.left(90)
Pen.end_fill()
for i in range(0, resolution):
Pen.goto(-size/2, -size/2 + i*size/resolution)
for j in range(0, resolution):
c = complex((-size/2 + j*size/resolution)/size*4,
(-size/2 + i*size/resolution)/size*4)
pixel(c)
Pen.forward(size/resolution)

Move Robot in a 10 X 10 grid

I am working on this code challenge:
Given a 2D bot/robot which can only move in four directions, move forward which is UP(U), move backward which is DOWN(D), LEFT(L), RIGHT(R) in a 10x10 grid. The robot can't go beyond the 10x10 area.
Given a string consisting of instructions to move.
Output the coordinates of a robot after executing the instructions. Initial position of robot is at origin(0, 0).
Example:
Input : move = “UDDLRL”
Output : (-1, -1)
Explanation:
Move U : (0, 0)–(0, 1)
Move D : (0, 1)–(0, 0)
Move D : (0, 0)–(0, -1)
Move L : (0, -1)–(-1, -1)
Move R : (-1, -1)–(0, -1)
Move L : (0, -1)–(-1, -1)
Therefore final position after the complete
movement is: (-1, -1)
I got the code working without using the 10x10 grid information. How could I incorporate the 10x10 grid information into my solution in an OOP fashion? My solution doesn't follow the OOP principles.
# function to find final position of
# robot after the complete movement
def finalPosition(move):
l = len(move)
countUp, countDown = 0, 0
countLeft, countRight = 0, 0
# traverse the instruction string 'move'
for i in range(l):
# for each movement increment its respective counter
if (move[i] == 'U'):
countUp += 1
elif(move[i] == 'D'):
countDown += 1
elif(move[i] == 'L'):
countLeft += 1
elif(move[i] == 'R'):
countRight += 1
# required final position of robot
print("Final Position: (", (countRight - countLeft),
", ", (countUp - countDown), ")")
# Driver code
if __name__ == '__main__':
move = "UDDLLRUUUDUURUDDUULLDRRRR"
finalPosition(move)
This fixes it:
class Robot:
class Mover:
def __init__(self, x, y):
self.x, self.y = x, y
def new_pos(self, x, y):
new_x = x + self.x
new_y = y + self.y
if (new_x > 9 or new_y > 9):
raise ValueError("Box dimensions are greater than 10 X 10")
return new_x, new_y
WALKS = dict(U=Mover(0, 1), D=Mover(0, -1),
L=Mover(-1, 0), R=Mover(1, 0))
def move(self, moves):
x = y = 0
for id in moves:
x, y = self.WALKS[id].new_pos(x, y)
return (x,y)
if __name__ == '__main__':
moves2 = "UDDLLRUUUDUURUDDUULLDRRRR"
robot = Robot()
print(robot.move(moves2))
Output :
(2,3)
The way you use your counters makes it less trivial to detect that you would hit the border of the 10x10 grid. Without changing too much, you could replace the countUp and countDown variables by one countVertical variable, and add -1 to it when going up and 1 when going down. Then ignore a move if it would make that counter negative or greater than 9. And obviously you would do the same for horizontal movements.
[Edit: After the edit to your question, it turns out that you want the Y-coordinate to be opposite to what I assumed above. So I have changed the sign of the Y-coordinate updates (+1, -1).]
That's really it.
Now to make this more OOP, you could define a Robot class, which would maintain its x and y coordinate. Anyhow it would be good to remove the print call out of your function, so the function only deals with the movements, not with the reporting (separation of concern).
Here is how it could work:
class Robot:
def __init__(self, x=0, y=0):
self.position(x, y)
def position(self, x, y):
self.x = min(9, max(0, x))
self.y = min(9, max(0, y))
def move(self, moves):
for move in moves:
if move == 'U':
self.position(self.x, self.y + 1)
elif move == 'D':
self.position(self.x, self.y - 1)
elif move == 'L':
self.position(self.x - 1, self.y)
elif move == 'R':
self.position(self.x + 1, self.y)
else:
raise ValueError(f"Invalid direction '{move}'")
if __name__ == '__main__':
moves = "UDDLLRUUUDUURUDDUULLDRRRR"
robot = Robot(0, 0)
robot.move(moves)
print(f"Final position: {robot.x}, {robot.y}")

simpson integration on python

I am trying to integrate numerically using simpson integration rule for f(x) = 2x from 0 to 1, but keep getting a large error. The desired output is 1 but, the output from python is 1.334. Can someone help me find a solution to this problem?
thank you.
import numpy as np
def f(x):
return 2*x
def simpson(f,a,b,n):
x = np.linspace(a,b,n)
dx = (b-a)/n
for i in np.arange(1,n):
if i % 2 != 0:
y = 4*f(x)
elif i % 2 == 0:
y = 2*f(x)
return (f(a)+sum(y)+f(x)[-1])*dx/3
a = 0
b = 1
n = 1000
ans = simpson(f,a,b,n)
print(ans)
There is everything wrong. x is an array, everytime you call f(x), you are evaluating the function over the whole array. As n is even and n-1 odd, the y in the last loop is 4*f(x) and from its sum something is computed
Then n is the number of segments. The number of points is n+1. A correct implementation is
def simpson(f,a,b,n):
x = np.linspace(a,b,n+1)
y = f(x)
dx = x[1]-x[0]
return (y[0]+4*sum(y[1::2])+2*sum(y[2:-1:2])+y[-1])*dx/3
simpson(lambda x:2*x, 0, 1, 1000)
which then correctly returns 1.000. You might want to add a test if n is even, and increase it by one if that is not the case.
If you really want to keep the loop, you need to actually accumulate the sum inside the loop.
def simpson(f,a,b,n):
dx = (b-a)/n;
res = 0;
for i in range(1,n): res += f(a+i*dx)*(2 if i%2==0 else 4);
return (f(a)+f(b) + res)*dx/3;
simpson(lambda x:2*x, 0, 1, 1000)
But loops are generally slower than vectorized operations, so if you use numpy, use vectorized operations. Or just use directly scipy.integrate.simps.

Creating a symmetrical grid of random size squares in Python3/Tkinter

I have a question revolving around what would be a viable approach to placing out random-sized squares on a symmetrical, non-visible grid on a tkinter-canvas. I'm going to explain it quite thoroughly as it's a somewhat proprietary problem.
This far I've tried to solve it mostly mathematically. But I've found it to be quite a complex problem, and it seems reasonable that there would be a better approach to take it on than what I've tried.
In its most basic form the code looks like this:
while x_len > canvas_width:
xpos = x_len + margin
squares[i].place(x=xpos, y=ypos)
x_len += square_size + space
i += 1
x_len is the total width of all the squares on a given row, and resets when exiting the while-loop (eg. when x_len > window width), among with xpos (the position on X), as well as altering Y-axis to create a new row.
When placing same-size squares it looks like this:
So far so good.
However when the squares are of random-size it looks like this (at best):
The core problem, beyond that the layout can be quite unpredictable, is that the squares aren't centered to the "invisible grid" - because there is none.
So to solve this I've tried an approach where I use a fixed distance and a relative distance based on every given square. This yields satisficing results for the Y-axis on the first row, but not on the X-axis, nor the following rows on Y.
See example (where first row is centered on Y, but following rows and X is not):
So with this method I'm using a per-square alteration in both Y- and X-axis, based on variables that I fetch from a list that contain widths for all of the generated squares.
In it's entirety it looks like this (though it's work in progress so it's not very well optimized):
square_widths = [60, 75, 75, 45...]
space = square_size*0.5
margin = (square_size+space)/2
xmax = frame_width - margin - square_size
xmin = -1 + margin
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
m_ypos = 0
extra_x = 0
while len(squares) <= 100:
n=-1
# row_ypos alters y for every new row
row_ypos += 200-square_widths[n]/2
# this if-statement is not relevant to the question
if x < 0:
n=0
xpos = x
extra_x = x
x_len = z
while x_len < xmax:
ypos = row_ypos
extra_x += 100
ypos = row_ypos + (200-square_widths[n])/2
xpos = extra_x + (200-square_widths[n])/2
squares[i].place(x=xpos, y=ypos)
x_len = extra_x + 200
i += 1
n += 1
What's most relevant here is row_ypos, that alters Y for each row, as well as ypos, that alters Y for each square (I don't have a working calculation for X yet). What I would want to achieve is a similar result that I get for Y-axis on the first row; on all rows and columns (eg. both in X and Y). To create a symmetrical grid with squares of different sizes.
So my questions are:
Is this really best practice to solve this?
If so - Do you have any tips on decent calculations that would do the trick?
If not - How would you approach this?
A sidenote is that it has to be done "manually" and I can not use built-in functions of tkinter to solve it.
Why don't you just use the grid geometry manager?
COLUMNS = 5
ROWS = 5
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
This will line everything up, but the randomness may make the rows and columns different sizes. You can adjust that with the row- and columnconfigure functions:
import tkinter as tk
from random import randint
COLUMNS = 10
ROWS = 5
class GUI(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
labels = []
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
labels.append(l)
self.update() # draw everything
max_width = max(w.winfo_width() for w in labels)
max_height = max(w.winfo_height() for w in labels)
for column in range(self.grid_size()[0]):
self.columnconfigure(col, minsize=max_width) # set all columns to the max width
for row in range(self.grid_size()[1]):
self.rowconfigure(row, minsize=max_height) # set all rows to the max height
def main():
root = tk.Tk()
win = GUI(root)
win.pack()
root.mainloop()
if __name__ == "__main__":
main()
I found the culprit that made the results not turn out the way expected, and it wasn't due to the calculations. Rather it turned out that the list I created didn't put the squares in correct order (which I should know since before).
And so I fetched the width from the raw data itself, which makes a lot more sense than creating a list.
The function now looks something like this (again, it's still under refinement, but I just wanted to post this, so that people don't waste their time in coming up with solutions to an already solved problem :)):
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
while len(squares) <= 100:
n=0
if y > 1:
ypos -= max1 + 10
if y < 0:
if ypos < 0:
ypos=10
else:
ypos += max1 + 10 #+ (max1-min1)/2
if x < 0:
n=0
xc=0
xpos = x
x_len = z
while x_len < xmax:
yc = ypos + (max1-squares[i].winfo_width())/2
if xpos <= 0:
xpos = 10
else:
xpos += max1 + 10
xc = xpos + (max1-squares[i].winfo_width())/2
squares[i].place(x=xc, y=yc)
x_len += max1 + 10
print (x_len)
i += 1
n += 1

matplotlib.pyplot imshow() now shows a solid blue colour, no longer the colour rendering?

Further to my previous, helpfully addressed, question here
How to centre the origin in the centre of an imshow() plot
after some fiddling about with the some parameters, spyder now consistently shows a blank blue output. It is baffling!!
I've forced the dtype to be uint8 (I read this on a related question that this may be the cause) but to no avail.
EDIT: (Thanks to the rapid responses) here is the relevant code (from a larger program for modelling diffraction through a square aperture):
import numpy as np
import matplotlib.pyplot as plt
def expo(x,y,z,xp,yp,k):
"""
Function of the integrand in Eq. 5
"""
return np.exp((1j*k/(2*z))*(((x-xp)**2) + ((y-yp)**2)))
def square_2dsimpson_eval(a,b,n):
simp_eval = np.zeros((n+1,n+1))
deltap = (b-a)/n
xp = 0
yp = 0
w = np.zeros((n+1,n+1))
x=0
y=0
for h in range(n+1): #the first two for loops produce the 2d Simpson matrix of coeffecients
if h == 0 or h==n:
w[0,h] = 1
elif h%2 != 0:
w[0,h]=4
elif h%2 == 0:
w[0,h]=2
for g in range(n+1):
if g ==0 or g==n:
w[g,0]=1
elif g%2 != 0:
w[g,0]=4
elif g%2 == 0:
w[g,0]=2
for h in range(1,n+1):
for g in range(1,n+1):
w[h,g]=w[0,h]*w[g,0]
for h in range(0,n+1):
xp = h*deltap
for g in range(0,n+1):
yp = g*deltap
simp_eval[h,g] = expo(x,y,z,xp,yp,k) #the integrand
return (k/(2*np.pi*z))*((deltap**2)/9)*(np.sum(simp_eval*w))
n = 3.3
#this loop checks that user's N is even as required for Simpson's rule
while n % 2 != 0:
n = int(input("Type an even N value: "))
if n % 2 == 0:
break
else:
print("n must be even you noob!")
lam=float(input("Type light wavelength in mm: "))
k=(2*np.pi)/lam
z=float(input("Type screen distance, z in mm: "))
rho=float(input("Type rho in mm: "))
delta = 2/n
intensity = np.zeros((n+1,n+1),dtype='uint8')
for i in range(n+1):
x=-1+(i*delta)
for j in range(n+1):
y =-1+(j*delta)
intensity[i,j] = (abs(square_2dsimpson_eval(-rho/2,rho/2,n)))**2
print(intensity.dtype)
plt.imshow(intensity)
plt.show()
The plot has gone from this:
to this:
Thanks in advance.
Without Even knowing the code that produces either image, I can only say that the second image seems to be a cutout of the first image in a region where there is no data or data is close to or equal the minimum value.

Resources