Can't stop python from iterating through string in loop - python

class Hat:
def __init__(self, **kwargs):
self.contents = []
for balltype in kwargs.keys():
for ballnum in range(kwargs[balltype]):
self.contents += balltype
hattrial = Hat(red = 1, blue = 2)
print(hattrial.contents)
I'm trying top create a list that contains the keys from the input argument dictionary, but instead of simply adding the string entry I get:
['r', 'e', 'd', 'b', 'l', 'u', 'e', 'b', 'l', 'u', 'e']
Instead of:
['red', 'blue', 'blue']
Where red occurs once and blue occurs twice. I've tried a few different solutions short of just manipulating the array afterwards such as the attempt below but nothing I've done has changed the output. Surely there's an elegant solution that doesn't require me sticking characters back together?
end = len(balltype)
self.contents += balltype[0:end]
self.contents += balltype

Using append
class Hat:
def __init__(self, **kwargs):
self.contents = []
for balltype in kwargs.keys():
for ballnum in range(kwargs[balltype]):
self.contents.append(balltype)
hattrial = Hat(red = 1, blue = 2)
print(hattrial.contents)
Be careful with the += operator in lists
This also works, try to understand why it appends correctly here with +=
class Hat:
def __init__(self, **kwargs):
self.contents = []
for balltype in kwargs.keys():
self.contents += kwargs[balltype] * [balltype]
hattrial = Hat(red = 1, blue = 2)
print(hattrial.contents)
Basically, the problem with your code can be reduced to the following:
a = []
a += "hello"
a
['h', 'e', 'l', 'l', 'o']

Related

Concat method in Class

I am working through the following exercise: I am implementing a class that represents sorted lists of basic types.
Currently:
class SortedList():
def __init__(self, input_list):
self.input_list= input_list
def add(self,value):
self.input_list.append(value)
return self.input_list
def concat(self):
return
def __repr__(self):
self.input_list.sort()
return str(self.input_list)
I make the following calls:
l1= SortedList(['z','l','a'])
print(l1)
l1.add('b')
print(l1)
l2= SortedList(['q','g'])
l3= l1.cocat(l2)
print(l3)
Everything behaves as expected until the l3 definition, since unsure how to define this type of function x.function(y) within a class.
The desired output from the last print statement is ['a','b','g','l','q','z']
You can use the + operator on lists which extends a list with another list, and then return a new instance of SortedList when concat() is called.
class SortedList:
def __init__(self, input_list):
self.input_list = sorted(input_list)
def add(self, value):
self.input_list.append(value)
self.input_list.sort()
return self.input_list
def concat(self, other):
merged = self.input_list + other.input_list
return SortedList(merged)
def __repr__(self):
return str(self.input_list)
l1 = SortedList(["z", "l", "a"])
print(l1)
# ['a', 'l', 'z']
print(l1.add("b"))
# ['a', 'b', 'l', 'z']
l2 = SortedList(["q", "g"])
l3 = l1.concat(l2)
print(l3)
# ['a', 'b', 'g', 'l', 'q', 'z']

Python Method in a Class

I'm trying to start using objects properly, I've built a deck of cards which is my object. I want to be able shuffle and deal cards from it. However I can't figure out how to get the shuffle method to work correctly or even if this is the best way to do it.
import itertools
import random
class Deck:
'''Deck of cards to be used in a card game'''
def __init__(self):
self.faces = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4','3', '2']
self.suits = ['c', 'd', 'h', 's']
self.cards = set(itertools.product(self.faces, self.suits))
def shuffle(self):
self.cards = random.shuffle(self.cards)
def deal(self):
card = self.cards.pop()
return card[0] + card[1]
Usage;
deck = Deck()
deck.shuffle()
deck.deal()
Sets are not ordered, you could use list() to obtain an ordered deck.
Furthermore random.shuffle(l) acts directly on the list and returns None, so you are overwriting the list with None.
import itertools
import random
class Deck:
'''Deck of cards to be used in a card game'''
def __init__(self):
self.faces = ['A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4','3', '2']
self.suits = ['c', 'd', 'h', 's']
self.cards = list(itertools.product(self.faces, self.suits)) # ordered deck
# self.cards = set(itertools.product(self.faces, self.suits)) # unordered deck
def shuffle(self):
random.shuffle(self.cards)
def deal(self):
card = self.cards.pop()
return card[0] + card[1]

Yen's Algorithm implementation not choosing shortest paths

