Using recursion in Python class methods - python

NB Noob alert ... !
I am trying to use recursion in a Python class method, but with limited results.
I'm trying to build a car class, with very basic attributes: id, position in a one lane road (represented by an integer), and velocity. One of the functions I have is used to return which car id is in front on this one -- i.e. if we have class:
class Car:
def __init__(self, position, id, velocity):
self.position = position
self.id = id
self.velocity = velocity
Now, I've come up with the following class method (additional details below the code):
def findSuccessorCar(self, cars):
successorCar = ""
smallestGapFound = 20000000
for car in cars:
if car.id == self.id: continue
currentGap = self.calculateGap(car)
if (currentGap > -1) and (currentGap < smallestGapFound):
smallestGapFound = currentGap
successorCar = car
if successorCar == "":
return 1 # calling code checks for 1 as an error code
else:
return successorCar
The plan is to create car objects, then store them in a list. Each time the findSuccessorMethod is called, this global list of cars is passed to it, e.g.
c1 = testCar.Car(4, 5, 1) # position, pos_y, Vel, ID
c2 = testCar.Car(7, 9, 2)
c3 = testCar.Car(9, 1, 2)
cars = [c1, c2, c3]
c1_succ = c1.findSuccessorCar(cars)
This works fine: the find successor car function will say that car c2 is in front of car c1 (position 7 ahead of position 4).
However, I want car c1 to work out what car is in front of its immediate successor -- that is, which car is in front of the car in front, which in this case is car c3. My thinking was that if I did c1_succ.findSuccessorCars(cars) then this should work fine: doing type(c1_succ) shows it is an instance and hasattr shows that it has the anticipated object attributes.
However, when I do try to execute c1_succ.findSuccessorCars(cars), an integer is returned. Hence, I am confused -- why doesn't this work? Why can you not recursively execute a class method in this fashion? Where does this integer come from?
NB Gut feel says that this has something to do with the self declaration, and that I'll need to modify my code so that as well as a global list of cars, there'll need to be a global list of their current positions, or another class method, e.g.
findSuccessorsSuccessor (yes, fully aware of crummy naming!). However, I am interested to understand why this recursive approach does not work.
UPDATE
Here is the requested code for calculating a gap between 2 cars -- I appreciate it is very basic, so not too much laughter at the back please.
def calculateGap(self, car):
''' Calculate the gap between two cars
'''
thisCar = self
otherCar = car
gap = otherCar.position_x - thisCar.position_x
return gap

What you're calling a class method is actually an instance method. Class methods operate on the class, and instance methods operate on the instance. Here, we're dealing with Car instances, not the Car class itself.
class Car(object):
def __init__(self, position, id, velocity):
self.position = position
self.id = id
self.velocity = velocity
def __eq__(self, other):
return self.id == other.id
def __str__(self):
return 'Car(%d, %d, %d)' % (self.position, self.id, self.velocity)
def calculateGap(self, other):
return other.position - self.position
def findSuccessor(self, cars):
ret = smallestGap = None
for car in cars:
if car == self:
continue
gap = self.calculateGap(car)
if gap < 0:
continue
if smallestGap is None or gap < smallestGap:
ret, smallestGap = car, gap
return ret
def findNthSuccessor(self, n, cars):
cur = self
for x in xrange(n):
cur = cur.findSuccessor(cars)
if cur is None:
return None
return cur
c1 = Car(4, 5, 1)
c2 = Car(7, 9, 2)
c3 = Car(9, 1, 2)
cars = [c1, c2, c3]
print c1.findSuccessor(cars)
print c1.findSuccessor(cars).findSuccessor(cars)
print c1.findNthSuccessor(2, cars)
Output:
Car(7, 9, 2)
Car(9, 1, 2)
Car(9, 1, 2)

