Manipulating instances of a class change the whole class - python-2.7

So I'm struggling. I've seen quite a few posts on this topic, but after a couple hours struggling with the problem, I can't figure this out. Here are a few snippets of code I have. I'd like to be able to change one instance of the class without the other being changed as well, and I'm coming up short
class voter:
def __init__(self, iPositon, jPosition, affiliation):
self.affiliation = affiliation
self.iPositon = iPositon
self.jPosition = jPosition
class district:
def __init__(self, *listOfVotersPassed):
# super(district, self).__init__()
self.listOfVoters = []
for x in listOfVotersPassed:
self.listOfVoters.append(x)
class city:
def __init__(self, *listOfDistrictsPassed):
self.listOfDistricts = []
for x in listOfDistrictsPassed:
self.listOfDistricts.append(x)
def main():
# I have a list of class district that I pass to city()
startCity = city(*startCityList)
solutionCity = city(*solutionCityList)
print solutionCity.listOfDistricts[0].listOfVoters[0].affiliation # Democrat
print startCity.listOfDistricts[0].listOfVoters[0].affiliation # Democrat
solutionCity.listOfDistricts[0].listOfVoters[0].affiliation = "Republican"
print solutionCity.listOfDistricts[0].listOfVoters[0].affiliation # Republican
print startCity.listOfDistricts[0].listOfVoters[0].affiliation # Republican
if __name__ == "__main__":
main()
edit.
So I was asked about how I created the instance(s) of city. I have a file I'm reading in, each has either R or D in each line.
file = open(fileName)
# import contents
startVoterList = []
solutionVoterList = []
for line in file:
for house in line:
if " " in house:
pass
elif "\n" in house:
pass
else:
startVoterList.append(house)
solutionVoterList.append(house)
I also updated the classes, now each is in the following format. No changes besides the "(object)" after each class className.
class voter(object):
def __init__(self, iPositon, jPosition, affiliation):
self.affiliation = affiliation
self.iPositon = iPositon
self.jPosition = jPosition

Related

Creating a Class Attribute dependent on Another Attribute

