Python class: The data is somehow lost - python

I am trying to define a Python class which analyzes subreddit data via the praw package.
I am fairly experienced with OOP in C++, but have not had much experience with OOP in Python. Here is the code I have so far:
import praw
class SubRedditAnalyzer:
def __init__(self, reddit_session, name='dataisbeautiful'):
self.name = name # subreddit name
self.reddit_session = reddit_session # assign the reddit session
self.subreddit = self.reddit_session.get_subreddit(self.name) # create the subreddit object
self.timeframe = 'day'
self.max_post_count = 10
self.submissions = self.subreddit.get_top_from_hour(limit=10)
def __del__(self):
class_name = self.__class__.__name__
print class_name, "destroyed"
def get_top_submissions(self, max_post_count):
timeframe = self.timeframe
if (timeframe == 'hour'):
self.submissions = self.subreddit.get_top_from_hour(limit= max_post_count)
elif (timeframe == 'day'):
self.submissions = self.subreddit.get_top_from_day(limit= max_post_count)
elif (timeframe == 'week'):
self.submissions = self.subreddit.get_top_from_week(limit= max_post_count)
elif (timeframe == 'month'):
self.submissions = self.subreddit.get_top_from_month(limit= max_post_count)
elif (timeframe == 'year'):
self.submissions = self.subreddit.get_top_from_year(limit= max_post_count)
elif (timeframe == 'all'):
self.submissions = self.subreddit.get_top_from_all(limit= max_post_count)
def combine_titles(self):
titles = ""
for submission in self.submissions:
titles += submission.title
self.titles = titles
def display_titles(self):
counter = 1
ya = self.submissions
for sub in self.submissions:
sc = sub.score
ti = sub.title
print('T%d- [%d] %s \n' %(counter,sc,ti))
counter += 1
def main():
r = praw.Reddit('Request to fetch data by user')
sr = SubRedditAnalyzer(r, 'dataisbeautiful')
sr.get_top_submissions(15) # top 15 from reddit
sr.combine_titles() # combine the top titles
sr.display_titles() # display all the titles
main()
For some unknown (to me) reason, it seems that the data in class 'sr' is lost after calling:
sr.combine_titles()
When I try to call this method, the data in class is empty:
sr.display_titles()
In fact, I do see the message that the class is destroyed:
SubRedditAnalyzer destroyed
What is it that I am doing wrong?
In advance, thanks for your attention.

It seems that self.submissions may be an iterable but not a collection (e.g. a list). The docs call get_top_from_hour() a generator method (although they state also that what is returned is a list...). If it is indeed a generator method, the result can be iterated over only once. All other attempts at iteration will fail silently (the loop in display_titles() executes nothing).
So, the solution would be:
self.submissions = list(self.subreddit.get_top_from_hour(limit=10))
in __init__() to convert an iterable into a permanent collection (list) that can be iterated over multiple times.

According to the PRAW docs, get_content and its associated methods like get_top_from_hour return a generator. A generator can only be iterated once, which you do in combine_titles. After that iteration, the generator is exhausted and cannot be iterated again.
You could presumably convert the submissions to a list when you get them in __init__:
self.submissions = list(self.subreddit.get_top_from_hour(limit=10))

Related

Attritube error when importing local python file

