Python instance variable hiding class variables - python

class Arrival(db.Document):
...
articles = db.ListField(db.EmbeddedDocumentField(ArticlesArrival))
articles_ncmd = db.ListField(db.EmbeddedDocumentField(ArticlesArrival))
...
def __repr__(self):
return '<Arrival {}>'.format(self.code)
def __unicode__(self):
return self.code
def modify_article_arrival(self, data, item_id, index):
index_dict = {'ncmd': self.articles_ncmd, 'cmd': self.articles}
item_to_update = next(i for i in index_dict[index] if i._id == ObjectId(item_id))
I have a mongodb collection represented by model Arrival. I have two Embedded documents represented by articles and articles_ncmd which contain similar data but whose context of use is different. The modify_article_arrival method allows me to update an element of one of the two Embedded documents according to the id of the element (item_id) and its index. The index can be either cmd or ncmd and is used to determine which of the two Embedded documents to manipulate.
Currently I am storing the reference to the two Embedded documents in a dictionary mapped with the two indexes (index_dict). I would like to be able to make this dictionnary global to the whole class to use in other methods. The problem is that if I put this in the __init__ method, the fields of my model defined as class variables are no longer accessible.
def __init__(self):
self.index = {'ncmd': self.articles_ncmd, 'cmd': self.articles}
def modify_article_arrival(self, data, item_id, index):
item_to_update = next(i for i in self.index[index] if i._id == ObjectId(item_id))
So my question is: what is the most pythonic strategy to achieve this?

Related

please how do i use a method to store a collection of class objects using a dictionary for to store the items with appropriate keys and values

class: Define a Python class(MovieList) with methods to do the following:
Define a constructor to create an object of the MovieList class.
A method to store a collection of movie objects that are created using the Movie class above. You should use a dictionary
for to store the items with appropriate keys and values.
A method to search through the collection and find a movie by one or more of the following movie attributes: title, genre
or release date.
A method to remove a movie from the collection based on the title of the movie.
A method to calculate and return the total number of movies stored in the collection.
You should include appropriate error checking and detailed comments in our code.
class MovieList:
def __init__(self, name, maxMovieList):
self.name = name
self.maxMovieList = maxMovieList
self.MovieCollection = []
def add_movie(self, movie):
if len(self.MovieCollection) < self.maxMovieList +1 :
self.MovieCollection.append(movie)
return True #if successfully added
return False #if not successfully added
def find_movie(self, title):
if self.title in MovieCollection:
return True
return False
As the instructions say, you should be storing the movies in a dictionary, not a list. Use the movie title as the key.
Instruction 3 says that the search method can take more than just the title. You can use keyword parameters, and then test if all the attributes match a movie. Use the getattr() method to retrieve an attribute dynamically.
class MovieList:
def __init__(self, name, maxMovieList):
self.name = name
self.maxMovieList = maxMovieList
self.MovieCollection = {}
def add_movie(self, movie):
if len(self.MovieCollection) < self.maxMovieList +1 :
self.MovieCollection[movie.title] = movie
return True #if successfully added
return False #if not successfully added
def find_movie(self, **attributes):
for movie in self.MovieCollection.values():
if all(getattr(movie, key) == value for key, value in attributes):
return movie
return False

Method __init__ has too many parameters

