Python cannot iterate a list completely - list

So I posted this before but I didn't follow the community guidelines so I decided to post it again this time following the community guidelines. (I tried to delete my other question but it wouldn't let me)
Here is the minimal amount of code I could do to create the same problem:
class Object:
def __init__(self, *args, **kwargs):
for k, v in kwargs.iteritems():
setattr(self, k, v)
self.inventory = []
try: self.itemComponent.owner = self
except: self.itemComponent = None
class Item:
def drop(self):
for obj in objects:
if self.owner in obj.inventory:
objects.append(self.owner)
obj.inventory.remove(self.owner)
def monster_death(monster):
monster.name = 'Goblin Corpse'
for element in monster.inventory:
if element.itemComponent:
element.itemComponent.drop()
objects = []
#Create Goblin
monster = Object(name = 'Goblin')
#Populate Goblin's Equipment
monster.inventory = [
Object(name='Dagger', itemComponent=Item() ),
Object(name='Light Healing Potion', itemComponent=Item() ),
Object(name='Scroll of Chain Lightning', itemComponent=Item())
]
objects.append(monster)
print '=~In Monster Inventory~='
for item in monster.inventory:
print item.name
print
print '=~In World~='
for obj in objects:
print obj.name
print
print '***MONSTER DIES***'
print
monster_death(monster)
print '=~In Monster Inventory~='
print
for item in monster.inventory:
print item.name
print
print '=~In World~='
print
for obj in objects:
print obj.name
What happens is one of the items always stays in the monsters inventory...it seems almost random which item stays in but it is always the same item every time unless I remove or add more items to his inventory.

You're removing from the list that you are currently iterating through, that will affect the iteration.
If you need to process each item, then do that in the loop, and then clear the list afterwards
my_list = ['a', 'b', 'c']
my_list[:] = [] # clear the list without replacing it

Related

Implement Recursive function to check whether a model has child or not

I'm trying to implement a recursive function that will check if an object has child with the parent id. If has than it will add the child to the list and will recursively call the function to check if newly added child has further children.
My Current Code that works fine as expected:
def get_nav_items(self, instance):
childs = []
items = Content.objects.filter(parent_id=instance)
for item in items:
childs.append(item)
for item in items:
ch = Content.objects.filter(parent_id=item)
if ch.count() > 0:
for c in ch:
childs.append(c)
menu_objecs = []
for item in childs:
menu_objecs.append(ContentNevItemSerializer(item).data)
return menu_objecs
The method that isn't returning any result now:
def extract_item(self, nav_obj, nav_list = []):
cont = Content.objects.filter(parent_id=nav_obj)
if len(cont) == 0:
return nav_list
else:
for ct in cont:
self.extract_item(ct, nav_list)
# nav_list.append(cont)
return nav_list
Here is the recursive version of checking children of django query objects.
The trick was to send a blank list through the parameter and check if the queryset has object more than zero. If the result is zero than return the passed list.
However, if there is object then it would add each objects to the list and recursively call the function and finally return the complete list.
NB: checking the object with len() will lead the queryset to re-check the database. Instead since django caches the ORM result therefore, count() will use the cache and calculate already received result.
def extract_item(self, nav_obj, nav_list: list):
"""
Recursively call and check if there is any child available.
"""
cont = Content.objects.filter(parent_id=nav_obj)
if len(cont) == 0:
return nav_list
else:
for ct in cont:
nav_list.append(ct)
self.extract_item(ct, nav_list)
return nav_list
def get_nav_items(self, instance):
"""
Get all the children for the model object
"""
childs = self.extract_item(instance, [])
menu_objecs = []
for item in childs:
menu_objecs.append(ContentNevItemSerializer(item).data)
return menu_objecs

Django - why does a list passed as context not clear on refresh, and instead duplicates?