I am using Yen's Algorithm (Wikipedia) to find k shortest paths in a graph. In the example below, my graph is a dictionary where each node is a key, with its value being the neighbors. Map() from dotmap simply allows for dictionaries to be converted into an object where keys can be accessed with dot notation. I want to find the four shortest paths in descending order from A to F where every edge has equal weight. The first two are ties (A > B > D > F) and (A > E > D > F), and the next two are (A > B > C > G > F) and finally (A > B > D > C > G > F). It is possible that my implementation of Dijkstra's (called AStar despite having no heuristic) is flawed because it is returning an empty list when no path is found. How can I have my code only pick the valid paths? Currently it returns [['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], [], []] -- it should return [['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], ['A', 'B', 'C', 'G', 'F'], ['A', 'B', 'D', 'C', 'G', 'F']] which are the shortest paths.
import copy
import heapq
from dotmap import Map
from itertools import count
graph = {
'A': ['B', 'E'],
'B': ['C', 'D'],
'C': ['G'],
'D': ['C', 'F'],
'E': ['D'],
'F': [],
'G': ['F']
}
class PriorityQueue:
def __init__(self):
self.elements = []
self._counter = count()
def empty(self):
return len(self.elements) == 0
def put(self, item, priority):
heapq.heappush(self.elements, (priority, item,))
def get(self):
return heapq.heappop(self.elements)[1]
class AStar:
def __init__(self, graph, start, goals=[]):
self.graph = graph
self.start = start
self.frontier = PriorityQueue()
self.frontier.put(start, 0)
self.previous = {}
self.previous[start] = None
self.costs = {}
self.costs[start] = 0
self.final_path = None
self.goals = goals
self.goal = None
def search(self):
graph = self.graph
frontier = self.frontier
goals = self.goals
costs = self.costs
while not frontier.empty():
state = frontier.get()
if state in goals:
cost = self.costs[state]
self.goal = state
self.final_path = self.trace_path()
return Map({'path': self.final_path, 'cost': cost})
for next_state in graph[state]:
new_cost = costs[state] + 1
if next_state not in costs or new_cost < costs[next_state]:
costs[next_state] = new_cost
priority = new_cost
frontier.put(next_state, priority)
self.previous[next_state] = state
# No path found
return Map({'path': [], 'cost': 0})
def trace_path(self):
current = self.goal
path = []
while current != self.start:
path.append(current)
current = self.previous[current]
path.append(self.start)
path.reverse()
return path
def YenKSP(graph, source, sink, k_paths):
graph_clone = copy.deepcopy(graph)
A = [AStar(graph, source, sink).search().path]
B = []
for k in range(1, k_paths):
for i in range(len(A[-1]) - 1):
spur_node = A[-1][i]
root_path = A[-1][:i+1]
for path in A:
if len(path) > i and root_path == path[:i+1]:
graph_clone[path[i]].remove(path[i+1])
result = AStar(graph_clone, spur_node, sink).search()
spur_path = result.path
total_path = root_path[:-1] + spur_path
spur_cost = AStar(graph_clone, source, spur_node).search().cost
B.append(Map({'path': total_path, 'cost': result.cost + spur_cost}))
graph_clone = copy.deepcopy(graph)
if len(B) == 0:
break
B.sort(key=lambda p: (p.cost, len(p.path)))
A.append(B[0].path)
B.pop()
return A
paths = YenKSP(graph, 'A', 'F', 4)
print(paths)
import copy
import heapq
#from dotmap import Map
from itertools import count
class Map(dict):
def __getattr__(self, k):
return self[k]
def __setattr__(self, k, v):
self[k] = v
graph = {
'A': ['B', 'E'],
'B': ['C', 'D'],
'C': ['G'],
'D': ['C', 'F'],
'E': ['D'],
'F': [],
'G': ['F']
}
class PriorityQueue:
def __init__(self):
self.elements = []
self._counter = count()
def empty(self):
return len(self.elements) == 0
def put(self, item, priority):
heapq.heappush(self.elements, (priority, item,))
def get(self):
return heapq.heappop(self.elements)[1]
class AStar:
def __init__(self, graph, start, goals=[]):
self.graph = graph
self.start = start
self.frontier = PriorityQueue()
self.frontier.put(start, 0)
self.previous = {}
self.previous[start] = None
self.costs = {}
self.costs[start] = 0
self.final_path = None
self.goals = goals
self.goal = None
def search(self):
graph = self.graph
frontier = self.frontier
goals = self.goals
costs = self.costs
while not frontier.empty():
state = frontier.get()
if state in goals:
cost = self.costs[state]
self.goal = state
self.final_path = self.trace_path()
return Map({'path': self.final_path, 'cost': cost})
for next_state in graph[state]:
new_cost = costs[state] + 1
if next_state not in costs or new_cost < costs[next_state]:
costs[next_state] = new_cost
priority = new_cost
frontier.put(next_state, priority)
self.previous[next_state] = state
# No path found
return Map({'path': [], 'cost': float('inf')})
def trace_path(self):
current = self.goal
path = []
while current != self.start:
path.append(current)
current = self.previous[current]
path.append(self.start)
path.reverse()
return path
def YenKSP(graph, source, sink, k_paths):
A = [AStar(graph, source, sink).search().path]
B = []
for _ in range(1, k_paths):
for i in range(len(A[-1]) - 1):
graph_clone = copy.deepcopy(graph)
spur_node = A[-1][i]
root_path = A[-1][:i+1]
for path in A:
if len(path) > i and root_path == path[:i+1]:
if path[i+1] in graph_clone[path[i]]:
graph_clone[path[i]].remove(path[i+1])
result = AStar(graph_clone, spur_node, sink).search()
spur_path = result.path
total_path = root_path[:-1] + spur_path
spur_cost = AStar(graph_clone, source, spur_node).search().cost
B.append(Map({'path': total_path, 'cost': result.cost + spur_cost}))
if len(B) == 0:
break
B.sort(key=lambda p: (p.cost, len(p.path)))
best_b = B.pop(0)
if best_b.cost != float('inf'):
A.append(best_b.path)
return A
paths = YenKSP(graph, 'A', 'F', 4)
print(paths)
Produces:
[['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], ['A', 'B', 'C', 'G', 'F'], ['A', 'B', 'D', 'C', 'G', 'F']]
The main issue was that when there was no path found, your default returned a path with 0 cost. So when sorted by path cost, these paths were appearing as the best choice in B and being added to A. I changed the default path cost to float('inf'). Doing so revealed an error that could occur when you tried to remove the same edge twice from graph_clone (inside for path in A: ...), so I added an if check to conditionally remove the edge. The two last things the diff indicate that I did were (a) imitate your dotmap.Map class (you can remove this and uncomment the import), and (b) only add a path to the resultset A if the cost is finite.

Issues with classes in Python 3: class doesn't recognize variable that was declared within it

I am creating a calculator in Python 3 in which you can type a full problem such as:
3 + 2
or
5 * 2
And I want it to be able to calculate just from that info.
Here is the code I already have:
# calc.py
import os
class Main:
def calculate(self):
# At the moment, \/ this is not in use.
self.alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
self.numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.operators = ['+', '-', '*', '/']
self.prob = input('>>>')
os.system('cls')
self.prob.split()
self.num = 'a'
for i in range(0, len(self.prob) - 1):
if self.prob[i] in self.numbers:
if self.num == 'a':
self.a = int(self.prob[i])
if self.num == 'b':
self.b = int(self.prob[i])
if self.prob[i] in self.operators:
self.operator = self.prob[i]
self.num = 'b'
if self.prob[i] == ' ':
pass
if self.operator == '+':
self.c = self.a + self.b
elif self.operator == '-':
self.c = self.a - self.b
elif self.operator == '*':
self.c = self.a * self.b
elif self.operator == '/':
self.c = self.a / self.b
print(self.c)
os.system('pause')
os.system('cls')
main = Main()
main.calculate()
It's giving me the error below:
Traceback (most recent call last):
File "C:\Python33\Programs\calc.py", line 48, in <module>
main.calculate()
File "C:\Python33\Programs\calc.py", line 31, in calculate
self.c = self.a + self.b
AttributeError: 'Main' object has no attribute 'a'
There is a variable named self.a in the Main class, so I'm not sure why it doesn't recognize it.
It's because self.a is assigned within a conditional if statement, 2 of them in fact. So if both of those conditions aren't met, self.a is never assigned, and an error is thrown when you try to use it to calculate self.c
Try setting self.a and self.b to a default value (maybe 0) before the conditional, then if the condition isn't met, it will at least have a default value.
-- EDIT --
actually, for this case you probably don't want to just assign them to 0. But you need to make sure they are defined somehow before you try to use them.

Autogrowing list in Python?

I need a list-like object that will "autogrow" whenever a slot number greater or equal to its length is accessed, filling up all the newly created slots with some pre-specified default value. E.g.:
# hypothetical DefaultList class
x = DefaultList(list('abc'), default='*')
x[6] = 'g'
print x[2], x[4], x[6], x[8] # should print 'c * g *'
Thanks!
PS. I know it is not hard to implement a class like this, but I avoid wheel-reinvention as much as possible, especially if a particularly efficient/well-designed wheel already exists.
PS2. A dict (or a collections.defaultdict) is not an acceptable implementation of the desired data structure. For why, see here: http://groups.google.com/group/comp.lang.python/msg/bcf360dfe8e868d1?hl=en
class DefaultList(list):
def __init__(self,*args,**kwargs):
list.__init__(self,*args)
self.default=kwargs.get('default',None)
def __getitem__(self,key):
# retrieving an item does not expand the list
if isinstance(key,slice):
return [self[elt] for elt in range(key.start,key.stop,key.step)]
else:
try:
return list.__getitem__(self,key)
except IndexError:
return self.default
def __setitem__(self,key,value):
# setting an item may expand the list
try:
list.__setitem__(self,key,value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
x = DefaultList(list('abc'), default='*')
print(x)
# ['a', 'b', 'c']
x[6] = 'g'
print(x)
# ['a', 'b', 'c', '*', '*', '*', 'g']
print x[2], x[4], x[6], x[8] # should print 'c * g *'
# c * g *
print(x[2:9:2])
# ['c', '*', 'g', '*']
I would use a sparse data structure (1xn matrix).
You could always make a function that handles this:
def fillList(item, slot, myList):
length = len(myList)
if slot > length:
augmentation = [item for x in range(slot-length)]
myList.extend(augmentation)
else:
myList[slot] = item
Which while not a data structure, does accomplish what you want.
Using the idea of wheaties's solution and making a prettier interface:
You could inherit from list and overwrite the list 'getitem(index)' method which maps to [index] in your class. It should be something like this:
class GrowingList(list):
def __getitem__(self, index):
length = len(self)
# list is 0 indexed
if index >= length:
tail = [ self.default_value for x in range(index - length + 1)]
self.extend(tail)
return super(self.__class__, self).__getitem__(index)
This same code can be used if you don't extend the list, but just return some default value on invalid index
This preserves the whole list interface.
(This isn't a new answer; just a comment on unutbu's. It should really be possible to post stuff like this in comments; it isn't, so I have to post it as an answer.)
CombineListClasses and CombineListClasses2 inherit from two classes that both inherit from list. The behavior and doctests are straightforward, but break badly in the original version.
This is all standard practice in Python's data model; you almost never should be calling a base class method directly rather than via super.
class DefaultList(list):
"""
>>> x = DefaultList('abc', default='*')
>>> x
['a', 'b', 'c']
>>> x[6] = 'g'
>>> x
['a', 'b', 'c', '*', '*', '*', 'g']
>>> x[2], x[4], x[6], x[8] # should print 'c * g *'
('c', '*', 'g', '*')
>>> x[2:9:2]
['c', '*', 'g', '*']
>>> x = DefaultList()
>>> x[1] = 'a'
>>> x
[None, 'a']
>>> x = DefaultList(sequence=[1,2,3], default=5)
>>> x
[1, 2, 3]
>>> x[10]
5
"""
def __init__(self, *args, **kwargs):
if 'default' in kwargs:
self.default = kwargs['default']
del kwargs['default']
else:
self.default = None
super(DefaultList, self).__init__(*args, **kwargs)
def __getitem__(self, key):
# retrieving an item does not expand the list
if isinstance(key, slice):
return [self[elt] for elt in range(key.start, key.stop, key.step)]
else:
try:
return super(DefaultList, self).__getitem__(key)
except IndexError:
return self.default
def __setitem__(self, key, value):
# setting an item may expand the list
try:
super(DefaultList, self).__setitem__(key, value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
# Another class that derives from list:
class AddMethodToList(list):
def __init__(self, *args, **kwargs):
self.value = kwargs['value']
del kwargs['value']
super(AddMethodToList, self).__init__(*args, **kwargs)
def new_method(self):
return self.value
# Derive from both classes.
class CombineListClasses(AddMethodToList, DefaultList):
"""
>>> a = CombineListClasses(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
# Derive from both classes in reverse, reversing the call chain order.
class CombineListClasses2(DefaultList, AddMethodToList):
"""
>>> a = CombineListClasses2(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
if __name__ == '__main__':
import doctest
print doctest.testmod()
Note that in Python 3, this is supported by the language directly:
class DefaultList(list):
def __init__(self, *args, default=None, **kwargs):
self.default = default
super(self).__init__(*args, **kwargs)
but that's not supported in Python 2. http://www.python.org/dev/peps/pep-3102

Categories