I'm super new to Python (I started about 3 weeks ago) and I'm trying to make a script that scrapes web pages for information. After it's retrieved the information it runs through a function to format it and then passes it to a class that takes 17 variables as parameters. The class uses this information to calculate some other variables and currently has a method to construct a dictionary. The code works as intended but a plugin I'm using with Pycharm called SonarLint highlights that 17 variables is too many to use as parameters?
I've had a look for alternate ways to pass the information to the class, such as in a tuple or a list but couldn't find much information that seemed relevant. What's the best practice for passing many variables to a class as parameters? Or shouldn't I be using a class for this kind of thing at all?
I've reduced the amount of variables and code for legibility but here is the class;
Class GenericEvent:
def __init__(self, type, date_scraped, date_of_event, time, link,
blurb):
countdown_delta = date_of_event - date_scraped
countdown = countdown_delta.days
if countdown < 0:
has_passed = True
else:
has_passed = False
self.type = type
self.date_scraped = date_scraped
self.date_of_event = date_of_event
self.time = time
self.link = link
self.countdown = countdown
self.has_passed = has_passed
self.blurb = blurb
def get_dictionary(self):
event_dict = {}
event_dict['type'] = self.type
event_dict['scraped'] = self.date_scraped
event_dict['date'] = self.date_of_event
event_dict['time'] = self.time
event_dict['url'] = self.link
event_dict['countdown'] = self.countdown
event_dict['blurb'] = self.blurb
event_dict['has_passed'] = self.has_passed
return event_dict
I've been passing the variables as key:value pairs to the class after I've cleaned up the data the following way:
event_info = GenericEvent(type="Lunar"
date_scraped=30/01/19
date_of_event=28/07/19
time=12:00
link="www.someurl.com"
blurb="Some string.")
and retrieving a dictionary by calling:
event_info.get_dictionary()
I intend to add other methods to the class to be able to perform other operations too (not just to create 1 dictionary) but would like to resolve this before I extend the functionality of the class.
Any help or links would be much appreciated!
One option is a named tuple:
from typing import Any, NamedTuple
class GenericEvent(NamedTuple):
type: Any
date_scraped: Any
date_of_event: Any
time: Any
link: str
countdown: Any
blurb: str
#property
def countdown(self):
countdown_delta = date_of_event - date_scraped
return countdown_delta.days
#property
def has_passed(self):
return self.countdown < 0
def get_dictionary(self):
return {
**self._asdict(),
'countdown': self.countdown,
'has_passed': self.has_passed,
}
(Replace the Anys with the fields’ actual types, e.g. datetime.datetime.)
Or, if you want it to be mutable, a data class.
I don't think there's anything wrong with what you're doing. You could, however, take your parameters in as a single dict object, and then deal with them by iterating over the dict or doing something explicitly with each one. Seems like that would, in your case, make your code messier.
Since all of your parameters to your constructor are named parameters, you could just do this:
def __init__(self, **params):
This would give you a dict named params that you could then process. The keys would be your parameter names, and the values the parameter values.
If you aligned your param names with what you want the keys to be in your get_dictionary method's return value, saving off this parameter as a whole could make that method trivial to write.
Here's an abbreviated version of your code (with a few syntax errors fixed) that illustrates this idea:
from pprint import pprint
class GenericEvent:
def __init__(self, **params):
pprint(params)
event_info = GenericEvent(type="Lunar",
date_scraped="30/01/19",
date_of_event="28/07/19",
time="12:00",
link="www.someurl.com",
blurb="Some string.")
Result:
{'blurb': 'Some string.',
'date_of_event': '28/07/19',
'date_scraped': '30/01/19',
'link': 'www.someurl.com',
'time': '12:00',
'type': 'Lunar'}

How to implement forms for sets and supersets of objects with Django

