James Biederbeck James Biederbeck - 1 month ago 8
Python Question

I want a class' __init__ to add the object to a list, but I want to prevent redundancy and "if self not in objectlist" isn't working

I'm writing a script that checks multiple sources for weather data, then parses them for an some scripts on Scripting Layer for Android. The google API stopped working, so this is a hacked together replacement for the old weather module.

I made a class called "weatherdata", and I want to have all instances of the class add themselves to a list called "weatherobjects", for shenanigans like this:

for source in weatherobjects:
source.check()


Here's the catch: each time the function that fetches the weather is called, it causes the objects to run their
__init__
method (I think this is technically called a constructor method?) without destroying the objects, or clearing the list. This is intentional. The problem comes when the function is called more than once in the lifetime of the module, and and the objects get redundantly added to the list. This seems like a potential source of a memory leak.

Here's the
__init__
method:

class weatherdata():
def __init__(self, url, unit = 'f'):
self.url = url
self.unit = unit
print(self) #debug statement, please ignore
if self not in weatherobjects:
weatherobjects.append(self)
if self.url.count("yahoo"):
self.yahoo = True
else:
self.yahoo = False
self.check()


And the troublesome function:

def fetch_weather(location=98661, hl='', weatherobjects= []):
yahoo = weatherdata(yahoo_url, 'f')
wunderground = weatherdata(wunderground_url, 'f')
data = {}
data['city'] = 'Vancouver'
data['temperature'] = wunderground.temp
data['conditions'] = 'foo'
return data


Here's some shell output for context:

>>> weatherobjects
[<__main__.weatherdata object at 0x01F8BDF0>, <__main__.weatherdata object at 0x02035B70>]
>>> for i in range(3):
... fetch_weather()
...
{'city': 'Vancouver', 'conditions': 'foo', 'temperature': '66.7'}
{'city': 'Vancouver', 'conditions': 'foo', 'temperature': '66.7'}
{'city': 'Vancouver', 'conditions': 'foo', 'temperature': '66.7'}
>>> weatherobjects
[<__main__.weatherdata object at 0x01F8BDF0>, <__main__.weatherdata object at 0x02035B70>, <__main__.weatherdata object at 0x01FA2E10>, <__main__.weatherdata object at 0x01FA2FB0>, <__main__.weatherdata object at 0x02035C30>, <__main__.weatherdata object at 0x02035E10>, <__main__.weatherdata object at 0x02035DF0>, <__main__.weatherdata object at 0x02035D10>]
>>> len(weatherobjects)
8


As you can see, there's a lot of redundancy in the list. Is it possible to do this in the
__init__
method? Or do I need to have a main function do something like
weatherobjects.append(foo)
?

Answer

So two years later, I have found the answer while working on something else, and come back to this. Putting aside other issues with my code, I think I should have defined the list in the class (before instantiation), rather than as a global.

My desired behavior was like this example, taken from the Python Docs:

 class Dog:

    tricks = []             # mistaken use of a class variable

    def __init__(self, name):
        self.name = name

    def add_trick(self, trick):
        self.tricks.append(trick)

    >>> d = Dog('Fido')
    >>> e = Dog('Buddy')
    >>> d.add_trick('roll over')
    >>> e.add_trick('play dead')
    >>> d.tricks                
    ['roll over', 'play dead']

This is inappropriate because dogs don't have a hivemind, but the behavior is not explicitly disapproved of in the docs, so I'm going to roll with it. I WANT my weatherobjects to have a hivemind.

So instead of something like this,

global weatherobjectslist
class weathersource():
    def __init__(self, weatherobjectlist):
        weatherobjectlist.append(self)

My desired behavior is produced by this:

class weathersource():
    weathersourcelist = []

    def __init__(self, name):
        self.weathersourcelist.append(self)
        self.name = name

Now, I can check weathersource.weathersourcelist for a list of all my objects.

To put this more generically, here's an example with the same behavior, and some output:

>>> class person():
    people= []
    def __init__(self, name):
        self.people.append(self)
        self.name = name


>>> a,b,c = person('bob'), person('joe'), person('steve')

>>> for individual in person.people:
    print(individual.name)


bob
joe
steve
Comments