Naming objects created in a loop - python

I want to create a loop that creates an object if a certain condition is met and then adding it to a list. However, I am unsure how to uniquely name these objects so they can be used/referenced later on. For example something like this:
favNumbers = []
numbers = [6, 21, 5, 22]
class favNumber(object):
def __init__(self, value):
self.value = value
for i in numbers:
if i % 2 == 0:
unique_name_for_object = favNumber(i)
favNumbers.append(unique_name_for_object)
How would I gave the favNumber objects a unique name?
I was thinking of somehow naming them number1, number2, number3... but I think dynamically naming them would be a bad idea.
Is there a way of doing it without dynamically naming them? It seems as if that would be impossible but I am unsure as to how else I could accomplish it.

There is no need to name the instances. Just try this one
favNumbers = []
numbers = [6, 21, 5, 22]
class favNumber(object):
def __init__(self, value):
self.value = value
for i in numbers:
if i % 2 == 0:
favNumbers.append( favNumber(i) )
Now, favNumbers is a list of all created instances. If you want to get the value ot the each instance, just type
for fn in favNumbers:
print(fn.value)

If you simply want to generate a list, you can use a list comprehension:
favNumbers = [favNumber(i) for i in numbers if n % 2 == 0]
The last expression (if n % n == 0) acts as a filter. To access the values, you use an index (eg. favNumbers[0] for the first item), or loop through the list like you did with numbers.
As someone pointed out in the comments, {} is a dict and cannot be appended to. You can however insert items given a key (which might as well be the integer i, although it usually makes more sense to use lists when the keys are consecutive non-negative integers).
for i in numbers:
if n % 2 == 0:
favNumbers[i] = favNumber(i)
It can also be done with a dictionary comprehension:
favNumbers = { i : favNumber(i) for i in numbers if n % n == 0 }
I'll update the answer if you provide additional context.

Related

How to fix 'int object is not iterable'

