Can't get variables from a function inside a class (python) - python

i'm stuck with this problem, i can't read the variables bpm and spo2 from the function run_sensor (i need these two variables in another script). If someone can help me, thanks.
class HeartRateMonitor(object):
LOOP_TIME = 0.10
def __init__(self, print_raw=False, print_result=False):
self.bpm = 0
if print_raw is True:
print('IR, Red')
self.print_raw = print_raw
self.print_result = print_result
def run_sensor(self):
sensor = MAX30102()
ir_data = []
red_data = []
bpms = []
# run until told to stop
while not self._thread.stopped:
# check if any data is available
num_bytes = sensor.get_data_present()
if num_bytes > 0:
# grab all the data and stash it into arrays
if len(ir_data) == 100:
bpm, valid_bpm, spo2, valid_spo2 = hrcalc.calc_hr_and_spo2(ir_data, red_data) # <-------------- here
if valid_bpm:
bpms.append(bpm)
while len(bpms) > 4:
bpms.pop(0)
self.bpm = np.mean(bpms)
if (np.mean(ir_data) < 50000 and np.mean(red_data) < 50000):
self.bpm = 0
if self.print_result:
print("Finger not detected")
if self.print_result:
print("BPM: {0}, SpO2: {1}".format(round(self.bpm), round(spo2)))

You need to use Global keyword for this issue. Write this -
global bpm
global spo2
under the function you need to use the variables in

You can't access class variables from another function. However, if you were to create an instance of the HeartRateMonitor class, then you can import it and get its bpm and spo2.
instance = HeartRateMonitor()
Then, in your other script:
from heartratemonitor.py import instance
instance.run_sensor() # I assume this is what you want to do
print(instance.bpm)
print(instance.spo2)

Related

How do I make this python loop increment an int up and down repeating

I'm trying to create a fake thermostat that has a set temperature and increments up and down between the minimum and maximum temperatures over time. The minimum is the set temperature - 2 and the max temperature is the set temperature + 1.
Here's the thermostat class:
class Thermostat:
def __init__(self, set_temp = 70, actual_temp = 70):
self.set_temp = set_temp
self.actual_temp = actual_temp
self.max = set_temp + 1
self.min = set_temp - 2
def set_the_temp(self, x):
self.set_temp = x
def increment_temp(self, x):
self.actual_temp += x
#create a thermostat
thermostat = Thermostat()
Here's what I have for the loop that increments, but I can't figure out the logic for the conditional statements, it always just loops between 70 and 71.
def increment_temp():
last_temp = 69
def increment(x):
last_temp = thermostat.actual_temp
thermostat.increment_temp(x)
print("set temp: ", thermostat.set_temp)
print("actual temp: ", thermostat.actual_temp)
print("last temp:", last_temp)
while True:
if thermostat.actual_temp > last_temp:
if thermostat.actual_temp < thermostat.max:
increment(1)
else:
increment(-1)
elif thermostat.actual_temp < last_temp:
if thermostat.actual_temp > thermostat.min:
increment(-1)
else:
increment(1)
time.sleep(randint(2,10))
for the life of me, I can't figure out why actual_temp goes back up to 71 from 70 even when actual_temp is less than set_temp when it's 71.
In here, your last temp you are changing isnt the same as the last temp outside of the function, and that is causing some issues with the code. You can see if you debug that the value of last_temp isn't changing, and is stuck at 69 with your code.
What I would recommend is simply adding an extra parameter to the thermostat, call it last temp, and add the incrementing function as a function of the thermostat itself, something like this:
def increment_temp(self, x):
self.last_temp = self.actual_temp
self.actual_temp += x
print("....")
It also reduces your code complexity and makes it easier to troubleshoot any further issues.
It's due to the scope of the variable last_temp.
You can use the nonlocal statement to enable rebinding of a nonlocal.
def increment_temp():
last_temp = 69
def increment(x):
nonlocal last_temp
last_temp = thermostat.actual_temp
As the answers have pointed out, the problem was with the scoping of one of your variables. The debugging of this was hard because of the various scopes and conflicting responsibilities within your code.
increment_temp, increment_temp and increment are all method/function names, for example. This makes it hard for the reader to understand which function / class has the responsibility to increment the temperature.
A refactor of this may look something like this:
from dataclasses import dataclass
import time
#dataclass
class Environment:
temp: int = 70
class Thermostat:
'''Adjusts temperature to a set point.
has a max and min temp range around setpoint'''
def __init__(self, setpoint: int):
self.setpoint = setpoint
self.max = setpoint + 1
self.min = setpoint - 2
self.rate_of_change = 1
def connect_to_env(self, env: Environment):
self.env = env
def adjust_temp(self, change):
if self.min < self.env.temp + (self.rate_of_change * change) < self.max:
self.env.temp += (self.rate_of_change * change)
else:
self.rate_of_change *= -1
self.env.temp += (self.rate_of_change * change)
def print_reading(self):
print(f'Setpoint: {self.setpoint}')
print(f'Environment Temp: {self.env.temp}')
print(f'Rate of Change: {self.rate_of_change}')
if __name__ == '__main__':
thermostat = Thermostat(setpoint=69)
thermostat.connect_to_env(env=Environment(temp=70))
while True:
thermostat.adjust_temp(change=1)
thermostat.print_reading()
time.sleep(1)
Here the environment holds the data associated with it, the temperature. The thermostat then holds the data relevant to it and the methods which make sense in the context of a thermostat object.
I can't figure out the logic for the conditional statements, it always just loops between 70 and 71.
Conditional control flow with nested if and elif statements often indicates that the logic can be simplified. Here there is a rate_of_change variable which indicates which way the temperature should be changed.

