Unable to access class attribute in another function - python-3.x

import rospy
from sensor_msgs.msg import Imu
class ImuData:
def __init__(self):
#self.data = None
pass
def get_observation(self):
rospy.Subscriber('/imu', Imu, self.imu_callback)
imuData = self.data
print(imuData)
def imu_callback(self, msg):
self.data = msg.orientation
print(self.data)
if __name__ == '__main__':
rospy.init_node('gett_imu', anonymous= True)
idd = ImuData()
idd.get_observation()
In the above code, I would like to access self.data defined in imu_callback from get_observation function. The problem is I get error saying that ImuData has no attribute data.
How do I solve this issue?
Note: I feel that the question has to do with the python classes and not with Ros and rospy.

A couple of things are going on here. One, that was mentioned in the comment, is that you should be initializing your attributes inside __init__. The error your seeing is partially because of Python and the fact that self.data has not actually been initialized yet.
The second issue you have is where you setup the subscriber. This should also be done in __init__ and only once. Sensors will be publishing at a fairly constant rate, thus it takes time to actually receive any data on the topic. Also if you plan to call get_observation more than once you would create a new subscription, which you do not want.
Take the following code as a fixed example:
def __init__(self):
rospy.Subscriber('/imu', Imu, self.imu_callback)
self.data = None
def get_observation(self):
imuData = self.data
print(imuData)

Related

In Python, how to get typing right when using a class decorator?

This is my first attempt to make a Python decorator, and I humbly admit to drawing inspiration from a number of StackOverflow posts while doing so. When I scavenged the __instancecheck__ method, the last two asserts stopped triggering, but now PyCharm's built-in linter isn't happy with the very last assert.
Expected type 'Union[type, Tuple[Union[type, Tuple[Any, ...]], ...]]', got 'Multiton' instead.
What is the root of the typing problem, and how can it be fixed?
Addition, after making the __init__ change recommended by a commenter another warning surfaces.
Local variable 'instance' might be referenced before assignment.
class Multiton(object):
""" When init parameters match, reuse an old initialized object instead of making a new one. """
def __init__(self, cls):
# self.__dict__.update({'instances': list(), 'cls': cls})
self.instances = list()
self.cls = cls
def __call__(self, *args, **kwargs):
# make a key with the parameters
key = (args, kwargs)
# search for a matching old instance
found = False
for instance in self.instances:
if instance['key'] == key:
found = True
break
# if not found then create a new instance
if not found:
instance = {'key': key, 'object': self.cls(*args, **kwargs)}
self.instances.append(instance)
return instance['object']
def __instancecheck__(self, other):
return isinstance(other, self.cls)
#Multiton
class YourClass:
def __init__(self, number):
self.number = number
yc_1 = YourClass(1)
yc_2 = YourClass(2)
yc_two = YourClass(2)
assert yc_1 != yc_2
assert yc_2 == yc_two
assert not isinstance(yc_1, Multiton)
assert isinstance(yc_1, YourClass)
The false warnings are PyCharm bugs which hopefully the fine folks at JetBrains will remedy soon.
https://youtrack.jetbrains.com/issue/PY-38590
https://youtrack.jetbrains.com/issue/PY-49966

Specify class variable in Python to be a numpy array of not yet known size

I have a class like
class MyClass:
def __init__(self):
self.will_be_a_numpy_array = None
def compute():
tmp = receive_data()
self.will_be_a_numpy_array = np.zeros(len(tmp))
# process each item in tmp, save result in corresponding element of self.will_be_a_numpy_array
Here __init__ method is vague regarding the type of self.will_be_a_numpy_array variable. It is unclear to fellow developer or compiler what type of variable should be expected. I cannot initialize variable with self.will_be_a_numpy_array = np.zeros(len(tmp)) because I haven't received data yet. What is the right way to articulate variable type in this case?
You can use the strategy that scikit-learn uses for their estimators, namely, you create the attribute when you receive the data and you use a trailing underscore to warn that this is an attribute that is not created at initialisation:
class MyClass:
def __init__(self):
pass
def process(self, data):
self.data_ = np.array(data)
def is_processed(self):
return hasattr(self, 'data_')

Multiple inheritance problem with super()

I'm having a problem with multiple inheritance that I can't seem to figure out. Here is a very abstracted minimal example that reproduces my error (my code is much more complex than this).
class Thing(object):
def __init__(self, x=None):
self.x = x
class Mixin(object):
def __init__(self):
self.numbers = [1,2,3]
def children(self):
return [super().__init__(x=num) for num in self.numbers]
class CompositeThing(Mixin, Thing):
def __init__(self):
super().__init__()
def test(self):
for child in self.children():
print(child.x)
obj = CompositeThing()
obj.test()
Per this, I expect the children() method to return a list of Things built up from self.numbers. Instead, I get TypeError: super(type, obj): obj must be an instance or subtype of type. Incidentally, the same thing happens if I don't call the constructor and allow children to return super() 3 times (i.e., the uninstantiated superclass). Any ideas why this might be happening?
Thanks in advance!
In line 9 of your code, it looks like you are trying to call __init__ of object. I am assuming you meant to have Mixin inherit from Thing.
class Thing(object):
def __init__(self, x=None):
self.x = x
class Mixin(Thing):
def __init__(self):
self.numbers = [1,2,3]
def children(self):
return [super().__init__(x=num) for num in self.numbers] # Now calls Thing.__init__ instead of object.__init__
class CompositeThing(Mixin, Thing):
def __init__(self):
super().__init__()
def test(self):
for child in self.children():
print(child.x)
obj = CompositeThing()
obj.test()
Actually, I figured it out. There were two problems: (1) super() doesn't work as expected inside comprehensions because comprehensions in Py3 have their own scope - this was causing the TypeError I was experiencing. (2) What I was really trying to do was create a new instance of the parent, rather than calling a method from the parent. I have posted a new question for just the latter problem for clarity.