I would like some help in figuring out an issue. The code below is attempting to import a file called game_getter.py to access it's all_games dictionary variable.
from django.db import models
from catolog import game_getter
# Create your models here.
class Game(models.Model):
url_clue = ["//images.igdb.com"]
for game in game_getter.all_games:
title = game_getter.all_games[game][0]
if url_clue in game_getter.all_games[game][1]:
cover_art = game_getter.all_games[game]
else:
pass
if game_getter.all_games[game][2] == None:
pass
else:
storyline = game_getter.all_games[game][2]
if game_getter.all_games[game][3] == None:
pass
else:
storyline = game_getter.all_games[game][3]
genre_pac = game_getter.all_games[game][4]
def __str__(self):
return self.title
class UserSearch():
user_input = str
At the bottom of this next session I used a return on the dictionary object all_games. I've even tried making it a global variable and the computer still won't see it.
# Create a search
def search_query(user_input, exp_time):
# Generate token if needed
generate_token(exp_time)
#establish client_id for wrapper
client_id = "not my client_id"
wrapper = IGDBWrapper(client_id, token)
# Query the API based on a search term.
query = wrapper.api_request(
'games', # Requesting the name, storyline, genre name, and cover art url where the user input is the search tearm
f'fields name, storyline, genres.slug, cover.url; offset 0; where name="{user_input}"*; sort first_release_date; limit: 100;',
# Also sorted by release date with a limit of 100 results
)
# Load the binary data into json
message_json = json.loads(query)
# List of all games returned
global all_games
all_games = dict()
key = 0
# Grab each value by key and separate per game
for game in message_json:
name = game.get('name')
cover_url = game.get('cover')
storyline = game.get('storyline')
summary = game.get('summary')
genre_set = game.get('genres')
# Genre posses none to many tags which needs to be sorted.
genre_list = []
if genre_set:
for type in genre_set:
for i in type:
genre_list.append(type[i])
for i in genre_list:
genre_list = [x for x in genre_list if not isinstance(x, int)]
else:
pass
# Group together by game
if game.get('cover') != None:
result = [name,cover_url.get('url'),storyline,summary,genre_list]
# Add the game to the collection of all games found
all_games[key] = result
key += 1
else:
result = [name,storyline,summary,genre_list]
# Add the game to the collection of all games found
all_games[key] = result
key += 1
return all_games
What am I missing?

How to reduce the complexity of several for loops in python