Hard to explain this in a simple subject. This is with a GET, no POSTs.
I have a function defined in a different file, we'll say list_gen.py that returns list 'gen_list'. 'gen_list' is a global variable here.
In the views.py, I call this function and assign the output as 'output_list' and send it with the return as such: " return render(request, 'detail.html', {'output_list':output_list}.
Then, in detail.html I simply place the results with {{output_list}}.
When I visit the page, I see the exact output I expected. But, if press refresh, the output duplicates and continues to do so each time I press refresh or if I visit another entry that uses that detail.html page.
Has anyone seen this before?
Below is the view in question. The suspect list is "ped"
#login_required(login_url='/accessdenied/')
def detail(request, did):
try:
dog = Dogs.objects.get(did__exact=did)
resp = pedigree(did, 3)
ped = ''.join(map(str, resp))
try:
dam = Dogs.objects.get(did__exact=dog.dam).registered_name
except:
dam = "Not Listed"
try:
sire = Dogs.objects.get(did__exact=dog.sire).registered_name
except:
sire = "Not Listed"
parents = {'dam': dam, 'sire': sire}
except Dogs.DoesNotExist:
raise Http404("Dog does not exist")
return render(request, 'main/detail.html', {'dog': dog, 'parents': parents, 'ped': ped})
Below am image of the first time visiting the details for a specific entry (a dog):
And refresh a few times, and then visit a different entry. Note that each recent entry appears at the bottom.
The pedigree function (which calls printTree). Yes... I am making an html file into a list which is probably dumb. BUT, I get the exact same duplication if I use the "dog" list you see commented out where used.
dog = []
ped = ['<table border=1>']
def pedigree(did, max):
'''
:param did: id of the child dog
:param max: max number of generations to display
:return: list of dogs
'''
con = sqlite3.connect(f)
con.row_factory
cursor = con.execute('SELECT "Registered Name", ROUND(((JulianDay("now") - JulianDay("Date of Birth"))/365.25),1), "Sex" FROM dogs where did IS %s ' % did).fetchall()
for row in cursor:
name, age, gender = row[0], row[1], row[2]
sql = "SELECT a.Sire, a.Dam, s.'Registered Name', d.'Registered Name' FROM dogs a INNER JOIN dogs s ON a.Sire = s.did INNER JOIN dogs d ON a.Dam = d.did WHERE a.did = "
printTree(sql, did, name, 0, max)
return ped
#return dog
def printTree(stmt, did, name, N, max):
'''
:param stmt: sql statement to query information
:param did: dog to obtain information on
'''
rspan = 2**(max-N)
if (rspan > 1):
ped.append('<td rowspan='+str(rspan)+'><a href=/dogs/'+str(did)+'>'+name+'</td>')
# dog.append({'name': name, 'rspan': rspan})
else:
ped.append('<td><a href=/dogs/'+str(did)+'>'+name+"</td>")
if (N == max):
ped.append("</tr><tr>")
if(N < max):
s = None
d = None
sn = None
dn = None
con = sqlite3.connect(f).execute(stmt+str(did)).fetchall()
for row in con:
s, d, sn, dn = row[0], row[1], row[2], row[3]
if (s and sn) != None:
printTree(stmt, s, sn, N+1, max)
if (d and dn) != None:
printTree(stmt, d, dn, N+1, max)
Thanks to the comments, I fixed by altering the pedigree function as such to clear the list.
ped = []
def pedigree(did, max):
'''
:param did: id of the child dog
:param max: max number of generations to display
:return: list of dogs
'''
ped.clear()
ped.append('<table border=1>')
con = sqlite3.connect(f)

Python 2.7 create a dictionary from dotted values [duplicate]