How can i use multiprocessing in class

I'm making program for study python.
it is gui web crawler.
i success multi work gui and main classes using QThread
but i have a problem.
in main class, get picture addresses using webdriver first, and make a list called data.
after that use pool() and map() to start download pictures using download_image method in Main class.
i searched and tried many things.
imap and lambda etc.
here is my code
(i import multiprocess as mul)
(and my python version is 3.7)
# crawler and downloader class
class Main(QThread, QObject):
def __init__(self, path, brand, model, grade):
QThread.__init__(self)
self.path = path
# this is download method
def download_image(self, var):
a = var.split("/")
filename = a[-1]
download_path = self.path + filename
urllib.request.urlretreieve(var, download_path)
# this is start method when button clicked in Gui
def core(self):
#sample url data list
data = ['url.com', 'url2.com', 'url3.com', ...]
download_p = Pool(mul.cpu_count())
download_p.map(self.download_image, data)
download_p.close()
download_p.join()
print("end")
def run(self):
self.core()
class Gui(QMainWindow, design.Ui_MainWindow):
def __init__(self):
(and gui code here)
if i set download_p.map(self.download_image, data)
i get this error -> [ TypeError: can't pickle Main objects ]
if i set download_p.map(self.download_image, self.data)
(and also set self.data = [urls...])
i get same TypeError
if i set download_p.map(self.download_image, self, data)
i get this error -> [TypeError : 'Main' object is not iterable
I'm not good at English and Python too
but i want to resolve this problem so i decide ask here
really thanks for looking this newbie's question...

How to initialize python watchdog pattern matching event handler

I'm using the Python Watchdog to monitor a directory for new files being created. Several different types of files are created in said directory but I only need to monitor a single file type, hence I use the Watchdog PatternMatchingEventHandler, where I specify the pattern to monitor using the patterns keyword.
To correctly execute the code under the hood (not displayed here) I need to initialize an empty dataframe in my event-handler, and I am having trouble getting this to work. If I remove the __init__ in the code below, everything works just fine btw.
I used the code in this answer as inspiration for my own.
The code I have set up looks as follows:
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import time
import pandas as pd
import numpy as np
from multiprocessing import Pool
class HandlerEQ54(PatternMatchingEventHandler):
def __init__(self):
#Initializing an empty dataframe for storage purposes.
data_54 = pd.DataFrame(columns = ['Barcode','DUT','Step12','Step11','Np1','Np2','TimestampEQ54'])
#Converting to INT for later purposes
data_54[['Barcode','DUT']]=data_54[['Barcode','DUT']].astype(np.int64)
self.data = data_54
def on_created(self,event):
if event.is_directory:
return True
elif event.event_type == 'created':
#Take action here when a file is created.
print('Found new files:')
print(event.src_path)
time.sleep(0.1)
#Creating process pool to return data
pool1 = Pool(processes=4)
#Pass file to parsing function and return parsed result.
result_54 = pool1.starmap(parse_eq54,[(event.src_path,self.data)])
#returns the dataframe rather than the list of dataframes returned by starmap
self.data = result_54[0]
print('Data read: ')
print(self.data)
def monitorEquipment(equipment):
'''Uses the Watchdog package to monitor the data directory for new files.
See the HandlerEQ54 and HandlerEQ51 classes in multiprocessing_handlers for actual monitoring code. Monitors each equipment.'''
print('equipment')
if equipment.upper() == 'EQ54':
event_handler = HandlerEQ54(patterns=["*.log"])
filepath = '/path/to/first/file/source/'
# set up observer
observer = Observer()
observer.schedule(event_handler, path=filepath, recursive=True)
observer.daemon=True
observer.start()
print('Observer started')
# monitor
try:
while True:
time.sleep(5)
except KeyboardInterrupt:
observer.unschedule_all()
observer.stop()
observer.join()
However, when I execute monitorEquipment I receive the following error message:
TypeError: __init__() got an unexpected keyword argument 'patterns'
Evidently I'm doing something wrong when I'm initializing my handler class, but I'm drawing a blank as to what that is (which probably reflects my less-than-optimal understanding of classes). Can someone advice me on how to correctly initialize the empty dataframe in my HandlerEQ54 class, to not get the error I do?
Looks like you are missing the patterns argument from your __init__ method, you'll also need a super() call to the __init__ method of the parent class (PatternMatchingEventHandler), so you can pass the patterns argument upwards.
it should look something like this:
class HandlerEQ54(PatternMatchingEventHandler):
def __init__(self, patterns=None):
super(HandlerEQ54, self).__init__(patterns=patterns)
...
event_handler = HandlerEQ54(patterns=["*.log"])
or, for a more generic case and to support all of PatternMatchingEventHandler's arguments:
class HandlerEQ54(PatternMatchingEventHandler):
def __init__(self, *args, **kwargs):
super(HandlerEQ54, self).__init__(*args, **kwargs)
...
event_handler = HandlerEQ54(patterns=["*.log"])

Resources