I'm trying to add all the integers in the 'a' variable, but this 'a' variable isn't working as a list nor a string, despite having various different integers in it.
I'm writing a Python program that given a positive integer num, provided by the user, prints the sum of all its divisors.
I've already tried to make this 'a' variable a list but the same error happens
import math
num = int(input("Num: "))
a = num + 1 # because range excludes the last number
b = range(1, a)
for i in (b):
x = num / i
if math.floor(x) == x:
c = list(i)
I've already tried to make this 'a' variable a list but the same error happens: 'int object is not iterable'
list() creates a new list, and its argument must be an iterable (e.g. a tuple, another list, etc.). If you only pass one number, i, it won't work.
I suppose what you want to do is not to create a new list with each loop iteration, but add the i element to an already existing list.
You can achieve it this way:
num = int(input("Num: "))
a = num + 1 # because range excludes the last number
b = range(1, a)
divisors = [] # create a new list where the results will be stored
for i in (b):
x = num / i
if math.floor(x) == x:
divisors.append(i) # add the number at the end of the list
If you want to sum all the divisors, use:
sum(divisors)
An even more 'Pythonic' (though, admittedly, not necessarily easier to read if you're not used to list comprehensions) way to achieve the same result would be:
num = int(input("Num: "))
divisors_sum = sum(i for i in range(1, num + 1) if num//i == num/i)
I assume you're using Python 3 here. In Python 3, // is floor division, so you don't have to use math.floor. See this post for more details on // vs. /.
You can create an empty list outside of the loop: c = [], and then each time append an element to the list by c.append(i).

Python Coin Change Dynamic Programming

I am currently trying to implement dynamic programming in Python, but I don't know how to setup the backtracking portion so that it does not repeat permutations.
For example, an input would be (6, [1,5]) and the expected output should be 2 because there are 2 possible ways to arrange 1 and 5 so that their sum is equivalent to 6. Those combinations are {1,1,1,1,1,1} and {1,5} but the way my program currently works, it accounts for the combinations displayed above and the combination {5,1}. This causes the output to be 3 which is not what I wanted. So my question is "How do I prevent from repeating permutations?". My current code is shown below.
import collections as c
class DynamicProgram(object):
def __init__(self):
self.fib_memo = {}
# nested dictionary, collections.defaultdict works better than a regular nested dictionary
self.coin_change_memo = c.defaultdict(dict)
self.__dict__.update({x:k for x, k in locals().items() if x != 'self'})
def coin_change(self, n, coin_array):
# check cache
if n in self.coin_change_memo:
if len(coin_array) in self.coin_change_memo[n]:
return [n][len(coin_array)]
# base cases
if n < 0: return 0
elif n == 1 or n == 0: return 1
result = 0
i = 0
# backtracking (the backbone of how this function works)
while i <= n and i < len(coin_array):
result += self.coin_change(n-coin_array[i], coin_array)
i += 1
# append to cache
self.coin_change_memo[n][len(coin_array)] = result
# return result
return result
One of the way of avoiding permutation is to use the numbers in "non-decreasing" order. By doing so you will never add answer for [5 1] because it is not in "non-decreasing" order.And [1 5] will be added as it is in "non-decreasing" order.
So the change in your code will be if you fix to use the ith number in sorted order than you will never ever use the number which is strictly lower than this.
The code change will be as described in Suparshva's answer with initial list of numbers sorted.
Quick fix would be:
result += self.coin_change(n-coin_array[i], coin_array[i:]) # notice coin_array[i:] instead of coin_array
But you want to avoid this as each time you will be creating a new list.
Better fix would be:
Simply add a parameter lastUsedCoinIndex in the function. Then always use coins with index >= lastUsedCoinIndex from coin array. This will ensure that the solutions are distinct.
Also you will have to make changes in your memo state. You are presently storing sum n and size of array(size of array is not changing in your provided implementation unlike the quick fix I provided, so its of no use there!!) together as a state for memo. Now you will have n and lastUsedCoinIndex, together determining a memo state.
EDIT:
Your function would look like:
def coin_change(self,coin_array,n,lastUsedCoinIndex):
Here, the only variables changing will be n and lastUsedCoinIndex. So you can also modify your constructor such that it takes coin_array as input and then you will access the coin_array initialized by constructor through self.coin_array. Then the function would become simply:
def coin_change(self,n,lastUsedCoinIndex):

How to sort a list of objects based on multiple attributes (and also count them) in Python?

This is how my class looks:
class Item:
def __init__(self, name, is_old, is_init):
self.name = name
self.is_old = is_old
self.is_init = is_init
I have a list of these objects and I want to sort them like this: if the is_init parameter is true, they have to be in the front of the list, if the is_old parameter is true they have to be at the end of the list. The others should be in the middle. I would also like to generate some sort of count for each object (how many have the is_old parameter true, how many have is_init parameter true, etc).
I have been using this:
is_init_count = sum(p.is_init == True for p in item_list)
is_old_count = sum(p.is_old == True for p in item_list)
other_count = len(item_list) - is_init_count + is_old_count
but I'm thinking there might be a more pythonic way and that this could be done together with the sorting.
You can sort with a key that returns a tuple of the properties you're interested in:
item_list = sorted(item_list, key=lambda x: (not x.is_init, x.is_new))
Note that the is_old parameter is saved as self.is_new in your class, which may lead to some confusion.
Next, you can loop through this list a single time and count all the properties you're interested in:
init = 0
old = 0
for i in item_list:
if i.is_init:
init += 1
if i.is_new:
old += 1

List index out of range

I'm trying to create my own Hash data structure in python. In __init__ I initialize a list (m_list) of size m and in another function I add hashes to it from my hash function.
I'm now trying to search through the list, looking for value k. I'm getting a list index out of range error on the if self.m_list[i] == k: line.
class Hash:
def __init__ (self, n, m, m_list=None):
self.n = n
self.m = m
self.a = choice(range(1, n))
self.b = choice(range(n))
if m_list is None:
m_list = []
self.m_list = m_list * m
def search(self, k):
found = False
for i in self.m_list:
if i is not None and found is False:
if self.m_list[i] == k:
found = True
if found:
print True
else:
print False
I created m_list using guidelines from Create an empty list in python with certain size
There are multiple problems with this code:
1) Indexing a list with its own contents.
for i in self.m_list:
when you loop on a list in python using this syntax, the value in the variable (i) is the value from in the list, not the index for that iteration.
There are two choices of ways to solve this. If you, for some reason need to have the index, you can loop by using the range function to create the indices and loop over them, like so:
for i in range(len(self.m_list)):
if not found and self.m_list[i] == k:
found = True
Or you can just use python's native iteration over the contents of the list:
for item in self.m_list:
if not found and item == k:
found = True
Another option, if you want easy access to both the index and the value is to use enumerate. enumerate returns tuples containing the index of the value and the value itself, so you can use python's multi-assignment to have access to both:
for i, val in enumerate(self.m_list):
if val == k:
...
if i == some_index
...
The original code will only ever return true if m_list[i] == i == k, so if you indented to check that this condition held, you could just check m_list[k] == k.
2) As noted in Peter's answer, [] * m always gives [], so no matter what the indexes being provided are, the list will have zero length and therefore any index will be out of range. To get a list with length m, you need to have one element in the list to duplicate. You can use None or 0 as that value: [0] * m gives a list of m zeroes, and [None] * m gives a list of m none values.
You are not creating a list of size m. [] * m gives you [], as you can see in an interactive shell. The linked answers show how multiplying a list will shallow copy the contents of the list m times, but of course [] has no contents to copy. Try if m_list is None: m_list = [None] * m or something similar.
Your search method makes no sense to me (there are better ways to store just the existence of integers) but that's a separate problem.