Your method does work in theory; this is an implementation bug. That said, it is not the right way to do things; specifically, findSuccessorCar should not be a class method of Car. This is because the list of Car instances is a separate construct; the class Car doesn't and shouldn't know anything about it. If you wanted to make a class for it you should make a Road which is a list of Cars, and put findSuccessorCar on that.
That said, I don't see why you can't do
import operator
cars.sort( key = operator.attrgetter( "position" ) )
to sort the list of cars in position order. I think you're implementing your own sorting algorithm to find the successor car?
Other points of note: you should use exceptions (raise BadCarMojoError) to indicate failure, not magic return codes; classmethods traditionally use cls instead of self as the first argument; and Car should inherit from object.
import bisect
class Car( object) :
def __init__( self, position, id, velocity ):
self.position = position
self.id = id
self.velocity = velocity
def __lt__( self, other ):
return self.position < other.position
class Road( object ):
def __init__( self ):
self.cars = [ ]
def driveOn( self, car ):
bisect.insort( self.cars, car )
def successor( self, car ):
i = bisect.bisect_left( self.cars, car )
if i == len( self.cars ):
raise ValueError( 'No item found with key at or above: %r' % ( car, ) )
return self.cars[ i + 1 ]
c1 = Car( 4, 5, 1 )
c2 = Car( 7, 9, 2 )
c3 = Car( 9, 1, 2 )
c1 < c2
road = Road( )
for car in ( c1, c2, c3 ):
road.driveOn( car )
c1_succ = road.successor( c1 )

Related

Is there a cleaner more pythonic way to create class instances within class instances?