I am currently working on a small RPG in Pygame to get used to object oriented coding.
When looking into how to auto-update a property I came across the following:
class P:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
I tried applying it to my code but I get the following error:
File "weapons.py", line 13, in __init__
self.name = '{} {}'.format(ammo, self.raw_name)
TypeError: name() takes exactly 3 arguments (2 given)
I understand what the error is but I don't get how to solve it since I need both the raw_name and the ammo attributes to auto-update my Arrow instance's name.
My class is as such:
class Projectile(Item):
def __init__(self, name, value, image, x, y, speed, dmg, dmg_modif, ammo):
super(Projectile, self).__init__(name, value, image, x, y)
self.dest = (self.rect[0],self.rect[1])
self.speed = speed
self.dmg_modif = dmg_modif
self.dmg = dmg
self.orientation = 0
self.ammo = ammo
#property
def name(self):
return self.___name
#name.setter
def name(self, raw_name, ammo):
if '{} {}'.format(raw_name,ammo) != self.___name:
self.___name = '{} {}'.format(raw_name,ammo)
The child class which returns the error is:
class Arrow(Projectile):
def __init__(self, ammo): #name, value, image, x, y, dmg
self.raw_name = 'Arrows'
self.name = '{} {}'.format(ammo, self.raw_name)
self.value = 5
self.image = variables.quiver_img
self.speed = 4
self.dmg = 2
self.dmg_modif = 1
super(Arrow, self).__init__(self.name, self.value, self.image, 200, 150, self.speed, self.dmg, self.dmg_modif, ammo)
And the parent classes are, Item and MySprite:
class Item(MySprite):
def __init__(self, name, value, image, x, y):
# Call the parent class (Sprite) constructor
super(Item, self).__init__(image, x, y)
self.name = name
self.value = value
self.inv_pos = -1
and
class MySprite(pygame.sprite.Sprite):
def __init__(self,image,x,y):
# Call the parent class (Sprite) constructor
super(MySprite, self).__init__()
self.image = image
self.rect = self.image.get_rect().move(x, y) #initial placement
self.top_cp = (self.rect[0]+self.rect[2]/2,self.rect[1])
self.bot_cp = (self.top_cp[0],self.rect[1]+self.rect[3])
self.left_cp = (self.rect[0],self.rect[1]+self.rect[3]/2)
self.right_cp = (self.left_cp[0]+self.rect[2],self.left_cp[1])
self.center = self.rect.center
self.pos = self.rect.topleft
self.blit_order = 1
self.level = variables.current_level #Level(1)#level to which sprite belongs
Any help would be welcome !
Please provide a minimal example next time.
Your code looks a little complicated. OK, the first problem is that you can't pass multiple arguments to a setter. You can either pass a tuple or just use a traditional setter method def set_name(self, name, ammo):.
Another problem is that you use the ___name attribute before it has been set, for example in the second line of the Arrows __init__ method.
Private attributes should have one underscore not three (that's just a convention to warn other programmers). If you have two or more underscores than the name gets mangled.
Also, it looks to me like you change the name in the setter only if the new name is equal to the old name (kinda pointless ;)). What do you actually want to do there? Maybe you don't need properties at all.
Here's a fixed (minimal) version of your code:
import pygame
class MySprite(pygame.sprite.Sprite):
def __init__(self):
super(MySprite, self).__init__()
class Item(MySprite):
def __init__(self, name):
super(Item, self).__init__()
self.name = name
class Projectile(Item):
def __init__(self, name):
super(Projectile, self).__init__(name)
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name + ' foo'
class Arrow(Projectile):
def __init__(self):
super(Arrow, self).__init__(name='Arrows')
arrow = Arrow()
print(arrow.name)
arrow.name = 'New name'
print(arrow.name)
So I eventually managed to solve my problem:
class Projectile(object):
def __init__(self, raw_name, ammo):
self.raw_name = raw_name
self.name = self.raw_name
self.ammo = ammo
#property
def ammo(self):
return self._ammo
#ammo.setter
def ammo(self, ammo):
self.name = str(ammo) + self.raw_name
self._ammo = ammo
class Arrow(Projectile):
def __init__(self):
self.raw_name = ' Arrows'
self.name = self.raw_name
super(Arrow, self).__init__(self.raw_name, ammo
= 10)
arrow = Arrow()
print arrow.ammo
print arrow.name
arrow.ammo = 15
print arrow.ammo
print arrow.name
gives:
>>>10
>>>10 Arrows
>>>15
>>>15 Arrows

How do I fix the part in __str__ so it would display the name of the instance within the class? Python 2X

How do I change the code so that it would return a is a square with side:5 and area:25 ?? when I call print a?
class Square():
def __init__(self, length):
self.length = length
def area(self):
return self.length ** 2
def __str__(self): #this is the part I don't know how to write
return "is a square with side:%s and area:%s." % (self.length, self.area())
a = Square(5)
print a
I guess you want to get the name of the variable that you asign with your class, right??
I don't know how to do that or is that is posible (or usefull in any way) because you can have have multiples variables pointing to the same object
for example in this
a=SomeObject()
b=a
c=a
d=c
and in this case how you will know which one you are using if all of them point to the same object?
A alternative is give your class a extra paremetrer with the name that you wish for you instance
class Square():
def __init__(self, length,name=""):
self.length = length
self.name = name
def area(self):
return self.length ** 2
def __str__(self): #this is the part I don't know how to write
return "%s is a square with side:%s and area:%s." % (self.name, self.length, self.area())
a = Square(5,'MySquare')
print a # MySquare is a square with side:5 and area:25.

How to call a custom method with arguments in the models.py from views.py

I have this example:
class MyModel(models.Model):
# Some fields...
price = models.FloatField()
def calculate(self, number):
return self.price * number
In the views:
def whatever(request, any_number):
m = MyModel.objects.all()
c = m.calculate(any_number)
# More code...
It's a really easy example because I want to do something similar, so how can I do this?
Thank you!
You need to do it in a for loop, since m is an array of objects:
for item in m:
result = item.calculate(any_number)
# do some stuff with the result

why my function doesn't remove objects from final_list?

