Search code examples
pythonooppython-3.xsl4a

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)?


Solution

  • 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