NameError: name 'x' is not defined Python

I'm trying to get around this error but can't find a solution. Actually the code worked perfectly some time but suddendly started to drop the error:
"NameError: name 'number_exit' is not defined"
And also:
"NameError: name 'price_buy' is not defined"
The code is generating a list of random numbers
import numpy as np
# This function generates a list of numbers under certain rules
def num_var_list():
global number_list, number_exit, number_target
number_list = []
number_target = number_exit
num_max_robot = (number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1,int(num_max_robot))
if num_robot > number_target:
number_list.append(number_target)
else:
number_list.append(num_robot)
number_target = number_target - number_target
return number_list
# This function generates a random number between a certain range
def fun_price_buy():
global price_buy
price_buy = np.random.randint(50000,300000)
return price_buy
# This function generates a random number between a certain range
def fun_mx_buy():
global number_exit
number_exit = np.random.randint(50, 150)
return number_exit
lista_number_list = []
lista_price_buy = []
lista_mx_buy = []
# This loop append each function 50 times to a new list
while len(lista_price_buy) <= 50:
lista_number_list.append(num_var_list())
lista_price_buy.append(fun_price_buy())
lista_mx_buy.append(fun_mx_buy())
Actually, when Python doesn't drop the error, the code makes exactly what I want it to do. So I'm not sure how to adjust it to let it work without NameError warnings.
Any help will be highly appreciated. Thank you!
When doing global price_buy that means you use the globally defined price_buy to be defined localy in your method but
neither price_buy nor number_exit are defined globally (outside a method)
you don't need global variable
They only to be local, and just better : inlined
def fun_price_buy():
price_buy = np.random.randint(50000,300000)
return price_buy
# inline, no temporaty variable is needed
def fun_price_buy():
return np.random.randint(50000,300000)
Finally, and if you want to get the value from the method in a variable for doing something with it:
import numpy as np
# This function generates a list of numbers under certain rules
def num_var_list(number_exit):
number_list = []
number_target = number_exit
num_max_robot = (number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1,int(num_max_robot))
if num_robot > number_target:
number_list.append(number_target)
else:
number_list.append(num_robot)
number_target = number_target - number_target
return number_list
def fun_price_buy():
return np.random.randint(50000,300000)
def fun_mx_buy():
return np.random.randint(50, 150)
lista_number_list = []
lista_price_buy = []
lista_mx_buy = []
# This loop append each function 50 times to a new list
while len(lista_price_buy) <= 50:
number_exit = fun_mx_buy()
price_buy = fun_price_buy()
vr_list = num_var_list(number_exit)
lista_number_list.append(vr_list)
lista_price_buy.append(price_buy )
lista_mx_buy.append(number_exit )
why do you have so much global ?
def num_var_list():
number_exit=fun_mx_buy()
number_list = []
number_target = number_exit
num_max_robot = (number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1, int(num_max_robot))
if num_robot > number_target:
number_list.append(number_target)
else:
number_list.append(num_robot)
number_target = number_target - number_target
return number_list
is it works for you?
It looks like you are defining variables via globals inside a function. You'll want to convert your price_buy and number_exit to local variables.
or:
# This function generates a random number between a certain range
def fun_price_buy():
return np.random.randint(50000,300000)
# This function generates a random number between a certain range
def fun_mx_buy():
return np.random.randint(50, 150)
If you still want to use global variables (as other answers pointed, not a recommended approach), you will have to give them a value before using them:
number_exit = 0
# This function generates a list of numbers under certain rules
def num_var_list():
global number_list, number_exit, number_target
number_list = []
You can instantiate number_exit in the scope of the function as well, but you need to do so before actually using it.
# This function generates a list of numbers under certain rules
def num_var_list():
global number_list, number_exit, number_target
number_list = []
number_exit = 0
You haven't defined those global variables. Typing global whatever doesn't automatically define a variable called whatever. It just tells the interpreter that there's an existing variable called whatever in global scope.
For example, the following code produces no errors:
blah = 'yada'
def foo(bar):
global blah
print(blah, bar)
foo('test') # output: yada test
Whereas in this example, if the global variable isn't defined beforehand (I commented it out), it gets the same error as you:
#blah = 'yada'
def foo(bar):
global blah
print(blah, bar)
foo('test') # output: NameError: name 'blah' is not defined
So, to stop getting the error, you have to give your global variables some value beforehand, like None. Although you could be avoiding globals altogether if you used a class to hold those values you need, like this:
import numpy as np
class MyClass(object):
def __init__(self):
#you'll have to set the numerical values to something that causes num_var_list to not loop infinitely
self.number_list = None
self.number_exit = 0
self.number_target = 0
self.price_buy = 0
# This function generates a list of numbers under certain rules
def num_var_list(self):
self.number_list = []
self.number_target = self.number_exit
num_max_robot = (self.number_target * 20) / 100
while num_max_robot > 0:
num_robot = np.random.randint(1,int(num_max_robot))
if num_robot > self.number_target:
self.number_list.append(self.number_target)
else:
self.number_list.append(num_robot)
self.number_target = self.number_target - self.number_target
return self.number_list
# This function generates a random number between a certain range
def fun_price_buy(self):
self.price_buy = np.random.randint(50000,300000)
return self.price_buy
# This function generates a random number between a certain range
def fun_mx_buy(self):
self.number_exit = np.random.randint(50, 150)
return self.number_exit
def main():
lista_number_list = []
lista_price_buy = []
lista_mx_buy = []
my_class_instance = MyClass()
# This loop append each function 50 times to a new list
while len(lista_price_buy) <= 50:
lista_number_list.append(my_class_instance.num_var_list())
lista_price_buy.append(my_class_instance.fun_price_buy())
lista_mx_buy.append(my_class_instance.fun_mx_buy())
if __name__ == '__main__':
main()

