I have 2 almost identical classes. How can they be solved by inheritance?
1 class (maybe parent):
class Heart():
""" třída zdraví """
def __init__(self):
self.x = []
self.y = []
self.s = []
self.h = 0
def vykresli(self):
#vykreslení srdce
heart_big = pygame.image.load(os.path.join(DIR, "images/heart_big.png"))
heart_small = pygame.image.load(os.path.join(DIR, "images/heart_small.png"))
if random.randrange(0, 500) == 1 and len(self.x) <= 0:
self.x.append(random.randrange(50, 970))
self.y.append(0)
self.s.append(random.randrange(1, 4))
for x in range(len(self.x)):
if self.h % 9 == 0:
SCREEN.blit(heart_big, (self.x[x], self.y[x]))
else:
SCREEN.blit(heart_small, (self.x[x], self.y[x]))
self.y[x] += self.s[x]
self.h += 1
if self.h > 100: self.h = 0
for y in range(len(self.x)):
if self.y[y] > OKNO_Y:
del self.y[y]
del self.x[y]
del self.s[y]
break
2 class (maybe child)
class MissileStack():
"""definuje třídu nabití zásobníku střelami"""
def __init__(self):
self.x = []
self.y = []
self.s = []
self.h = 0
self.rotate = 0
def vykresli(self):
#vykreslení dodávky zásoby střel
misilestack = pygame.image.load(os.path.join(DIR, "images/misilestack.png"))
if random.randrange(0, 50) == 1 and len(self.x) <= 0:
self.x.append(random.randrange(50, 970))
self.y.append(0)
self.s.append(random.randrange(1, 4))
#self.missile = pygame.transform.rotate(pygame.image.load(os.path.join(DIR, "images/missile.png")),180)
for x in range(len(self.x)):
misilestackrotate = pygame.transform.rotate(misilestack, self.rotate)
SCREEN.blit(misilestackrotate, (self.x[x], self.y[x]))
self.y[x] += self.s[x]
self.h += 1
self.rotate += 1
if self.rotate >= 360: self.rotate = 0
if self.h > 100: self.h = 0
for y in range(len(self.x)):
if self.y[y] > OKNO_Y:
del self.y[y]
del self.x[y]
del self.s[y]
break
Both classes do practically the same thing. They just use a different image and a different random generation. I've read a lot of tutorials on class inheritance, but I can't use inheritance in practice.
Ignoring the fact that your code is doing a bunch of questionable things, You can create a base class that factors out the common operations.
All the class and method names are completely made up here, since I don't know what you are really trying to do and my command of your native language is non-existent:
class Sprite:
def __init__(self, r):
self.x = []
self.y = []
self.s = []
self.r = r
def vykresli(self):
if random.randrange(0, self.r) == 1 and len(self.x) <= 0:
self.x.append(random.randrange(50, 970))
self.y.append(0)
self.s.append(random.randrange(1, 4))
self.pre_loop()
for x in range(len(self.x)):
self.do_blit((self.x[x], self.y[x]))
self.y[x] += self.s[x]
self.post_loop()
for y in range(len(self.x)):
if self.y[y] > OKNO_Y:
del self.y[y]
del self.x[y]
del self.s[y]
break
def pre_loop(self):
pass
def post_loop(self):
pass
The deltas now make for two small specific classes:
class Heart(Sprite):
def __init__(self):
super().__init__(500)
self.h = 0
#vykreslení srdce
self.heart_big = pygame.image.load(os.path.join(DIR, "images/heart_big.png"))
self.heart_small = pygame.image.load(os.path.join(DIR, "images/heart_small.png"))
def do_blit(self, coord):
if self.h % 9 == 0:
SCREEN.blit(self.heart_big, coord)
else:
SCREEN.blit(self.heart_small, coord)
def post_loop(self):
super().post_loop()
self.h += 1
if self.h > 100:
self.h = 0
and
class Missiles(Sprite):
def __init__(self):
super().__init__(50)
#vykreslení dodávky zásoby střel
misilestack = pygame.image.load(os.path.join(DIR, "images/misilestack.png"))
def pre_loop(self):
super().pre_loop()
self.misilestackrotate = pygame.transform.rotate(misilestack, self.rotate)
def do_blit(self, coord):
SCREEN.blit(self.misilestackrotate, coord)
def post_loop(self):
super().post_loop()
self.rotate += 1
if self.rotate >= 360:
self.rotate = 0
I've added hooks called pre_loop, do_blit and post_loop to allow you to do class-specific operations for each type of Sprite. You can experiment with abstract base classes if you want to do things like enforce an implementation of do_blit.
Related
maybe a bit trivial question but i'm having trouble calling the object.
How can I call an object from this class and possibly call the add method correctly?
sample code:
class MyMatrix:
height = 0
width = 0
data = tuple()
def __init__(self, data):
self.height = len(data)
self.width = len(data[0])
self.data = data
def add(mat1, mat2):
if mat1.height != mat2.height or mat1.width != mat2.width:
print("The matrices are not the same size!")
return
rows = []
for i in range(len(mat1.data)):
row = []
for j in range(len(mat1.data[0])):
row.append(mat1[i][j] + mat2[i][j])
rows.append(tuple(row))
return MyMatrix(tuple(rows))
Thank you in advance for every answer.
You can call the methods like follows; for your code to work, however, you need to implement getitem (so that you can do matrix[1][2] e.g.).
class MyMatrix:
height = 0
width = 0
data = tuple()
def __init__(self, data):
self.height = len(data)
self.width = len(data[0])
self.data = data
def __getitem__(self, item):
return self.data.__getitem__(item)
def add(mat1, mat2):
if mat1.height != mat2.height or mat1.width != mat2.width:
print("The matrices are not the same size!")
return
rows = []
for i in range(len(mat1.data)):
row = []
for j in range(len(mat1.data[0])):
row.append(mat1[i][j] + mat2[i][j])
rows.append(tuple(row))
return MyMatrix(tuple(rows))
m1 = MyMatrix([[1,2,3]])
m2 = MyMatrix([[3,2,1]])
m12 = m1.add(m2)
print(m12.data)
Here is my code:
class Prizes(object):
def __init__(self, purchases, n, d):
self.p = purchases
self.n = n
self.d = d
self.x = 1
def __iter__(self):
return self
def __next__(self):
print(self.x)
if self.x % self.n == 0 and self.p[self.x - 1] % self.d == 0:
self.x = self.x + 1
return self.x - 1
elif self.x > len(self.p):
raise StopIteration
self.x = self.x + 1
def superPrize(purchases, n, d):
return list(Prizes(purchases, n, d))
An example of usage:
superPrize([12, 43, 13, 465, 1, 13], 2, 3)
The output should be:
[4]
But actual output is:
[None, None, None, 4, None, None].
Why does it happen?
Your problem is your implementation of __next__. When Python calls __next__, it will always expect a return value. However, in your case, it looks like you may not always have a return value each call. Thus, Python uses the default return value of a function - None:
You need some way to keep program control inside of __next__ until you have an actually return value. This can be done using a while-loop:
def __next__(self):
while True:
if self.x % self.n == 0 and self.p[self.x - 1] % self.d == 0:
self.x = self.x + 1
return self.x - 1
elif self.x > len(self.p):
raise StopIteration
self.x = self.x + 1
Wrap it with while so that your method doesn't return a value until you found one:
def __next__(self):
while True:
if self.x % self.n == 0 and self.p[self.x - 1] % self.d == 0:
self.x = self.x + 1
return self.x - 1
elif self.x > len(self.p):
raise StopIteration
self.x = self.x + 1
Things working with iterators call __next__ expecting it to return a value, but the method returns a value only under a condition, otherwise it reaches the end of the method and it returns None.
I'm making a program that will go through at least 1,016,064 gear permutations on Diablo 3. I've been experimenting with different theoretical implementations and I decided that I wanted to use classes to represent each permutation rather than having to deal with massive and convoluted dictionaries. With this method I can store an instance and then replace it when a new permutation is superior to the former.
In any case it takes my computer (i7-3632QM) about 40 seconds go through all of the permutations just doing about 30 flops per permutation, and I cant even imagine how long it'll take if it has to define all 50 methods each time a class is instantiated. Anyway, this is what I think it'll look like:
class perm:
def __init__(self, *args):
self.x = 10
self.y = 5
self.z = 100
for item in args:
if hasattr(self, item):
getattr(self, item)()
self.val_1 = self.x * 2
self.val_2 = self.y * 5
self.val_3 = self.z/(self.z+300)
def head_1(self):
self.x += 5
self.z + 200
def head_2(self):
self.x += 10
self.y += 10
def feet_1(self):
self.y += 5
self.z += 250
def feet_2(self):
self.x += 10
self.z += 500
current_best = perm('head_1','feet_2')
It seems like the correct way to do this is to make objects for each of the gear options you have, then a function that calculates them all.
import itertools
class Gear(object):
def __init__(self, *args, **kwargs):
# I have no idea what Gear should do...
class Headpiece(Gear):
...
class Legs(Gear):
...
# etc
def calculate_perm(gear_tuple):
result = do_some_calculation_over(gear_tuple)
return result
best = max(itertools.permutations(all_your_gear), key=calculate_perm)
You could even create one class that's analogous to your perm, though I'd give it a more descriptive name:
class EquipmentSet(object):
slots = ['head', 'legs', ... ]
def __init__(self, head=None, legs=None, ...)
self.head = head
self.legs = legs
...
self.equipment = [getattr(self, item) for item in self.slots]
#property
def x(self)
return sum(item.x for item in self.equipment)
# similar for y and z
#property
def val_1(self):
return self.x * 2
# similar for val_2, val_3
# implement dunder rich comparison methods?
result = max(EquipmentSet(*gearset) for \
gearset in itertools.permutations(all_your_gear))
Strings are just as a example. These lists should contain Gear class, which instances knows what type of 'bonuses' gear gives.
import itertools
headpieces = ['headpiece1', 'headpiece2', 'headpiece3']
armors = ['armor1', 'armor2']
weapons = ['weapon1', 'weapon2']
print list(itertools.product(headpieces, armors, weapons))
# result:
[('headpiece1', 'armor1', 'weapon1'),
('headpiece1', 'armor1', 'weapon2'),
('headpiece1', 'armor2', 'weapon1'),
('headpiece1', 'armor2', 'weapon2'),
('headpiece2', 'armor1', 'weapon1'),
('headpiece2', 'armor1', 'weapon2'),
('headpiece2', 'armor2', 'weapon1'),
('headpiece2', 'armor2', 'weapon2'),
('headpiece3', 'armor1', 'weapon1'),
('headpiece3', 'armor1', 'weapon2'),
('headpiece3', 'armor2', 'weapon1'),
('headpiece3', 'armor2', 'weapon2')]
This code gives you all possible gears in lazy way (without passing it to list() it returns generator), is optimized (itertools are implemented in C) as is elegant. Note that in each element there is only one headpiece / weapon / armor. May be generalized to additional piece of gears.
After that you'll just have to write some kind of aggregator which takes input gear and returns 'score'.
Well I decided to use itertools, a module I have no experience with (but that will change after this!), and I've already half made the script making a test. It works so I might as well finish it even if it isn't the most efficient way, although I'm open to suggestions...
import time, itertools
class Barb:
def __init__(_, args):
_.elements = ['arcane','cold','fire','lightning','poison','physical']
_.strength = 5460 # max ancient str
_.vitality = 140
_.armor = 10188
_.all_res = 250
_.resistances = {element:7.7 for element in _.elements}
_.dodge = 0
_.armor_bonus_percent = .25
_.all_res_bonus_percent = 0
_.life_bonus_percent = .25
_.elemental_damage_reduction = 1
_.regen = 10730
_.life_on_hit = 8035
_.life_per_fury_spent = 0
_.life_percent_per_second_regen = 0
_.damage_mod = 1
_.cc = .05
_.cd = 2.8
_.ias = .25
_.attacks_per_second = 1.69
_.ww_damage_percent = 0
_.dibs = 0
_.cdr = 1
_.elemental_damage_bonus = .2
_.bastions = False
# apply gear bonuses
for arg in args:
getattr(_, arg)()
def helm_1(_):
_.cc += .06
_.ww_damage_percent += .15
_.life_bonus_percent += .23
_.resistances['arcane'] += 210
def helm_2(_):
_.cc += .06
_.vitality += 1000
_.life_bonus_percent += .23
_.resistances['arcane'] += 210
def torso_1(_):
_.vitality += 650
_.life_bonus_percent += .15
_.resistances['fire'] += 210
def torso_2(_):
_.all_res += 120
_.vitality += 650
def pants_1(_):
_.vitality += 650
_.armor += 700
_.resistances['physical'] += 210
def pants_2(_):
_.vitality += 650
_.all_res += 120
def bastions_1(_):#ring set
_.strength += 1000
_.cc += .12
_.cd += 1
_.resistances['physical'] += 210
_.resistances['poison'] += 210
_.bastions = True
def bastions_2(_):
_.strength += 500
_.cc += .12
_.cd += 1
_.cdr *= .92
_.resistances['physical'] += 210
_.resistances['poison'] += 210
_.bastions = True
def bk_1(_): # (str, dmg, cdr) + (str, cdr, vit)
_.strength += 2000
_.damage_mod *= 1.05
_.cdr *= .9 * .9
_.vitality += 1000
def bk_2(_): # (str, dmg, cdr) + (str, dmg, loh)
_.strength += 2000
_.damage_mod *= 1.1
_.cdr *= .9
_.life_on_hit += 18000
def best_score():
def available_items(prefix):
#automagically check barb for possible item variants of the item slot 'prefix'
# so more can be added at a later time
r = []
i = 1
while True:
name = '%s_%s'%(prefix, i)
if hasattr(Barb, name):
r.append(name)
else: return r
i += 1
gear_slots = [
'helm','torso','pants','bastions','bk']
helms, torso, pants, bastions, bk = [available_items(i) for i in gear_slots]
gears = itertools.product(helms, torso, pants, bastions, bk)
bestOffense = {'gear':[],
'health':0,
'mitigation':0,
'damage':0}
elapsed = time.time()
while True:
try:
args = next(gears)
barb = Barb(args)
armor = barb.armor * (1 + barb.armor_bonus_percent)
damage_reduction = armor / (armor + 3500)
resistances = {res:(barb.resistances[res] + barb.all_res) \
* (1 + barb.all_res_bonus_percent) for \
res in barb.resistances}
elemental_dr = {res:resistances[res]/(resistances[res] + 350) \
for res in resistances}
health = barb.vitality * 100 * (1 + barb.life_bonus_percent)
aps = barb.attacks_per_second * (1 + barb.ias)
damage_mod = barb.damage_mod * (1 + (barb.strength / 100))
damage_mod *= (1 - barb.cc) + (barb.cc * barb.cd)
damage_mod *= 2.25 if barb.bastions else 1
damage_mod *= 1 + barb.elemental_damage_bonus
dust_devils = 25 * damage_mod * (1 + barb.dibs + barb.ww_damage_percent)
min_elemental_dr = elemental_dr[min(elemental_dr)]
mitigation = 1 - ((1-damage_reduction) * (1-min_elemental_dr))
if dust_devils > bestOffense['damage']:
bestOffense = {'gear':args,
'health':health,
'mitigation':mitigation,
'damage':dust_devils}
except: return bestOffense, time.time() - elapsed
Python static methods will stop the interpreter making a new function in memory for every instance of a class. You can only use it for functions that don't need an instance to operate on though, i.e. functions that don't use self.
I'm trying to make a Python module like this:
class square:
def _init_(self):
self._length = 0
self._perimeter = 0
self._area = 0
def setLength(self, length):
self._length = float(length)
self._perimeter = 0
self._area = 0
def getLength(self):
return self._length
def getPerimeter(self):
if self._perimeter == 0:
self._perimeter = float(self._length * 4)
return self._perimeter
def getArea(self):
if self._area == 0:
self._area = float(self._length * self._length)
return self._area
class rectangle:
def _init_(self):
self._length = 0
self._width = 0
self._perimeter = 0
self._area = 0
def setLength(self, length):
self._length = float(length)
self._perimeter = 0
self._area = 0
def getLength(self):
return self._length
def setWidth(self, width):
self._width = float(width)
self._perimeter = 0
self._area = 0
def getWidth(self):
return self._width
def getPerimeter(self):
if self._perimeter == 0:
perim1 = float(self._length * 2)
perim2 = float(self._width * 2)
self._perimeter = float(perim1 + perim2)
return self._perimeter
def getArea(self):
if self._area == 0:
self._area = float(self._length * self._width)
return self._area
class circle:
def _init_(self):
self._radius = 0
self._diameter = 0
self._circumference = 0
self._pi = 3.14159265
def setRadius(self, radius):
self._radius = float(radius)
self._diameter = float(self._radius * 2)
self._circumference = 0
def setDiameter(self, diameter):
self._diameter = float(diameter)
self._radius = float(self._diameter / 2)
self._circumference = 0
def getRadius(self):
return self._radius
def getDiameter(self):
return self._diameter
def getPi(self):
return self._pi
def getCircumference(self):
if self._circumference == 0:
self._circumference = float(self._diameter * self._pi)
return self._circumference
class triangle:
def _init_(self):
self._side = []
self._side[0] = 0
self._side[1] = 0
self._side[2] = 0
self._side[3] = 0
self._angle = []
self._angle[0] = 0
self._angle[1] = 0
self._angle[2] = 0
self._angle[3] = 0
self._perimeter = 0
self._area = 0
def setSide(self, side, length):
self._side[side] = float(length)
def getSide(self, side):
return self._side[side]
def getPerimeter(self):
if self._perimeter == 0:
self._perimeter = side[1] + side[2] + side[3]
return self._perimeter
def setAngle(self, angle, measure):
self._angle[angle] = float(measure)
def getAngle(self, angle):
if self._angle[angle] == 0:
if angle == 1:
angle1 = self._angle[2]
angle2 = self._angle[3]
elif angle == 2:
angle1 = self._angle[1]
angle2 = self._angle[3]
elif angle == 3:
angle1 = self._angle[1]
angle2 = self._angle[2]
anglet = angle1 + angle2
angler = float(180) - anglet
self._angle[angle] = angler
return self.angle[angle]
It's part of a package named Mathworks. The calling code is this:
import mathworks as mw
mycircle = mw.shapes.circle()
mycircle.setDiameter(5)
circum = mycircle.getCircumference()
print circim
When I try two run the second module, I get this:
Traceback (most recent call last):
File "<string>", line 254, in run_nodebug
File "<module1>", line 21, in <module>
File "<module1>", line 17, in main
File "C:\Python27\lib\site-packages\mathworks\shapes.py", line 70, in getCircumference
self._circumference = float(self._diameter * self._pi)
AttributeError: circle instance has no attribute '_pi'
What's wrong? It works if I replace self._pi with 3.14159265, but I need it to work the other way.
You didn't name your initializers correctly, it needs double underscores on either end:
def __init__(self):
You need to correct that for all your classes.
Because you didn't name them correctly, they are not being run when you create an instance of your classes.
Next problem you'll run into is your triangle initializer; you cannot address indexes in an empty list. Create the whole list in one go instead:
def __init__(self):
self._side = [0, 0, 0, 0]
self._angle = [0, 0, 0, 0]
self._perimeter = 0
self._area = 0
You have the constructor written incorrectly. It should be __init__, not _init_, i.e. double underscores.
Because you wrote it wrong, it's not being called, so those attributes aren't being created when you instantiate your objects.
Here's what I want to write:
groups[m][n] = groups[m - 1][n] or ++gid
Here's what I have to write:
g = groups[m - 1][n]
if g:
groups[m,n] = g
else:
gid += 1
groups[m][n] = gid
Is there no more compact way of writing that in Python simply because it lacks a ++ operator?
A larger sample from a method I'm working on:
groups = [[0] * self.columns] * self.rows
gid = 0
for m in xrange(self.rows):
for n in xrange(self.columns):
stone = self[m, n]
if stone == self[m - 1, n]:
if groups[m - 1][n]:
groups[m][n] = groups[m - 1][n]
else:
gid += 1
groups[m][n] = gid
elif stone == self[m, n - 1]:
if groups[m][n - 1]:
groups[m][n] = groups[m][n - 1]
else:
gid += 1
groups[m][n] = gid
I think it's a lot harder to read when I have to blow it out like that, plus I'm evaluating m-1 twice... I'm not sure how I can condense it though.
This is what I came up with:
I created a wrapper class around int:
class Int(object):
def __init__(self, i):
self.i = i
def pre(self, a=1):
self.i += a
return Int(self.i)
def post(self, a=1):
cpy = Int(self.i)
self.i += a
return cpy
def __repr__(self):
return str(self.i)
def __nonzero__(self):
return self.i != 0
Which can be used like this:
def group_stones(self):
groups = [[None for _ in xrange(self.cols)] for _ in xrange(self.rows)]
gid = Int(0)
for m in xrange(self.rows):
for n in xrange(self.cols):
stone = self[m, n]
if stone == self[m - 1, n]:
groups[m][n] = groups[m - 1][n] or gid.pre()
elif stone == self[m, n - 1]:
groups[m][n] = groups[m][n - 1] or gid.pre()
else:
groups[m][n] = gid.pre()
Much like I would do in other languages.
gid = [0] # list - mutable object
def incremented(gid):
gid[0] += 1
return gid[0]
groups[m][n] = groups[m - 1][n] or incremented(gid)
You can add some "magic" to your Int class:
class C(object):
...
def __add__(self, other):
self.i += other
return self.__class__(self.i)
def __radd__(self, other):
cpy = self.__class__(self.i)
self.i += other
return cpy
>>> print Int(2) + 1 # pre
3
>>> i = Int(2)
>>> print 1 + i # post
2
>>> print i
3
Technically more compact, but not really more readable nor less DRY:
groups[m][n], gid = (groups[m-1][n], gid) if groups[m-1][n] else (gid+1, gid+1)
Less compact (for a single usage, at least), more readable:
def test_or_inc(val, accum):
return (val, accum) if val else (accum+1, accum+1)
groups[m][n], gid = test_or_inc(groups[m-1][n], gid)
Another option is to make gid something you can pass by reference... such as a property of an object, or an item in a list.
If you put the gid generation in a function you can do that. For example (using the global scope):
gid = 0
def newgid(): global gid; gid += 1; return gid
Now you can write:
groups[m][n] = groups[m - 1][n] or newgid()
Of course it would be better to put the gid and newgid in its own class or in the class where your other methods are.