Python: Sort a list according to two attributes

I have a list of the following kind:
class Ind(object):
def __init__(self,ID,mate):
self.ID=ID
self.mate=mate
population=[Ind(8,None), Ind(1,2), Ind(20,3), Ind(2,1), Ind(12,None), Ind(3,20), Ind(10,11), Ind(11,10)]
You can think of this list population as a population of individuals which all have an ID. Some of them have a mate (an individual who is present in the same population or the same list). The mate value is actually the ID of the mate! Therefore, if there is an instance of Ind which attributes ID equals 12 and mate equals 34, then there is necessarily an individual in the list whose ID equals 34 and whose mate equals 12. Individuals that do not have a mate have None in the mateattribute. Does it make sense?
I'd like to sort this list so that the first individual mates with the last one, the second individual mates with the second-to-last individual, etc... The individual which attribute mateequals None should stand in the middle of the list.
There are many possible outputs that fit what I want. Here is one example of these outputs for the above list:
population=[Ind(1,2), Ind(20,3), Ind(10,11), Ind(8,None), Ind(12,None), Ind(11,10), Ind(3,20), Ind(2,1)]
You can try something like this:
def custom_sort(population):
pop_dict = { ind.ID: ind for ind in population }
start = []
nones = []
end = []
for ind in population:
if ind.mate is None:
nones.append(ind)
elif pop_dict[ind.mate] not in start:
start.insert(0, ind)
end.append(pop_dict[ind.mate])
return start + nones + end
This is under assumption that "being a mate" is a 1-to-1 relation.
You just need a key for the sorting function. The following example requires that individuals are monogamous and not married to themselves. It also requires that if (a,b) is listed, (b,a) is also listed. If these prerequisites are not met and Ind(2,1) can occur without Ind(1,2), this function will place Ind(2,1) towards the end of the list. The first index in the key function is the type: "first" in relationship (where IDmate) comes third. These first and second types are sorted in order by their ids; last type is sorted in reverse order by its mate.
def keyfun(x):
if x.mate==None:
return (1,x.ID)
elif x.ID<x.mate:
return (0,x.ID)
else:
return (2,-x.mate)
sorted(population,key=keyfun)
Another way to handle this, still assuming that if (a,b) is in the list (b,a) will also be in the list, is to just preprocess by removing (b,a) cases, then postprocess by adding them back in in reverse order.
How about this. Split list into three lists, one with ID < mate, the second with ID > mate, and the third with mate is None. Then, concatenate the sorted lists, each sorted via ID.
I've added a __repr__ method to the Ind class for output readability.
class Ind(object):
def __init__(self,ID,mate):
self.ID=ID
self.mate=mate
def __repr__(self):
return 'Ind({},{})'.format(self.ID,self.mate)
population=[Ind(8,None), Ind(1,2), Ind(2,3), Ind(2,1), Ind(12,None), Ind(3,2), Ind(10,11), Ind(11,10)]
def custom_sort(pop):
singles, less, more = [], [], []
for p in pop:
if p.mate is None:
singles.append(p)
elif p.ID < p.mate:
less.append(p)
elif p.ID > p.mate:
more.append(p)
comp = lambda x,y: cmp(x.ID,y.ID)
return sorted(less,cmp=comp) + sorted(singles,cmp=comp) + sorted(more,cmp=comp,reverse=True)
print custom_sort(population)
This outputs:
[Ind(1,2), Ind(2,3), Ind(10,11), Ind(8,None), Ind(12,None), Ind(11,10), Ind(3,2), Ind(2,1)]
There is a lot you can do with costum key functions:
def my_key(ind):
if ind.mate is None:
return 0
if ind.ID < ind.mate:
return -ind.ID - 1
else:
return ind.mate + 1
population.sort(key=my_key)
This assumes that IDs will never be negative. If IDs are always greater than 0, you can discard the - 1 and + 1.

Categories

Resources