How can I get the variables in this code to update?

I'm wanting to be able to print and update values retrieved from a small GPS in real time. For whatever reason, the code will not update the GPS value, and only prints the same value until I restart the code. My guess is the variable is not able to update, but I wouldn't know how to fix that.
while ser.inWaiting() > 0:
decoded = (ser.read(1).decode)
echo += decoded()
class GPS:
def __init__(self):
#Write GPSinput
out = ''
ser.write(com.GPSstatus.encode())
time.sleep(1)
#Read output
while ser.inWaiting() > 0:
decoded = (ser.read(1).decode)
out += decoded()
strlen = len(str(out))
substr = out[0:strlen-9]
#GPS? information list
variables = substr.splitlines()
#Storing each output in a variable
self.PULSE_SAWTOOTH = [int(s) for s in variables[1] if s.isdigit()]
self.TRACKED_SATELLITES = [int(s) for s in variables[2] if s.isdigit()]
self.VISIBLE_SATELLITES = [int(s) for s in variables[3] if s.isdigit()]
self.LONGITUDE = variables[5]
self.longlen = len(self.LONGITUDE)
self.LONGDEG = self.LONGITUDE[0:self.longlen-7]
self.LONGMIN = self.LONGITUDE[self.longlen-7:]
self.LATITUDE = variables[6]
self.latlen = len(self.LATITUDE)
self.LATDEG = self.LATITUDE[0:self.latlen-7]
self.LATMIN = self.LATITUDE[self.latlen-7:]
self.HEIGHT = variables[7]
self.KNOTS = variables[8]
self.DEGREES = [9]
self.GPS_STATUS = variables[10]
self.TIMING_MODE = variables[17]
self.FIRMWARE_VERSION = variables[20]
if __name__ == "__main__":
#Call the functions
gps = GPS()
for i in range(100):
print(gps.LATITUDE)
#Enterable Parameters
# PULSE_SAWTOOTH
# TRACKED_SATELLITES
# VISIBLE_SATELLITES
# LONGITUDE
# longlen
# LONGDEG
# LONGMIN
# LATITUDE
# latlen
# LATDEG
# LATMIN
# HEIGHT
# KNOTS
# DEGREES
# GPS_STATUS
# TIMING_MODE
# FIRMWARE_VERSION
I'm guessing this is a pretty simple solution, but I'm a beginner and I don't fully understand whats happening enough to fix it. If anyone could shed some knowledge and help with my problem it would be greatly appreciated.
Aside from the while loop indent error and possible decode error. You're getting the same output for print(gps.LATITUDE) every time because you never update it, either by the gps object, or a call to a member function of class GPS. Your initial gps object always has LATITUDE equal to the value variables[6] and if that variable isn't changing, neither will your LATITUDE variable. You have options, you can either update LATITUDE directly:
class GPS:
def __init__(self , latitudeValue = 0):
self.LATITUDE = latitudeValue
if __name__ == "__main__":
gps = GPS()
print(gps.LATITUDE) # Prints 0, default value of latitudeValue
gps.LATITUDE = 5
print(gps.LATITUDE) # Now prints 5, changed value of LATITUDE in gps object
Or you can make it part of a function call:
class GPS:
def __init__(self , latitudeValue = 0):
self.LATITUDE = latitudeValue
def updateLatitude(self , newValue):
self.LATITUDE = newValue
if __name__ == "__main__":
gps = GPS()
print(gps.LATITUDE) # Prints 0
gps.updateLatitude(10) # Updates LATITUDE VALUE
print(gps.LATITUDE) # Prints 10
You can use either method with whatever the value of variables[6] is. Likewise, you can use this with all member variables of your class.
For real-time updating you need to use a event handling function, this scenario possibly calls for a listener. Without knowing more about the device passing data through the bus. If the manufacturer of your device doesn't supply an API to work with, then while you can use a loop to constantly reinitialize an object, another approach is module reloading.