I am programming a game with discord py. Each game have 10 players divided into 5 teams.
I don't really need:
A reference for each player, so the self.BlackHero etc properties are unnecesary...
But I do need:
Initiate 10 Player instances.
Initiate 5 Team instances, containing the 10 Player instances.
A list containing the 12 Player instances.
I was wondering if there's a more elegant way to write the code below? Thanks!
class Game:
def __init__(self, title):
self.title = title
self.round = 1
self.status = 'Registration'
self.winners = ''
self.deadline = ''
self.registrations = {}
self.chests = []
self.monsters = []
self.BlackHero = Player('BlackHero.png', 'black-team', 'hero', 'J6')
self.BlackWitch = Player('BlackWitch.png', 'black-team', 'witch', 'J5')
self.BlueHero = Player('BlueHero.png', 'blue-team', 'hero', 'A7')
self.BlueWitch = Player('BlueWitch.png', 'blue-team', 'witch', 'A8')
self.GreenHero = Player('GreenHero.png', 'green-team', 'hero', 'F9')
self.GreenWitch = Player('GreenWitch.png', 'green-team', 'witch', 'G9')
self.RedHero = Player('RedHero.png', 'red-team', 'hero', 'B0')
self.RedWitch = Player('RedWitch.png', 'red-team', 'witch', 'A0')
self.YellowHero = Player('YellowHero.png', 'yellow-team', 'hero', 'I0')
self.YellowWitch = Player('YellowWitch.png', 'yellow-team', 'witch', 'H0')
self.players = [
self.BlackHero,
self.BlackWitch,
self.BlueHero,
self.BlueWitch,
self.GreenHero,
self.GreenWitch,
self.RedHero,
self.RedWitch,
self.YellowHero,
self.YellowWitch
]
self.teams = {
'black-team':Team(self.BlackHero, self.BlackWitch),
'blue-team':Team(self.BlueHero, self.BlueWitch),
'green-team':Team(self.GreenHero, self.GreenWitch),
'red-team':Team(self.RedHero, self.RedWitch),
'yellow-team':Team(self.YellowHero, self.YellowWitch)
}
class Player:
def __init__(self, image, team, role, pos):
self.username = None
self.user_id = None
self.image = image
self.team = team #red/blue/green/yellow/black
self.role = role #witch/hero
self.position = pos
self.new_position = pos
self.old_target = None
self.new_target = None
self.has_sword = False
self.is_frozen = False
self.has_moved = False
self.will_die = False
self.status = 'ALIVE' #alive/dead
class Team:
def __init__(self, hero, witch):
self.hero = hero #username
self.witch = witch #username
self.items = []
self.status = 'ALIVE'
It's functional though...
Assuming that:
Every Team has exactly 2 players, a hero and a witch, and
Every Game consists of a number of Teams indicated by different colors,
you can considerably simplify your code by adapting the different __init__ functions
for better integration. E.g., all you need to determine a pair of hero and witch seems to be
a shared color and
a position for each of them.
Assuming they are always part of a team, it makes more sense to initiate the players in the Team constructor than in Game.
Consider transforming some redundant attributes into something less manual, e.g.,
I assume you manually change has_moved somewhere; I'd use a method for that so
you can check if a player moved via player.has_moved() or, if you set #property
as I did, player.has_moved like a regular attribute. Cf. is_dead in the Team.
I assume you could also check has_sword in a similar manner by checking the
items of the Team or something.
In general, it isn't necessary to initialize each and every possible attribute. E.g., you only
need to set self.username = None if someone could try to access
the username before it's set, which would throw an AttributeError.
If that cannot happen, you can just set the attribute directly when the time
has come to do so (player.username = 'Carl') even
if you didn't initialize it with None beforehand.
If status is binary (dead or alive), you might want to change that to
something like .is_dead and set it to True or False.
It's always a good idea to set __str__ and __repr__ for easier testing, too.
class Player:
"""
Represents a Player. Is called by the Team constructor.
color := color string, e.g. 'blue'
role := 'hero' or 'witch'
pos := '[A-J][0-9]' e.g. 'B4'
"""
def __init__(self, color, role, pos):
self.username = None
self.user_id = None
self.color = color
self.position = pos
self.new_position = pos
# e.g. 'red' and 'hero' -> 'RedHero.png'
self.image = '{}{}.png'.format(color.title(), role.title())
self.team = '{}-team'.format(color) #red/blue/green/yellow/black
self.role = role
self.old_target = None
self.new_target = None
self.has_sword = False # maybe set this as #property method, too?
self.is_frozen = False
self.will_die = False
# maybe set alive/dead as bool, too?
self.is_dead = False
#property
def has_moved(self):
return self.position != self.new_position
# Optional: set string methods
def __str__(self):
return f'<{self.color} {self.role}>'.title()
def __repr__(self):
return repr(str(self))
class Team:
"""
Represents a team of a certain color,
consisting of a hero and a witch player.
color := color string, e.g. 'blue'
positions := Tuple of two positions, strings in the pattern '[A-J][0-9]'
"""
def __init__(self, color, positions):
self.color = color
self.positions = positions
self.items = []
roles = ['hero', 'witch']
# initialize and store hero and witch
for role, pos in zip(roles, positions):
setattr(self, role, Player(color, role, pos))
self.players = [self.hero, self.witch]
#property
def is_dead(self):
"""Returns True if all players are dead"""
return all(p.is_dead for p in self.players)
# Optional: set string methods
def __str__(self):
return f'<{self.color.title()} Team>'
def __repr__(self):
return repr(str(self))
class Game:
"""
Represents a game with teams and players.
"""
def __init__(self, title, colors):
self.title = title
self.colors = colors
# you could also use some smarter heuristic
# to determine the positions
self.positions = [['J6', 'J5'],
['A7', 'A8'],
['F9', 'G9'],
['B0', 'A0'],
['I0', 'H0']]
self.round = 1
self.status = 'Registration'
self.winners = ''
self.deadline = ''
self.registrations = {}
self.chests = []
self.monsters = []
# Builds teams for each color
self.teams = [Team(color, pos) for color, pos in zip(self.colors, self.positions)]
self.players = [p for team in self.teams for p in team.players]
# Optional: set string methods
def __str__(self):
return f"<Game '{self.title}' – {len(self.teams)} teams>"
def __repr__(self):
return repr(str(self))
A new Game is initiated via
Game('Epic match name', ['black', 'blue', 'green', 'red', 'yellow'])
For a lower number of colors, the positions are consumed top-down. Adopt to your needs
if that doesn't make sense.

Matrix in Python That Can Represent Items on a Shelf