I have the following function
import requests
children_dict = {}
def get_list_of_children(base_url, username, password, folder="1"):
token = get_token()
url = f"{base_url}/unix/repo/folders/{folder}/list"
json = requests_json(url,token)
for obj in json["list"]:
if obj['name'] == 'MainFolder':
folderId = obj['id']
url_parent = f"{base_url}/unix/repo/folders/{folderId}/list"
json_parent = requests_json(url_parent,token)
for obj_child in json_parent['list']:
if obj_child['folder'] == True:
folder_grand_child_id = obj_child['id']
url_grand_child = f"{base_url}/unix/repo/folders/{folder_grand_child_id}/list"
json_grand_child = requests_json(url_grand_child,token)
for obj_grand_child in json_grand_child["list"]:
if obj_grand_child['name'] == 'SubFolder':
folder_grand_grand_child = obj_grand_child['id']
url_grand_grand_child = f"{base_url}/unix/repo/folders/{folder_grand_grand_child}/list"
json_grand_grand_child = requests_json(url_grand_grand_child,token)
for obj_grand_grand_child in json_grand_grand_child["list"]:
if obj_grand_grand_child['name'] == 'MainTasks':
folder_grand_grand_grand_child = obj_grand_grand_child['id']
url_grand_grand_grand_child = f"{base_url}/unix/repo/folders/{folder_grand_grand_grand_child}/list"
json_grand_grand_grand_child = requests_json(url_grand_grand_grand_child,token)
for obj_grand_grand_grand_child in json_grand_grand_grand_child["list"]:
children_dict[[obj_grand_grand_grand_child['id']] = obj_grand_grand_grand_child['name']
return children_dict
What i am trying to accomplish here is to make repeated api calls to traverse through http folder structure to get the list of files in the last directory
The function works as intended but sonarlint is through the below error
Refactor this function to reduce its Cognitive Complexity from 45 to the 15 allowed. [+9 locations]sonarlint(python:S3776)
is there is a better way to handle this function ?
can anyone refactor this, pointing me in the right direct will do
This isn't a complete solution, but to answer your question "how can I simplify this" more generally, you need to look for repeated patterns in your code and generalize them into a function. Perhaps it's a function you can call recursively, or in a loop. For example, in that deeply-nested statement of yours it's just the same pattern over and over again like:
url = f"{base_url}/unix/repo/folders/{folder}/list"
json = requests_json(url,token)
for obj in json["list"]:
if obj['name'] == '<some folder name>':
folderId = obj['id']
# ...repeat...
So try generalizing this into a loop, maybe, like:
url_format = "{base_url}/unix/repo/folders/{folder_id}/list"
folder_hierarchy = ['MainFolder', 'SubFolder', 'MainTasks']
folder_id = '1' # this was the argument passed to your function
for subfolder in folder_hierarchy:
url = url_format.format(base_url=base_url, folder_id=folder_id)
folder_json = requests_json(url, token)
for obj in folder_json['list']:
if obj['name'] == subfolder:
folder_id = obj['id']
break
# Now the pattern repeats for the next level of the hierarchy but
# starting with the new folder_id
This is just schematic and you may need to generalize further, but it's one idea.
If your goal is to traverse a more complicated hierarchy you might want to look into tree-traversal algorithms.
There's plenty of repeating code. Once you identify the repeating patterns, you can extract them to the classes and functions. In particular, I find it useful to isolate all the web API logic from the rest of the code:
class Client:
def __init__(self, base_url, token):
self.base_url = base_url
self.token = token
def list_folder(self, folder_id):
return request_json(
f'{self.base_url}/unix/repo/folders/{folder_id}/list', self.token
)['list']
def get_subfolders(self, parent_id=1):
return [c for c in self.list_folder(parent_id) if c['folder']]
def get_subfolder(self, parent_id, name):
children = self.list_folder(parent_id)
for child in children:
if child['name'] == name:
return child
return None
def resolve_path(self, path, root_id=1):
parent_id = root_id
for p in path:
current = self.get_subfolder(parent_id, p)
if not current:
return None
parent_id = current['id']
return current
Now you can use the class above to simplify the main code:
client = Client(base_url, token)
for folder in client.get_subfolders():
child = client.resolve_path(folder['id'], ('SubFolder', 'MainTasks'))
if child:
# do the rest of the stuff
The code above is not guaranteed to work as is, just an illustration of the idea.
I can't really test it, but I'd make it like the following in order to easily build many levels of repeating code.
class NestedProcessing:
def __init__(self, base_url):
self.base_url = base_url
self.token = get_token()
self.obj_predicates = []
def next_level_predicate(self, obj_predicate):
self.obj_predicates.append(obj_predicate)
return self
def final_action(self, obj_action):
self.obj_action = obj_action
return self
def process(self, first_folder_id):
self.process_level(0, first_folder_id)
def process_level(self, index, folder_id):
obj_is_good = self.obj_predicates[index]
url = f"{self.base_url}/unix/repo/folders/{folder_id}/list"
json = requests_json(url, self.token)
for obj in json["list"]:
if index == len(self.obj_predicates) - 1: # last level
self.obj_action(obj)
elif obj_is_good(obj):
self.process_level(index + 1, obj['id'])
def get_list_of_children(base_url, username, password, folder="1"):
children_dict = {}
NestedProcessing(base_url)
.next_level_predicate(lambda obj: obj['name'] == 'MainFolder')
.next_level_predicate(lambda obj: obj['folder'] == True)
.next_level_predicate(lambda obj: obj['name'] == 'SubFolder')
.next_level_predicate(lambda obj: obj['name'] == 'MainTasks')
.final_action(lambda obj, storage=children_dict: storage.update({obj['id']: obj['name']})
.process(folder)
return children_dict

Need help in implementing __new__ method for the below scenario

I wanted to know whether the below scenario be available using __new__ special method. If so, I would like to hear from stackoverflow. I have class name Listing which reads records from a file and then convert them in a queries. To be concise, initially the snippet reads all the lines from the file and converts them into list of lists. Again, this list of lists are passed to the loadlist method of Event, which reads each list, unpacks and then set them to class attributes.
For Instance, I have the below three records
1|305|8|1851|Gotterdammerung|2008-01-25 14:30:00
2|306|8|2114|Boris Godunov|2008-10-15 20:00:00
3|302|8|1935|Salome|2008-04-19 14:30:0
Here, Listing.py reads the above content and converts them into queries which is given below
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('1','305','8','1851','Gotterdammerung','2008-01-25 14:30:00')
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('2','306','8','2114','Boris Godunov','2008-10-15 20:00:00')
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('3','302','8','1935','Salome','2008-04-19 14:30:00')
The Whole program of Listing.py
class Event:
def __init__(self,eventid,venueid,catid,dateid,eventname,starttime):
self.eventid = eventid
self.venueid = venueid
self.catid = catid
self.dateid = dateid
self.eventname = eventname
self.starttime = starttime
def __iter__(self):
return (i for i in (self.eventid,self.venueid,self.catid,self.dateid,self.eventname,self.starttime))
def __str__(self):
return str(tuple(self))
def __repr__(self):
return "INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ({!r},{!r},{!r},{!r},{!r},{!r})".format(*self)
#classmethod
def loadlist(cls,records):
return [cls(*record) for record in records]
if __name__ == '__main__':
records = []
with open('tickitdb/allevents_pipe.txt','r') as f:
records = list(map(lambda s:s.rstrip('\n').split('|'),f.readlines()))
events = Event.loadlist(records=records)
with open('events.sql','w+') as f:
print('writing file')
for event in events:
f.write(repr(event)+"\n")
When i ran the program, i came across the below error.
TypeError: __init__() missing 5 required positional arguments:. And i figured out the root cause behind this. When the program reads the file and converts them into list of records, there was record which is empty hasn't, for instance
1.['1','305','8','1851','Gotterdammerung','2008-01-25 14:30:00']
2.['2','306','8','2114','Boris','Godunov','2008-10-15 20:00:00']
3.['3','302','8','1935','Salome','2008-04-19 14:30:0']
4.['']
For the 4th record, there are no values. So, to avoid such errors, i decided to make use of __new__ special method. I can achieve same functionality by putting the if condition and then checking whether the list is empty or not. But then i wondering how to make use of new special method to avoid such scenarios. With little knowledge of python, i have filled the new special method, but then I came across the below error
RecursionError: maximum recursion depth exceeded while calling a Python object
def __new__(cls,*args,**kwargs):
if len(args) != 0:
instance = Event.__new__(cls,*args,**kwargs)
return instance
Can we filter the records using the __new__ special method ?
What you want to do is totally possible. But you will need to initialize the instance by yourself once it returns from new .
I fixed your code as under
Given listing.txt
1|305|8|1851|Gotterdammerung|2008-01-25 14:30:00
2|306|8|2114|Boris Godunov|2008-10-15 20:00:00
3|302|8|1935|Salome|2008-04-19 14:30:0
4|302|8|1935|Salome|2008-04-19 14:30:0
class Event:
def __new__(cls, *args, **kwargs):
breakpoint()
if len(*args) > 1:
instance = object.__new__(cls)
breakpoint()
return instance
else:
return None
def __init__(self,eventid,venueid,catid,dateid,eventname,starttime):
self.eventid = eventid
self.venueid = venueid
self.catid = catid
self.dateid = dateid
self.eventname = eventname
self.starttime = starttime
def __iter__(self):
return (i for i in (self.eventid,self.venueid,self.catid,self.dateid,self.eventname,self.starttime))
def __str__(self):
return str(tuple(self))
def __repr__(self):
return "INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ({!r},{!r},{!r},{!r},{!r},{!r})".format(*self)
#classmethod
def loadlist(cls, records):
breakpoint()
return [cls.__init__(*record) for record in records ]
def initialize(e,eventid,venueid,catid,dateid,eventname,starttime):
e.eventid = eventid
e.venueid = venueid
e.catid = catid
e.dateid = dateid
e.eventname = eventname
e.starttime = starttime
return e
if __name__ == '__main__':
records = []
events = []
with open('listing.txt', 'r') as f:
records = list(map(lambda s: s.rstrip('\n').split('|'), f.readlines()))
for record in records:
breakpoint()
e = Event.__new__(Event, record)
breakpoint()
if e:
events.append(initialize(e, *record))
with open('events.sql','w+') as f:
print('writing file')
for event in events:
f.write(repr(event)+"\n")
OUTPUT
events.sql
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('1','305','8','1851','Gotterdammerung','2008-01-25 14:30:00')
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('2','306','8','2114','Boris Godunov','2008-10-15 20:00:00')
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('3','302','8','1935','Salome','2008-04-19 14:30:0')
INSERT INTO EVENT (EVENTID,VENUEID,CATID,DATEID,EVENTNAME,STARTTIME) VALUES ('4','302','8','1935','Salome','2008-04-19 14:30:0')
So I would solve it like this:
class Event:
def __init__(self, a, b):
self.a = a
self.b = b
def __new__(cls, *args, **kwargs):
if len(args) != 0:
return super(Event, cls).__new__(cls)
else:
return None
def print(self):
print("a " + str(self.a))
print("b " + str(self.b))
c = Event(1, 2)
if c is None:
print("do some stuff here if it is empty")
If you initialize Event with no parameters, it would return None according to len(args) != 0. Otherwise the instance is returned. Hope that helps.

Making a database with custom commands in python

I'm trying to make a simple, local database using Python where I can set values, get values, etc and I keep getting this error:
#Simple Database
#Functions include Set[name][value]
# Get[name]
# Unset[name]
# Numequalto[value]
# End
# Begin, Rollback, Commit
varlist = []
ops = []
class item:
def __init__(self,name,value):
self.name = name
self.value = value
class db:
def __init__(self):
self.varlist = []
self.ops = []
def Set(self,nm,val):
changed = False #Bool for keeping track of update
for item in varlist: #run through current list
if item.name == nm: #If the name is found
item.value = val #update the value
changed = True #found it
break #exit if found
if not changed:
newitem = item() #Create a new one and append it
newitem.name = nm
newitem.value = val
varlist.append(newitem)
def Get(key):
for item in varlist:
if item.name == key:
return item.value
break
def Unset(key):
for item in varlist:
if item.name == key:
item.value = -1
break
def Numequalto(key):
count = 0
for item in varlist:
if item.value == key:
count+=1
return count
def main():
newdb = db()
varlist=[]
comm = "" #prime it
while comm.lower() != "end":
comm = input("Command: ")
if comm.lower() == "begin":
print("----SESSION START---")
while comm.lower() != "end":
comm = input("Command: ")
part = []
for word in comm.split():
part.append(word.lower())
print(part)
if part[0].lower()=="set":
newdb.Set(part[1],part[2])
print(varlist)
elif part[0].lower()=="get":
gotten = Get(part[1])
print(gotten)
elif part[0].lower()=="unset":
Unset(part[1])
elif part[0].lower()=="numequalto":
numequal = Numequalto(part[1])
print(numequal)
print("Finished")
else:
print("--ERROR: Must BEGIN--")
if __name__ == "__main__":
main()
When I run this, and try to create a new item in my list using the command
set a 25
I get this error:
Traceback (most recent call last):
File "/Volumes/CON/LIFE/SimpleDatabase.py", line 81, in <module>
main()
File "/Volumes/CON/LIFE/SimpleDatabase.py", line 65, in main
newdb.Set(part[1],part[2])
File "/Volumes/CON/LIFE/SimpleDatabase.py", line 27, in Set
newitem = item() #Create a new one and append it
UnboundLocalError: local variable 'item' referenced before assignment
Any help would be much appreciated. I'm pretty new to Python
The line
for item in varlist:
shadows the class item with a local variable. So that when you get to your item() it thinks you are trying to call the local variable instead of the class. You can tell that your class item is never being constructed because it would fail as you are passing no parameters to the __init__
Also you should really call your class Item. Once I did that I got the constructor error as expected.
You have a few issues with your code:
You are shadowing the class item with a local variable of the same name.
You are using varlist instead of self.varlist.
Some of your class methods doesn't recieve a self first argument.
Also there is a strong convention in python to name classes with a first capital letter.
Not trying to be implite, just constructive here. I'm concerned that while there are comments questioning the intent to implement your own dictionary, no answer stated this forcefully. I say this only because part of Python (beyond the semantics, language, etc...) is the culture. We speak of things being 'Pythonic' for a reason - part of the value of this language is the culture. There are two aspects here to pay attention to - first, 'Batteries Included' and second, "Don't Reinvent the Wheel". You're reimplimenting the most fundamental composite (oxymoron, I know) data type in Python.
>>> a = {}
>>> a['bob'] = 1
>>> a['frank'] = 2
>>> a
{'frank': 2, 'bob': 1}
>>> del a['frank']
>>> a
{'bob': 1}
>>> del a['bob']
>>> a
{}
>>> a['george'] = 2
>>> b = len([x for x in a.values() if x == 2])
>>> b
1
And there you have it - the pythonic way of handling the functionality you're after.
If you're trying to add functionality or limitations beyond that, you're better off starting from the dict class and extending rather than rolling your own. Since Python is "duck-typed" there's a HUGE benefit to using the existing structure as your basis because it all falls into the same patterns.

'Queue' object has no attribute 'size'

I have seen other examples of this happening on StackOverflow, but I didn't understand any of the answers (I'm still a new programmer,) nor did the other examples I saw look quite like mine, else I wouldn't post this question.
I'm running Python 3.2 on Windows 7.
I have never had this happen to me before and I've done classes this way many times, so I don't really know what is different this time. The only difference is that I didn't make all of the Class file; I was given a template to fill in and a test file to try it on. It worked on the test file, but is not working on my file. I have been calling on the methods in the class in the exact same way as the test file (e.g. Lineup.size())
This is my Class:
class Queue:
# Constructor, which creates a new empty queue:
def __init__(self):
self.__items = []
# Adds a new item to the back of the queue, and returns nothing:
def queue(self, item):
self.__items.insert(0,item)
return
# Removes and returns the front-most item in the queue.
# Returns nothing if the queue is empty.
def dequeue(self):
if len(self.__items) == 0:
return None
else:
return self.__items.pop()
# Returns the front-most item in the queue, and DOES NOT change the queue.
def peek(self):
if len(self.__items) == 0:
return None
else:
return self.__items[(len(self.__items)-1)]
# Returns True if the queue is empty, and False otherwise:
def is_empty(self):
return len(self.__items) == 0
# Returns the number of items in the queue:
def size(self):
return len(self.__items)
# Removes all items from the queue, and sets the size to 0:
def clear(self):
del self.__items[0:len(self.__items)]
return
# Returns a string representation of the queue:
def __str__(self):
return "".join(str(i) for i in self.__items)
This is my program:
from queue import Queue
Lineup = Queue()
while True:
decision = str(input("Add, Serve, or Exit: ")).lower()
if decision == "add":
if Lineup.size() == 3:
print("There cannot be more than three people in line.")
continue
else:
person = str(input("Enter the name of the person to add: "))
Lineup.queue(person)
continue
elif decision == "serve":
if Lineup.is_empty() == True:
print("The lineup is already empty.")
continue
else:
print("%s has been served."%Lineup.peek())
Lineup.dequeue()
continue
elif (decision == "exit") or (decision == "quit"):
break
else:
print("%s is not a valid command.")
continue
And this is my error message when I enter "add" as my decision variable:
line 8, in
builtins.AttributeError: 'Queue' object has no attribute 'size'
So, what is going on here? What is different about this one?
Python 3 already has a queue module (which you might want to take a look at). When you import queue, Python finds that queue.py file before it finds your queue.py.
Rename your queue.py file to my_queue.py, change your import statements to from my_queue import Queue, and your code will work as you intend.
try rename size for other name or implement a counter to the list __items some like
def get_size(self):
cnt = 0
for i in self.__items:
cnt++
return cnt

Categories

Resources