hey guys:)im sorry for all the code but i feel its necessary for u to see everything..
i tried everything... i hidden prints in the code, debugging for ten times, triple checked the built in methods, and still, the .crawl() method dosnt remove any object from the final_list.
the object of my assignment is to built two classes:
Web_page : holds data of a web page.(the pages come in the form of html files saved in a folder on my desktop. Crawler: compare between pages and hold a list of the uniqe pages---> final_list
import re
import os
def remove_html_tags(s):
tag = False
quote = False
out = ""
for c in s:
if c == '<' and not quote:
tag = True
elif c == '>' and not quote:
tag = False
elif (c == '"' or c == "'") and tag:
quote = not quote
elif not tag:
out = out + c
return out
def lev(s1, s2):
return lev_iter(s1, s2, dict())
def lev_iter(s1, s2, mem):
(i,j) = (len(s1), len(s2))
if (i,j) in mem:
return mem[(i,j)]
s1_low = s1.lower()
s2_low = s2.lower()
if len(s1_low) == 0 or len(s2_low) == 0:
return max(len(s1_low), len(s2_low))
d1 = lev_iter(s1_low[:-1], s2_low, mem) + 1
d2 = lev_iter(s1_low, s2_low[:-1], mem) + 1
last = 0 if s1_low[-1] == s2_low[-1] else 1
d3 = lev_iter(s1_low[:-1], s2_low[:-1], mem) + last
result = min(d1, d2, d3)
mem[(i,j)] = result
return result
def merge_spaces(content):
return re.sub('\s+', ' ', content).strip()
""" A Class that holds data on a Web page """
class WebPage:
def __init__(self, filename):
self.filename = filename
def process(self):
f = open(self.filename,'r')
LINE_lst = f.readlines()
self.info = {}
for i in range(len(LINE_lst)):
LINE_lst[i] = LINE_lst[i].strip(' \n\t')
LINE_lst[i] = remove_html_tags(LINE_lst[i])
lines = LINE_lst[:]
for line in lines:
if len(line) == 0:
LINE_lst.remove(line)
self.body = ' '.join(LINE_lst[1:])
self.title = LINE_lst[0]
f.close()
def __str__(self):
return self.title + '\n' + self.body
def __repr__(self):
return self.title
def __eq__(self,other):
n = lev(self.body,other.body)
k = len(self.body)
m = len(other.body)
return float(n)/max(k,m) <= 0.15
def __lt__(self,other):
return self.title < other.title
""" A Class that crawls the web """
class Crawler:
def __init__(self, directory):
self.folder = directory
def crawl(self):
pages = [f for f in os.listdir(self.folder) if f.endswith('.html')]
final_list = []
for i in range(len(pages)):
pages[i] = WebPage(self.folder + '\\' + pages[i])
pages[i].process()
for k in range(len(final_list)+1):
if k == len(final_list):
final_list.append(pages[i])
elif pages[i] == final_list[k]:
if pages[i] < final_list[k]:
final_list.append(pages[i])
final_list.remove(final_list[k])
break
print final_list
self.pages = final_list
everything works fine besides this freaking line final_list.remove(final_list[k]). help please? whats wrong here?
I'm not sure why your code doesn't work, it's difficult to test it because I don't know what kind of input should end up calling remove().
I suggest following these steps:
Make sure that remove() is called at some point.
remove() relies on your __eq__() method to find the item to remove, so make sure that __eq__() isn't the culprit.
As a side note, you will probably want to replace this:
self.folder + '\\' + pages[i]
with:
import os.path
# ...
os.path.join(self.folder, page[i])
This simple change should make your script work on all operating systems, rather than on Windows only. (GNU/Linux, Mac OS and other Unix-like OS use “/” as path separator.)
Please also consider replacing loops of this form:
for i in range(len(sequence)):
# Do something with sequence[i]
with:
for item in sequence:
# Do something with item
If you need the item index, use enumerate():
for i, item in enumerate(sequence):

How can I sort my Django model using complex data from a second model?

I have 3 django models (simplified for this example):
class Fighter (models.Model):
name = models.CharField(max_length=100)
weight_class = models.ForeignKey(WeightClass, related_name="fighter_weight_class")
class Bout (models.Model):
fighter_1 = models.ForeignKey(Fighter, related_name="bout_fighter_1")
fighter_2 = models.ForeignKey(Fighter, related_name="bout_fighter_2")
winner = models.ForeignKey(Fighter, related_name="bout_winner", blank=True,
null=True, help_text='Leave blank for draw.')
date = models.DateField()
cancelled = models.BooleanField()
class WeightClass (models.Model):
name = models.CharField(max_length=100)
I would like to be able to list the Fighters by their stats. I created these
functions (which work) to calculate their wins, losses, and draws:
def get_bouts_with_fighter (fighter):
return Bout.objects.filter(fighter_1 = fighter) | \
Bout.objects.filter(fighter_2 = fighter)
def get_fighter_wins (fighter):
return Bout.objects.filter(winner=fighter)
def get_bouts_fought_not_won (fighter):
return get_bouts_with_fighter(fighter) \
.exclude(cancelled=True) \
.exclude(date__gt = datetime.datetime.now())
def get_fighter_losses (fighter):
return get_bouts_fought_not_won(fighter)\
.exclude(winner=fighter)\
.exclude(winner=None)
def get_fighter_draws (fighter):
return get_bouts_fought_not_won(fighter)\
.filter(winner=None)
But now, when comes to sorting the output for the view, I'm having two problems:
The "sort" is neither correct nor consistent.
During the sort, I'm losing the fighter's name
Here is the current code (the output is correct if no sorting is performed):
def list(request, sort=None):
class WeightGroup:
"""
Internal class to keep data organized by weight class.
"""
weight_class = None
fighters = None
def win_sort (self):
wins = []
winners = []
for fighter in self.fighters:
wins.append((fighter.id, get_fighter_wins(fighter)),)
for win in (sorted(wins, key=lambda f: f[1], reverse=True)):
winners.append(Fighter.objects.filter(id=win[0])) #build winner list by score
self.fighters = winners
def loss_sort (self):
#TO DO:
pass
def draw_sort (self):
#TO DO:
pass
title = 'Fighters'
weight_groups = []
for weight_class in WeightClass.objects.all():
weight_group = WeightGroup()
weight_group.weight_class = weight_class
weight_group.fighters = Fighter.objects.filter(weight_class=weight_class)
weight_groups.append(weight_group)
if sort in ['wins', 'losses', 'draws']:
title += ', sorted by ' + sort
for weight_group in weight_groups:
if (sort == 'wins'): weight_group.win_sort()
if (sort == 'losses'): weight_group.loss_sort()
if (sort == 'draws'): weight_group.draw_sort()
return render_to_response ('fighters/list.html',{
'title': title,
'weight_groups':weight_groups
})
Any suggestions how I can get this to work will be appreciated!
I've managed to solve both problems:
I was appending the collection of wins, not the "length" of that list.
I needed to change this:
wins.append((fighter.id, get_fighter_wins(fighter)),)
to:
wins.append((fighter.id, len(get_fighter_wins(fighter))),)
I was constructing a list of querysets, not a list of objects.
I needed to change this:
winners.append(Fighter.objects.filter(id=win[0]))
to:
winners.append(Fighter.objects.get(id=win[0]))
(Cheers to my friend Attila who explained the second issue to me.)
BTW, my final code doesn't use three separate functions. Instead I did this:
.
.
.
class WeightGroup:
.
.
.
def sort (self, sort_function):
scores = []
scorers = []
for fighter in self.fighters:
scores.append((fighter.id, len(sort_function(fighter))),)
for score in (sorted(scores, key=lambda f: f[1], reverse=True)):
scorers.append(Fighter.objects.get(id=score[0]))
self.fighters = scorers
.
.
.
if sort in ['wins', 'losses', 'draws']:
title += ', sorted by ' + sort
for weight_group in weight_groups:
if (sort == 'wins'): weight_group.sort(get_fighter_wins)
if (sort == 'losses'): weight_group.sort(get_fighter_losses)
if (sort == 'draws'): weight_group.sort(get_fighter_draws)
.
.
.