I'm trying to programmatically set a value in a dictionary, potentially nested, given a list of indices and a value.
So for example, let's say my list of indices is:
['person', 'address', 'city']
and the value is
'New York'
I want as a result a dictionary object like:
{ 'Person': { 'address': { 'city': 'New York' } }
Basically, the list represents a 'path' into a nested dictionary.
I think I can construct the dictionary itself, but where I'm stumbling is how to set the value. Obviously if I was just writing code for this manually it would be:
dict['Person']['address']['city'] = 'New York'
But how do I index into the dictionary and set the value like that programmatically if I just have a list of the indices and the value?
Python
Something like this could help:
def nested_set(dic, keys, value):
for key in keys[:-1]:
dic = dic.setdefault(key, {})
dic[keys[-1]] = value
And you can use it like this:
>>> d = {}
>>> nested_set(d, ['person', 'address', 'city'], 'New York')
>>> d
{'person': {'address': {'city': 'New York'}}}
I took the freedom to extend the code from the answer of Bakuriu. Therefore upvotes on this are optional, as his code is in and of itself a witty solution, which I wouldn't have thought of.
def nested_set(dic, keys, value, create_missing=True):
d = dic
for key in keys[:-1]:
if key in d:
d = d[key]
elif create_missing:
d = d.setdefault(key, {})
else:
return dic
if keys[-1] in d or create_missing:
d[keys[-1]] = value
return dic
When setting create_missing to True, you're making sure to only set already existing values:
# Trying to set a value of a nonexistent key DOES NOT create a new value
print(nested_set({"A": {"B": 1}}, ["A", "8"], 2, False))
>>> {'A': {'B': 1}}
# Trying to set a value of an existent key DOES create a new value
print(nested_set({"A": {"B": 1}}, ["A", "8"], 2, True))
>>> {'A': {'B': 1, '8': 2}}
# Set the value of an existing key
print(nested_set({"A": {"B": 1}}, ["A", "B"], 2))
>>> {'A': {'B': 2}}
Here's another option:
from collections import defaultdict
recursivedict = lambda: defaultdict(recursivedict)
mydict = recursivedict()
I originally got this from here: Set nested dict value and create intermediate keys.
It is quite clever and elegant if you ask me.
First off, you probably want to look at setdefault.
As a function I'd write it as
def get_leaf_dict(dct, key_list):
res=dct
for key in key_list:
res=res.setdefault(key, {})
return res
This would be used as:
get_leaf_dict( dict, ['Person', 'address', 'city']) = 'New York'
This could be cleaned up with error handling and such. Also using *args rather than a single key-list argument might be nice; but the idea is that
you can iterate over the keys, pulling up the appropriate dictionary at each level.
Here is my simple solution: just write
terms = ['person', 'address', 'city']
result = nested_dict(3, str)
result[terms] = 'New York' # as easy as it can be
You can even do:
terms = ['John', 'Tinkoff', '1094535332'] # account in Tinkoff Bank
result = nested_dict(3, float)
result[terms] += 2375.30
Now the backstage:
from collections import defaultdict
class nesteddict(defaultdict):
def __getitem__(self, key):
if isinstance(key, list):
d = self
for i in key:
d = defaultdict.__getitem__(d, i)
return d
else:
return defaultdict.__getitem__(self, key)
def __setitem__(self, key, value):
if isinstance(key, list):
d = self[key[:-1]]
defaultdict.__setitem__(d, key[-1], value)
else:
defaultdict.__setitem__(self, key, value)
def nested_dict(n, type):
if n == 1:
return nesteddict(type)
else:
return nesteddict(lambda: nested_dict(n-1, type))
The dotty_dict library for Python 3 can do this. See documentation, Dotty Dict for more clarity.
from dotty_dict import dotty
dot = dotty()
string = '.'.join(['person', 'address', 'city'])
dot[string] = 'New York'
print(dot)
Output:
{'person': {'address': {'city': 'New York'}}}
Use these pair of methods
def gattr(d, *attrs):
"""
This method receives a dict and list of attributes to return the innermost value of the give dict
"""
try:
for at in attrs:
d = d[at]
return d
except:
return None
def sattr(d, *attrs):
"""
Adds "val" to dict in the hierarchy mentioned via *attrs
For ex:
sattr(animals, "cat", "leg","fingers", 4) is equivalent to animals["cat"]["leg"]["fingers"]=4
This method creates necessary objects until it reaches the final depth
This behaviour is also known as autovivification and plenty of implementation are around
This implementation addresses the corner case of replacing existing primitives
https://gist.github.com/hrldcpr/2012250#gistcomment-1779319
"""
for attr in attrs[:-2]:
# If such key is not found or the value is primitive supply an empty dict
if d.get(attr) is None or isinstance(d.get(attr), dict):
d[attr] = {}
d = d[attr]
d[attrs[-2]] = attrs[-1]
Here's a variant of Bakuriu's answer that doesn't rely on a separate function:
keys = ['Person', 'address', 'city']
value = 'New York'
nested_dict = {}
# Build nested dictionary up until 2nd to last key
# (Effectively nested_dict['Person']['address'] = {})
sub_dict = nested_dict
for key_ind, key in enumerate(keys[:-1]):
if not key_ind:
# Point to newly added piece of dictionary
sub_dict = nested_dict.setdefault(key, {})
else:
# Point to newly added piece of sub-dictionary
# that is also added to original dictionary
sub_dict = sub_dict.setdefault(key, {})
# Add value to last key of nested structure of keys
# (Effectively nested_dict['Person']['address']['city'] = value)
sub_dict[keys[-1]] = value
print(nested_dict)
>>> {'Person': {'address': {'city': 'New York'}}}
This is a pretty good use case for a recursive function. So you can do something like this:
def parse(l: list, v: str) -> dict:
copy = dict()
k, *s = l
if len(s) > 0:
copy[k] = parse(s, v)
else:
copy[k] = v
return copy
This effectively pops off the first value of the passed list l as a key for the dict copy that we initialize, then runs the remaining list through the same function, creating a new key under that key until there's nothing left in the list, whereupon it assigns the last value to the v param.
This is much easier in Perl:
my %hash;
$hash{"aaa"}{"bbb"}{"ccc"}=1; # auto creates each of the intermediate levels
# of the hash (aka: dict or associated array)

How to print out the foods list?

How can I print out the list of "foods"?
All I can print was something like memory location.
class Fridge:
isOpened = False
foods = []
def open(self):
self.isOpened = True
print "Fridge open"
def put(self, thing):
if self.isOpened:
self.foods.append(thing)
print 'Food in'
else:
print 'Cannot do that'
def close(self):
self.isOpened = False
print 'Fridge closed.'
def __repr__(self):
return "%s" % (self.foods[0])
class Food:
pass
The way you defined repr(), it will only produce the first item in the list foods (as indicated by foods[0]). Furthermore, if the list foods is empty, calling repr() will result in an IndexError.
One way to print the list would be:
def __repr__(self):
return str([food for food in self.foods])
If you are not familiar with the syntax, please check out List Comprehensions.
Here's an example use case of your class:
>>> f = Fridge()
>>> f.open()
Fridge open
>>> f.put("Fruit")
Food in
>>> f.put("Vegetables")
Food in
>>> f.put("Beer")
Food in
>>> print f
['Fruit', 'Vegetables', 'Beer']

how to count articles in dictionary in python

I am wanting to make a count of items in the dictionary. When a user enters a value to search, it finds the value's key, then returns the next key before it. i.e.({john:fred, fred: bill})
if the user enters john, it will return bill and vice versa.
My code is:
myDict = {}
with open("names.dat") as f:
for line in f:
for pair in line.strip ().split(', '):
i = 0
x = 0
(key, val) = pair.split (":")
myDict[key.strip(":")] = val
print "father: ", key , " son: ", val
def findFather (myDict, lookUp):
father = ""
for key, val in myDict.iteritems( ):
if val == lookUp:
key = key
father = key
return father
lookUp = raw_input ("Enter a son's name: ")
print findFather(myDict, lookUp)
the dictionary values are:
john:fred, fred:bill, sam:tony, jim:william, william:mark, krager:holdyn, danny:brett, danny:issak, danny:jack, blasen:zade, david:dieter, adam:seth, seth:enos
According to your comments, you're trying to find the grandfather of a person in a database mapping people to their fathers:
def read_db(fname):
with open(fname) as f:
return dict(pair.split(":")
for pair in line.strip().split(", ")
for line in f)
def grandfather(db, person):
return db.get(db.get(person)) # same as db[db[person]] except returns None when no match found instead of error
def main():
DB = read_db("names.dat")
print "\n".join("father: %s son: %s" % pair for pair in DB)
person = raw_input("Enter a son's name: ")
print grandfather(DB, person) # prints None if no father or no grandfather found
main()
As you can see, DB is the only needed variable in the entire program (person in main can also be inlined); in your question, your code contains a lot of noise that does absolutely nothing preventing you from understanding your code and making your question look very untidy. So try to clean everything up next time you ask a question on Stack Overflow. Also, check out the almighty PEP8!