Modify object attribute in Class declaration due to attributes relationship - python

I have a class with 3 attributes:
class Player():
def __init__(self,pos,stack):
self.pos = pos
self.stack = stack
self.debt = 0
All instances will begin with debt = 0. In my main program I will be modifying these attributes for many instances, but a player's debt can't be greater than a player's stack. Is it a way to prevent (especify) this from the class declaration? . I don't want to write
if player.debt > player.stack:
player.debt = player.stack
everytime a player's debt is modified. Is it a way to do this automatically from the class Player?
For example, in the following code I want to make the automatic modifications :
jug = Player("BTN",1000) # jug.debt then must be 0
jug.debt = 500 # jug.debt then must be 500
jug.debt = 2000 # jug.debt then must be 1000

Write a property and in the setter check if it exceeds the limit.
class Player():
def __init__(self,pos,stack):
self.pos = pos
self.stack = stack
self._debt = 0
#property
def debt(self):
return self._debt
#debt.setter
def debt(self, val):
if val > self.stack:
val = self.stack
self._debt = val

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.

Python list with two values

I have a program that simulates a bus in the form of a list and there is an ability to add passengers to the bus. I want to be able to set a max number of passengers, so that if the list exceeds 25 passengers I display a code stating that the bus is full.
Is it possible to set this limit in a list with Python.
Here is a snippet of the code:
#defining a class for the passenger list
class Bus:
passengers = []
number_of_passengers = 0
You can use super keyword for override lists.
class PassengerList(list):
limit = 0
def __init__(self, lim):
self.limit = lim
def append(self, item):
if len(self) >= self.limit:
raise Exception('Limit exceeded.')
super(PassengerList, self).append(item)
passengers = PassengerList(25)
passengers.append('abc')
You can set limit by parameter.
You'd probably want to check the length of the list and then decide if you'll add a passenger or display a message. Something like so:
class Bus:
def __init__(self, max_number_of_passengers = 25):
self.passengers = []
self.max_number_of_passengers = max_number_of_passengers
def add_passenger(self, passenger):
if len(self.passengers) > self.max_number_of_passengers:
# display message
else:
self.passengers.append(passenger)
you can use a class
class Bus:
def __init__(self):
self.passengers = []
self.MAX_LIMIT = 25
def add_passenger(self, passenger):
if(len(self.passengers) <= self.MAX_LIMIT):
self.passengers.append(passenger)
else:
print('sorry bus is full')
def show_passengers(self):
print(self.passengers)
bus = Bus()
for i in range(26):
bus.add_passenger(i)
bus.add_passenger(26) #sorry bus is full
class Bus:
def __init__(self, limit=25):
self.passengers = []
self.bus_limit = limit
self.is_bus_full = False
def add_passenger(self):
if len(self.passengers) < self.bus_limit:
self.passengers.append(1) # add dummy values
else:
self.is_bus_full = True
def passenger_left(self):
self.passengers.pop()
def bus_status(self):
if self.is_bus_full:
return 'This bus is full'
else:
return 'This bus has vacant seats'
You can write something like this.

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

How to get the name of a variable

I'm practicing writing classes in Python and I'm stumped on how to do something.
class GolfClub:
def __init__(self, size, distance):
self.size = size
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
return "(name of club variable) hits further"
else:
return "(name of club variable) hits further"
If I do:
club1 = GolfClub(5, 200)
club2 = GolfClub(6, 300)
club1.hits_further(club2)
How can I make the hits_further method return the name of the variable? For example, I would like it to return:
"club2 hits further"
How can I get the variable names into the method?
Traditionally, you'd give the instance a name:
class GolfClub:
def __init__(self, name, size, distance):
self.name = name
self.size = size
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
return "%s hits further" % self.name
else:
return "%s hits further" % other.name
club1 = GolfClub('Driver', 5, 200)
club2 = GolfClub('9Iron', 6, 300)
club1.hits_further(club2)
The instance itself has no possible way of knowing what name you've given to the variable containing it. So, store the name inside the instance.
Fundamentally, you can't do what you want here, because, in Python, the name is not a property of the variable. It's just a handle, which can be attached and reattached, while the object can have multiple names pointing to it, none of which is the name.
This is why others have suggested adding a name as an init parameter.
If I were to do what you were doing, I would change your class to do something like:
class GolfClub:
def __init__(self, size, distance):
self.size = size
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
return True
else:
return False
And the code to do something like:
club1 = GolfClub(5, 200)
club2 = GolfClub(6, 300)
if club1.hits_further(club2):
print("club1 hits further")
else:
print("club2 hits further")
Change the return statement to a print statement.
Add a name attribute to your class. Then do something like this:
class GolfClub:
def __init__(self, size, distance,name):
self.size = size
self.name = name
self.distance = distance
def hits_further(self, other):
if self.distance > other.distance:
print self.name, "club hits further"
else:
print other.name ,"That club hits further"
club1 = GolfClub(5, 200,"c1")
club2 = GolfClub(6, 300,"c2")
club1.hits_further(club2)
As far as i know, what you wish to achieve is not really possible
In fact it can be done asking the stack
import inspect
import traceback
import sys
import re
class GolfClub:
def __init__(self, size, distance):
self.size = size
self.distance = distance
def hits_further(self, other):
call_str= traceback.extract_stack()[0][3]
m = re.search('([\w]+)\.hits_further\((.*)\)',call_str)
self_name=m.group(1)
other_name=m.group(2)
if self.distance > other.distance:
return self_name+" hits further"
else:
return other_name+" hits further"
club1=GolfClub(1,1)
club2=GolfClub(2,2)
print club1.hits_further(club2)
print club2.hits_further(club1)
The result is:
club2 hits further
club2 hits further
As asked by geogeogeo
You can make it like this:
def name(**variable):
return [x for x in variable]

Using recursion in Python class methods

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 )

Categories