My question is pretty straightforward. Is there a library in Python that has a data structure that can represent items on a shelf? Essentially, it would need to be a matrix where each row is the same length but the items in each row can be of varying lengths.
Here's a visual representation:
This should tell you if two shelf items overlap on the shelves (in the sense that one is below or above the other).... Assuming you know where your shelf items are by index.
class Item(list):
pass
shelves = [
[Item([1, 2, 3, 4]) ,Item([5, 6 ])],
[Item([7, 8]),Item([9]),Item([10, 11]), Item([12])]
]
# shelf items are indexed by two indices, shelf and item
def shelf_items_overlap(shelves, shelf1, item1, shelf2, item2):
item1_real_index = sum(map(len, shelves[shelf1][:item1]))
item2_real_index = sum(map(len, shelves[shelf2][:item2]))
return range_overlaps(
item2_real_index, item2_real_index + len(shelves[shelf2][item2]),
item1_real_index, item1_real_index + len(shelves[shelf1][item1])
)
def range_overlaps(x11,x12,x21,x22):
''' return if (x11, x12) overlaps (x21, x22) '''
return x21<=x12<=x22 or x21<=x11<=x22 or x11<=x21<=x12 or x11<=x22<=x12
print(shelf_items_overlap(shelves, 0, 1, 1, 0), shelf_items_overlap(shelves, 0, 1, 1, 2))
I think your problem should make a better use of classes than arrays. I know that this maybe this is not the answer you're looking but here is a quick code of a class implementation of items and shelf_rows that gets you the info of what it is in a shelf.
class Item ():
# Init the item class with a lenght
def __init__(self, length, name = 'Item'):
self.name = name
self.length = length
# Allow us to use the len method on item to get the length
def __len__(self):
return self.length
# Show us a clear print of the Item
def __repr__(self):
return '%r of length (%r)' % (self.name,self.length)
class ShelfRow ():
#Make a ShelfRow of a fixed length
def __init__ (self, length):
self.length = length
self.space_left = length
self.items = []
#Use append like if it was a list
def append (self,Item):
if Item.length > self.space_left:
return 'This should throw an error because the length is too long for the space left'
else:
self.items.append (Item)
self.space_left = self.space_left - Item.length
# Use the __getitem__ special method to get the position of an item
def __getitem__(self,position):
return self.items[position]
#Print the content of items
def __repr__(self):
return '%r' % self.items
# Make three items
item_a = Item(4, 'Big Box')
item_b = Item(2, 'Medium Box')
item_c = Item(2, 'Medium Box')
item_d = Item(8, 'Super Big Box')
#Make a shelfRow
shelf_row1 = ShelfRow(8)
shelf_row2 = ShelfRow(8)
#Populate shelfs
shelf_row1.append(item_a)
shelf_row1.append(item_b)
shelf_row1.append(item_c)
shelf_row2.append(item_d)
#Make a shelf, it could be an object too (it should probably)
shelf = [shelf_row1,shelf_row2]
#Print what is in every shelf_row
for shelf_row in shelf:
print(shelf_row)
The other answer is probably the closest to what you want but I get the feeling that maybe you could do for something a little more abstract to solve your problem.
Anyways I hope that this was useful.
I am not aware of any module, but this seems like a traditional "Make this data structure" type of question. So here's a base you can work with, and it may seem similar to other questions, but this should work and satisfy your needs. Its pretty much an implementation that uses the idea used in page allocation as your problem can be seen as you need frame to contain a certain amount of pages of a fixed size. It could be optimized by using a better algorithm (or process to add) but I forgot which one I used is called nor did I feel like implementing something more complicated.
class Item:
def set_width(self, width):
self.width = width
def get_width(self):
return self.width
def __init__(self, width=0):
self.width = width
class Shelf:
def add_item(self, item):
if item.get_width() > self.max_width:
print("Item will not fit in the shelf.")
return False
row = 0
while (row < len(self.rows)):
if (self.get_row_tot(row) + item.get_width()) <= self.max_width:
self.rows[row].append(item)
return True
row += 1
# Stop here, we want to prevent addition of a shelf
if max_height != -1 and row >= max_height:
print("Item will not fit in the shelf.")
return False
self.rows.append([item])
return True
def get_row_tot(self, row):
tot = 0
for i in self.rows[row]:
tot += i.get_width()
return tot
def remove_item(self, row, item):
if row < len(self.rows):
self.rows[row].remove(item)
return True
else:
print("Row does not exist")
return False
def __init__(self, max_width, max_height=-1):
"""
max_height = -1 means no height limit
"""
self.max_width = max_width
self.max_height = max_height
self.rows = []
self.head = 0
a = Shelf(3)
b = Item(1)
c = Item(1)
d = Item(2)
e = Item(2)
a.add_item(b)
a.add_item(c)
a.add_item(d)
a.remove_item(0, c)
a.remove_item(2, e)
a.add_item(e)
Like I said, this could be optimized and some additions could be made such as a method to swap, set, search, sort and so on so forth. You can also modify Item to be an object you're working with.