access a class instance/variable from one running python file in another?

I have a problem that involves collecting data continuously from multiple sources.
My setup as it is currently, writes each data entry from each source to a MySQL db, and then, with another python program, does Select's that bring all the data together. I need to make INSERT's at roughly 1000/second and as it is my SELECT's can take 15-20 seconds each.
The whole process takes so long the data is obsolete before I get to do anything useful with it.
I have created a toy example to try and demonstrate what I am looking for.
program 1 'generateClasses':
import time
import random
from datetime import datetime
class Race:
def __init__(self,name):
hist = {}
now = datetime.now()
self.name = name
self.now = now
hist[now] = 0
self.v = 0
self.hist = hist # example variable's.
def update(self,name,v):
now = datetime.now()
hist = self.hist
hist[now] = v
self.v = v
self.now - now
self.hist = hist
class Looper:
def __init__(self,name):
self.a = Race(name)
def loop(self,name):
# simulating the streaming API
while True:
v = self.a.v
v += 1
self.a.update(name,v)
print(a,datetime.now(),v) # can i access this stream using the location displayed with the print(a)?
time.sleep(0.1) # this should be more like time.sleep(0.001)
def pickData(self,name):
v = self.v
self.loop(name)
print('The state at {} {} = '.format(self.now,self.v))
return self.hist
if __name__ == "__main__":
x = 'Some_ID'
a = Looper(x)
a.loop(x)
program 2:
from generateClasses import Looper
from datetime import datetime
import time
start_time = int((datetime.now() - datetime(1970, 1, 1)).total_seconds())
print(start_time)
x = 'Some_orher_ID'
a = Looper(x)
print('this will print')
a.loop(x)
print('this wont ever print')
a.pickdata(x)
# this last section is the functionality i am looking for in this program, but, as it is, it will never run.
x = ‘Some_ID’
while True:
now_time = int((datetime.now() - datetime(1970, 1, 1)).total_seconds())
print(start_time)
if int(now_time-start_time) == 10:
a.pickData(x)
# b.pickData(x)
# c.pickData(x)
# d.pickData(x)
# make further actions.....
What happens currently in my examples is that it creates its own loop using the class structure from the first program.
What I want it to do is call the the pickData() method from program 2 at timely intervals of my choosing on a loop running in another program.
Is my best option picking a db located in memory and getting a faster computer?
Maybe something can be done with the object location shown when you print the instance name?
I have uploaded to github if anybody fancies it..
I would be grateful of any suggestions.
also, recommendations for further reading would be appreciated also.