Imagine the following two Django models:
class Item(models.Model):
'''
A single Item of something.
'''
name = models.CharField(unique = True)
sets = model.ManyToManyField('Set', blank = True)
def get_supersets(self):
'''
Returns the list of all the supersets the item belongs to EXCLUDING the
directly linked sets.
'''
res = []
for set in self.sets:
res = res + set.get_all_supersets()
return res
class Set(models.Model):
'''
A set of items wich can either contain items or not (empty set allowed).
Sets can be grouped in supersets. Supersets will contain all items of
the related subsets.
'''
name = models.CharField(unique = True)
superset = models.ForeignKey('self', on_delete = models.SET_NULL, null = True, blank = True)
# Note: Self-reference to the same object is avoided by excluding it
from the forms queryset for the superset field.
def get_all_spersets(self):
'''
Returns all supersets of the set.
'''
if self.superset:
return [self.superset] + self.superset.get_all_supersets()
else:
return []
I found two options for implementing the connection between supersets and the corresponding items of the sets in a given superset:
On saving a set or an item update the item_set of the supersets. With this, all relations will be stored in the database. This also needs to include some logic regarding circular relations.
Decide for "direct-links-only", which means an item will only be linked to its directly related set in the database. The relations to the supersets will be found on the fly if requested (e.g get all supersets) with model methods.
For me, option 2 seems much more attractive in terms of data integrity since connected relations will be updated on the fly. However, once a user enters an item -> set relation one needs to make sure the user does not unnecessarily select a superset of a set the item already belongs to, which would break the logic and in the worst case lead to infinite recursion in the model methods to retrieve the supersets.
Since the selection will take place in a form, the Item form looks like this:
class ItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.fields['sets'].queryset = Set.objects.all()
# For now, this shows all available sets. One could limit this
queryset to only the sets not in the items get_superset().
However, this would not allow the user to see wich supersets
the item already belongs to.
class Meta:
model = Item
widgets = {'sets': forms.CheckboxSelectMultiple()}
# Available sets are shown as checkboxes.
The Set form looks like this:
class SetForm(forms.ModelForm):
def __init__(self):
self.fields['superset'].queryset = Set.objects.all.exclude(id__exact=self.instance.id)
# As mentioned, avoiding self-reference.
class Meta:
model = Set
My question:
1) How can I show the Items supersets in the ItemForm but avoid that a user chooses them?
2) If a user chooses a set, wich is part of a superset, this superset should immediately become unavailable in the ItemForm. Is something like this possible and how?

how to get class instances out of a dict in python

I have a class `Collection' that looks like this:
class Collection():
def __init__(self, db, collection_name):
self.db = db
self.collection_name = collection_name
if not hasattr(self.__class__, 'client'):
self.__class__.client = MongoClient()
self.data_base = getattr(self.client, self.db)
self.collection = getattr(self.data_base, self.collection_name)
def getCollectionKeys(self):
....etc.
I cleverly created a function to create a dictionary of class instances as follows:
def getCollections():
collections_dict = {}
for i in range(len(db_collection_names)):
collections_dict[db_collection_names[i]] = Collection(database_name, db_collection_names[i])
return collections_dict
it works. however, whenever I want to access a class instance, I have to go through the dictionary:
agents_keys = collections_dict['agents'].getCollectionKeys()
I would love to just write:
agents_keys = agents.getCollectionKeys()
Is there a simple way to get those instances "out" of the dict?
You can get a reference to items in a vanilla python dictionary using a generator object in a for loop, or by using a list expression.
agent_keys = [x.getCollectionKeys() for x in collections_dict.values()]
or this
agent_keys = []
for name in db_collection_names:
#do something with individual item
#there could also be some logic here about which keys to append
agent_keys.append(collections_dict[name].getCollectionKeys())
#now agent_keys is full of all the keys
My mental model of how objects are interacted with in python. Feel free to edit if you actually know how it works.
You cannot "take" items of the dictionary per say unless you call the del operator which removes the association of a variable name (that is what you type in the editor like "foo" and "bar") with an object ( the actual collections of bits in the program your machine sees). What you can do is get a reference to the object, which in python is a symbol that for all your intents and purposes is the object you want.
The dictionary just holds a bunch of references to your database objects.
The expression collections_dict['agents'] is equivalent to your original database object that you put into the dictionary like this
collections_dict['agents'] = my_particular_object

How to use filter in Python with a function which belongs to an object which is an element on the list being filtered?

To be specific in my case, the class Job has a number of Task objects on which it operates.
import tasker
class Job(object):
_name = None
_tasks = []
_result = None
def __init__(self, Name):
self._name = Name
def ReadTasks(self):
# read from a Json file and create a list of task objects.
def GetNumTasks(self):
return len(self._tasks)
def GetNumFailedTasks(self):
failTaskCnt = 0
for task in self._tasks:
if task.IsTaskFail():
failTaskCnt += 1
To make GetNumFailedTasks more succinct, I would like to use a filter, but I am not sure what is the correct way to provide filter with IsTaskFail as the first parameter.
In case, this is a duplicate, please mark it so, and point to the right answer.
You can use a generator expression with sum:
failTaskCnt = sum(1 for task in self._tasks if task.IsTaskFail())

Categories