Create a Car class in python

I have to create a Car class that has the following characteristics:
It has a gas_level attribute.
It has a constructor (init method) that takes a float representing the initial gas level and sets the gas level of the car to this value.
It has an add_gas method that takes a single float value and adds this amount to the current value of the gas_level attribute.
It has a fill_up method that sets the car’s gas level up to 13.0 by adding the amount of gas necessary to reach this level. It will return a float of the amount of gas that had to be added to the car to get the gas level up to 13.0. However, if the car’s gas level was greater than or equal to 13.0 to begin with, then it doesn’t need to add anything and it simply returns a 0.
Result should be:
example_car = Car(9)
print(example_car.fill_up()) # should print 4
another_car = Car(18)
print(another_car.fill_up()) # should print 0
This is what I have so far. Just got stuck in the add_gas and fill_up methods.
class Car:
def __init__(self, gas_level_x):
self.x = gas_level_x
def add_gas(self):
return ((self.x +
def fill_up(self):
return
def main():
ex_car = Car(9)
print(ex_car.fill_up())
if __name__ == "__main__":
main()
If I understand it right all you want is this?
if the tank is lower then 13 you tank it full till it's 13 and return the added value.
else return 0
class Car:
def __init__(self, gas_level_x):
self.x = gas_level_x
def add_gas(self, gas):
self.x += gas
def fill_up(self):
added = 13-self.x
self.x += added # Or call self.add_gas(added) if for some reason you want to use the method
if added > 0:
return added
return 0
Edit: And to test your requirements I've ran:
car = Car(9)
print(car.fill_up()) # Prints 4
car = Car(18)
print(car.fill_up()) # Prints 0
class Car:
def __init__(self, init_g):
self.g = init_g
def add_gas(gas):
self.g = self.g + gas
def fill_up(self):
if self.g < 13:
return 13 - self.g
else:
return 0

Would I be able to separate variables into another class but keep the usage the same?

I'm trying to rewrite a script and I'm stuck on making it easy to use. Basically it's an assembly script (like the reverse of destruction), where you input a load of variables such as location, whether the location is absolute or relative, scale, rotation, visibility, random offset, etc, to create an animation. The first version was very non user friendly, so I'm trying to get it working nicely from the start this time.
I've thought of how I'd like it to work, and I've managed to keep it clean, but there is a flaw. As you can see below, it'd be possible to use anything like SetGroup.frame[i].save(), which I don't want (and I don't want to put checks on if name is None throughout the class).
Here is the code I have:
class SetGroup(object):
def __init__(self, name=None, _frame_only=False):
if name is None and not _frame_only:
raise TypeError('name of group must be provided')
self.selection = None
self.origin = None
self.start = None
self.end = None
self.offset = 0
self.distance = None
self.random = 0
self.location = None
self.rotation = None
self.scale = None
self.visibility = None
if not _frame_only:
self.frame = defaultdict(lambda: SetGroup(_frame_only=True))
def save(self):
self.load()
#do a bit of error checking here
self.data[self.name] = {'ObjectSelection': self.selection,
'ObjectOrigin': self.origin,
'FrameStart': self.start,
'FrameEnd': self.end,
'FrameOffset': self.offset,
'FrameDistance': self.distance,
'FrameRandom': self.random,
'StartLocation': self.location,
'StartRotation': self.rotation,
'StartScale': self.scale,
'StartVisibility': self.visibility,
'ExtraFrames': self.frame}
pm.fileInfo['AssemblyScript'] = StoreData().save(self.data)
def load(self):
try:
self.data = StoreData().load(pm.fileInfo['AssemblyScript'])
except KeyError:
pm.fileInfo['AssemblyScript'] = StoreData().save({})
The way I'd like it to work is like this:
a = SetGroup('test')
a.location = ((0, 0, 0), True)
a.start = 0
a.end = 10
a.frame[5].location = ((10, 10, 10), False)
a.frame[5].scale = ((2, 1, 1), True)
a.save()
Unless anyone can think of a way which would make it more friendly to use, how would I separate location, rotation, scale, and visibility into another class and link them up again, so that they still work at the core level of the class, but also work for the frame dictionary too?
Edit - Got it working to a basic level:
class _MovementInfo(object):
def __init__(self, location=None, rotation=None, scale=None, visibility=None):
self.location = location
self.rotation = rotation
self.scale = scale
self.visibility = visibility
def __repr__(self):
return '_MovementInfo(location={x.location}, rotation={x.rotation}, scale={x.scale}, visibility={x.visibility}'.format(x=self)
Then I used this in the main class to merge the dictionaries:
self.__dict__.update({k: v for k, v in _MovementInfo().__dict__.iteritems() if '__' not in k})
self.frame = defaultdict(_MovementInfo)
I would change the code like this:
class SetGroup(_Movement):
def __init__(self, name=None):
if name is None:
# ...
super().__init__()
# ...
self.random = 0 # __init__ should end here
# ...
But you should check that all _MovementInfo's in all frames are _MovementInfo's or have inherited from them (to check this: isinstance(x, _MovementInfo)), but are not SetGroup's (to check this: not isinstance(x, SetGroup)).
super() is short for super(SetGroup, self) (you have to use the last option for python2), and is basicly an object that holds all things that the base class has, and allows you to call methods that modify the class calling it.
Or in code:
class A(object):
def __init__(self, y):
self.x = 2
self.y = y
class B(A):
def __init__(self, y, z):
super().__init__(y) # equivalent to: A.__init__(self, y)
self.z = z
b = B(3, 4)
# b's x is 2, b's y is 3 (both set by A.__init__, the last one was passed by B), and b's z is 4 (set by B.__init__)
I hope this helped,
CodenameLambda

How to query on a list by list comprehension and min/max functionalities

I have two Python classes: Agent and Group...
Each Group has a centerGroup property, plus a static list of groups, i.e. GroupList
Here is a brief overview of the Group class:
import Agent
class Group(object):
"""description of class"""
GroupIdentifier = 1
GroupThreshold = 10
GroupList = []
def __init__(self, agentList = None ,groupCenter = None, gruopIdentifier = None):
global GroupIdentifier
global GroupList
self.groupIdentifier = GroupIdentifier
Group.GroupIdentifier += 1
Group.GroupList.append(self)
self.groupCenter = groupCenter
self.agentList = agentList
Furthermore, within the Agent class, I am going to find the minimum euclidean distance of a typical agent from all centerGroup properties corresponding to the groups in the groupList... (There is an offset, is which GAMMA_TRESHOLD)...
One can depict the related part of Agent class, as below snippet:
import Group
class Agent(object):
"""description of class"""
GAMMA_TRESHOLD = 20
def __init__(self, point = None, groupId = None):
self.locationX = point.x
self.locationY = point.y
self.groupId = 0
def get_agent_distance_from_groupCenter(self, object):
return math.sqrt(math.pow(self.locationX - point.x, 2) +
math.pow(self.locationY - point.y, 2))
def gamma_condition(self):
#I KNOW THIS IMPLEMENTATION IS WRONG... JUST GOTTA SHOW THE TARGET!
return Group.Group.GroupList[Group.Group.GroupList.index(min(get_agent_distance_from_groupCenter(agent, group.groupCenter) - GAMMA_TRESHOLD))]
From a mathematical manner perspective, the problem is minimizing the below norm and introducing the group, which its centerGroup is nearest to the agent:
min \norm{centerGroup_{i} - agent - TRESHOLD}
Would you please helping me to write such query (valid processing for gamma_condition method) by list comprehension of Python?!
All in all, with due attention to lack of any better idea from the other people, my investigations lead to below solution for this problem:
def gamma_condition(self):
temp = []
maxValue = 0
temp = [[item.groupIdentifier, JOIN_TRESHOLD - self.get_agent_distance_from_groupCenter(item.groupCenter)] for item in Group.Group.GroupList]
for item in temp:
maxValue = max(float(i) for i in item[1])
if maxValue > 0:
index = temp.index(maxValue)
NearestGroupIdToJoin = temp[index][0]
return NearestGroupIdToJoin
else:
return None

Categories