python function string parameter convert to variable name

I'm rather new to Python and programming in general, so I apologise in advance if my terminology is incorrect.
hue_alert_delay = 0
def delays(name, delay):
global hue_alert_delay
if name == 'hue_alert_delay':
for i in range(0, delay):
hue_alert_delay += 1
time.sleep(1)
hue_alert_delay = 0
delays('hue_alert_delay', 60)
What I'm trying to achieve:
I would like the function to convert the 'name' parameter, which is a string input, into a pre-exiting variable, which will negate the need for multiple IF statements.
The above example includes only one IF statement, but for my project there will be a lot more and I would rather keep the function clean and simple.
This won't work, but it's what I'm trying to aim for:
hue_alert_delay = 0
def delays(name, delay):
global name
for i in range(0, delay):
name += 1
time.sleep(1)
hue_alert_delay = 0
delays('hue_alert_delay', 60)
Any assistance would be appreciated.
Use a dict:
values = {
'hue_alert_delay': 0
}
def delays(name, delay):
values[name] += 1
Whenever you feel like using "variable variables", what you most likely really want is a dict storing key-value associations. Yes, there are other ways to do literally what you want, but that soon leads to insane code.
Use a dictionary like so.
vars = {'hue_alert_delay':0}
def delays(name, delay):
for i in range(0, delay):
vars[name] += 1
time.sleep(1)
vars[name] = 0
You can also use globals()[name] but I won't recommend it.
Use a dictionary:
vars = {'hue_alert_delay':0}
def delays(name, delay):
for i in range(delay):
vars[name] += 1
time.sleep(1)
vars[name] = 0
delays('hue_alert_delay', 60)

Categories