Call a "variable" from a function in Python? - python

I'm not even sure I'm calling those by their correct names but here's what I'm working with. I'm working on a simple hockey simulator that only uses math at this time to simulate the results of the games. Currently there is no names or anything like that and I've just generated a "list" of names for "team1" when the scorers are chosen. That's where this comes in. I want to create a list of players for each team and figured functions would be best...if its not please let me know an easier way. So here's the function right now:
def c1():
_name = "Jason Delhomme"
_overall = random.choice((6.00, 7.44, 8.91))
And the _overall variable would need to be called in some places whereas the _name variable would be called in another place. Is this possible?
tm1center = ((c1 * .5) + (c2 * .35) + (c3 * .15)) * 3
Instead of "c1" above I would replace that with _overall from the c1 function. I'm not sure how to do that or if its possible.. sorry for repeating myself here lol...
And then I have this (below) for the player list:
tm1players = ["player1", "player2", "player3", "player4", "player5"]
Ideally, I would replace all of those with the _name variable in each function. The player functions would also go into more depth with their speed, hands, shooting, etc. ratings as well. I don't know how sim sports handle this as I'm very new to coding (just started school) and I wrote the original engine in excel, which is why its just numbers and not physics or plays.
If this doesn't explain it well enough please let me know and I'll further explain what I'm trying to do.

In your case, its better to use a class and assign two variable to that class then create instances of it, for example:
class Player:
def __init__(self, name):
self.name = name
self.overall = random.choice((6.00, 7.44, 8.91))
player1 = Player('Jason Delhomme')
player2 = Player('Another Player')
tm1center = (player1.overall * .5) + (player2.overall * .35)
tm1players = [player1.name, player2.name]
EDIT:
to add overall when creating the class you need to change the init method to :
def __init__(self, name, overall):
self.name = name
self.overall = overall
then pass it when creating the class:
player1 = Player('Player Name', 6.23)
You can create a list of players at start and append each instance of player to that list for future use, for example:
player_list = []
player1 = Player('Player Name', 5)
player_list.append(player1)
player2 = Player('Player2 Name', 2.5)
player_list.append(player2)
# dynamically create tm1players and tm1center
tm1players = [x.name for x in player_list]
tm1center = [x.overall for x in player_list]

You have to read about object oriented programming, and then start working with classes, methods, and from that creating objects. This is an example for using different functions (methods) under the umbrella of a Class. Note how the use of self is necessary for accessing variables between the different methods:
ClassHockey():
def __init__(self):
self.description = "in here you would define some 'you always use'
variables"
def c1(self):
self._name = "Jason Delhome"
self._overall = random.choice((6.00, 7.44, 8.91))
def c2(self):
self._new_name = self._name + "Jefferson"
return self._new_name
hockeyName = ClassHockey()
hockeyName.c2()
If you run this you'll get the _new_name "Jason Delhome Jefferson", and you can apply this example to all kinds of operations between your class methods.

While manually creating a class is a great solution for this problem, I believe that generating one via collections.namedtuple could also work in this senario.
from collections import namedtuple
import random
Player = namedtuple("Player", "name overall")
player1 = Player("Jason Delhomme", random.choice((6.00, 7.44, 8.91)))
player1 = Player("Other Name", random.choice((6.00, 7.44, 8.91)))
final_overall = (player1.overall * .5) + (player2.overall * .35)
Using list comprehension, you could built your entire team:
list_of_players = [Player(input("Enter player: "), random.choice((6.00, 7.44, 8.91))) for i in range(5)]
tm1center = ((list_of_players[0].overall * .5) + (list_of_players[1].overall * .35) + (list_of_players[2].overall * .15)) * 3

Python lets you group info and data together using classes. In your case, you mostly have information about each player, like a name and various stats. You could define a class like:
class Player:
def __init__(self, name):
self.name = name
self.overal = random.choice((6.00, 7.44, 8.91))
Now you can create a bunch of objects from the class:
c1 = Player("player1")
c2 = Player("player2")
c3 = Player("player3")
And you could calculate
tm1center = ((c1.overall * .5) + (c2.overall * .35) + (c3.overall * .15)) * 3
Since you already have a list of player names, you can use a list comprehension to get a list of players in one step:
tm1players = ["player1", "player2", "player3", "player4", "player5"]
tm1 = [Player(name) for name in tm1players]
Now you can access each element in that list, and get their name and overall attributes to do with them as you see fit.
If you want to extend the tm1center1 calculation to include all five players now, you can assign a weight to each one:
class Player:
def __init__(self, name, weight):
self.name = name
self.weight = weight
self.overal = random.choice((6.00, 7.44, 8.91))
tm1center = sum([p.overall * p.weight for p in tm1]) * len(tm1)
where tm1 is defined something like:
tm1players = ["player1", "player2", "player3", "player4", "player5"]
tm1weights = [0.5, 0.3, 0.05, 0.1, 0.05]
tm1 = [Player(name, weight) for name, weight in zip(tm1players, tm1weights)]
Python is a pretty flexible language. Go crazy with it!

Related

Loading CSV file to create objects of a class (Python, Jupyter Notebook)

I am try to build a database of all MLB and minor league baseball players. Keeping track of what teams they are on, transactions, and eventually statistics.
It is a huge database, and I'm not far along yet, but if the file shuts down in Jupyter Notebook I will lose all of the objects I made in my Player class (to create players). I am creating players in the Player class and then using functions to add them to a main dictionary (to make the process of adding easier and quicker). I am creating ID's for the object names, and including a value in the object as the same ID and index number from the dataframe. Then I filter out data to separate teams.
My question is, if I need to save and reload the information back into the Jupyter Notebook, is there any way I can load the same objects from the Player class? A lot of the system I built depends on being able to track the object names of each player and the index number of each player to update information and record information for each player. I included a little bit of my code...
Thanks for any information and help.
class Player:
def __init__(self, number, first, last, pos, cls, ht, wt, bat, throw, birthdate, organization, team, level):
self.number = number
self.first = first
self.last = last
self.pos = pos
self.cls = cls
self.ht = ht
self.wt = wt
self.bat = bat
self.throw = throw
self.birthdate = birthdate
self.age = (age(self.birthdate))
self.organization = organization
self.team = team
self.level = level
def addDirectory(playerid, ident, x, y, z, i):
playerid.id = ident
playerid.status = x
playerid.active = y
playerid.expanded = z
playerid.index = i
directory['ID'].append(playerid.id)
directory['First'].append(playerid.first)
directory['Last'].append(playerid.last)
directory['Age'].append(playerid.age)
directory['Organization'].append(playerid.organization)
directory['Team'].append(playerid.team)
directory['Level'].append(playerid.level)
directory['Position'].append(playerid.pos)
directory['Height'].append(playerid.ht)
directory['Weight'].append(playerid.wt)
directory['Bats'].append(playerid.bat)
directory['Throws'].append(playerid.throw)
directory['Status'].append(playerid.status)
if playerid.expanded == True:
directory['40-Man'].append('Yes')
else:
directory['40-Man'].append('No')
print(playerid.first + ' ' + playerid.last + ' added to Directory')
print(playerid.id + ' index number is: ' + str(playerid.index))

How do I make my (derived?) python instance attributes "self-update" based on alterations to other same-instance attributes?

I know that I have a misunderstanding of how Python attributes work because I'm here writing this problem, but I don't know exactly what I'm misunderstanding. I'm trying to get
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
to attain their values based on self.hand, which I pass to __init__ upon instantiation. Throughout the game I am altering .hand, and I want .card & .card_val to change every time I change .hand, instead of having to tell it to do that elsewhere (outside of the attribute definitions). I know there is a way to do this, or at least I think there is, and by that I mean simply by defining their values as inherited based on whatever .hand is at any given time, without calling an internal or external function.
In the posted code, I have altered it to work as the game instructions require, using...
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
...but that's what I want to change. I want what that function is doing to be executed more concisely inside of the attribute definitions upon handling of the instance. Basically my question is why can't these two attributes get their values directly from the first attribute that I define via "hand" passed to the init?
Any help would be appreciated, and more importantly, I think more than just solutions, it would help me even more to understand what I am misunderstanding about how attributes, instances, and instantiation and all that works so that I know where my thinking is wrong. Thanks!
import random
from random import shuffle
from collections import deque
class Deck():
def __init__(self):
self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
self.suites = ["Heart", "Diamond", "Spade", "Club"]
self.cards = []
def create_cards(self):
for suite in self.suites:
for key in self.ranks.keys():
self.cards.append(key + " " + suite)
def shuffle(self):
random.shuffle(deck.cards)
deck = Deck()
deck.create_cards()
deck.shuffle()
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
def war(bool, p1, p2):
if bool == True:
for player in [p1, p2]:
player.card_number = 4
else:
for player in [p1, p2]:
player.card_number = 0
p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))
p2.name = "The Computer"
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
def cant_war_lose(winner, loser):
print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))
def battle_win(winner, loser):
print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))
def play_cards(p1, p2):
war(False, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-1)
elif p1.card_val == p2.card_val:
if len(p1.hand) < 5 or len(p2.hand) < 5:
if len(p1.hand) > len(p2.hand):
cant_war_lose(p1.name, p2.name)
else:
cant_war_lose(p2.name, p1.name)
return 0
else:
input("War is inititated! Press Enter to continue!")
print("---------------------------")
war(True, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
for i in range(0,5):
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-5)
elif p1.card_val < p2.card_val:
for i in range(0,5):
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-5)
else:
p1.hand.rotate(-1)
p2.hand.rotate(-1)
elif p1.card_val < p2.card_val:
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-1)
if len(p1.hand) != 0 and len(p2.hand) != 0:
input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
print("---------------------------")
else:
if len(p1.hand) > len(p2.hand):
battle_win(p1.name, p2.name)
else:
battle_win(p2.name, p1.name)
return 0
def game_run():
run = 1
p1.name = input("Player 1's name? ")
print("---------------------------")
while run == 1:
if play_cards(p1, p2) == 0:
run = 0
game_run()
You can use the property decorator to create a calculated property
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
#property
def hand(self):
return self._hand
#hand.setter
def hand(self, value):
self._hand = value
self.card = self._hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
What you misunderstand is variables, not instances. For instance, the attribute card is a scalar variable attached to the instance. Assigning to it with
self.card = <blah>
does not bind it to blah for constant recomputation. This is a value assignment, not a memory mapping. If you want that long-term binding, you must either write the maintenance routine yourself -- which you've already done, in a way, with the consistent recomputation -- or you must assign a mutable reference to self.card, so that card refers to teh same object as the expression you created.
Given that you are consistently rotating and altering the hand, this is not feasible in your design. Instead, simply write an access routine, perhaps get_next_card(hand), which will rotate the hand, extract the card, and return the desired rank and suit.
If you plan to program more card games, you will also find it handy to define a class card and class hand, with appropriate support routines. Maintain the card as a pair of integers; convert to strings only for printing.
Does that get you moving?
For anyone who wanted to compare a before and after of the problem & final solution, below is the working code for my specific issue. All I had to do was convert self.card and self.card_val to a calculated property. By passing in hand, and subsequently handling only hand, self.card & self.card_val are calculated, since every time I handle the instance of the class (by handling hand), these "method attributes" are being called and altered. Thanks for the input, guys!
class Player():
def __init__(self, hand):
self.name = "name"
self.card_number = 1
self.hand = hand
#property
def card(self):
return self.hand[self.card_number].split()
#property
def card_val(self):
return deck.ranks.get(self.card[0])

How to split received data into classes based on content

I am trying to write a chunk of code that will organize different types of data into classes. I can split them as of now, but I'm not sure how to get Python to look at the string and automatically sort them into either class based on the content of the string. For example, I have the following and would like to pass the string to either class depending on which type of data is being given to me:
#The data comes in by two different types continuously and is displayed as such below:
animal=dog, age=13, colour=brown, name=Jeff
animal=cat, age=9, colour=white, declawed=yes, friendly=yes, name=Jimmy
class Dogclass():
def __init__(self,age,colour,name):
self.age = age
self.colour = colour
self.name = name
class Catclass():
def __init__(self,age,colour,declawed,friendly,name):
self.age = age
self.colour = colour
self.declawed = declawed
self.friendly = friendly
self.name = name
def splitter():
m = re.split('[, =]', data),
if "dog" in m:
I would like my splitter function to not only have the ability to split the strings, but also go on to sort the split data into classes. This is what I had before (did not work) but would like to figure out a way to utilize OOP more and understand the use of classes.
dog = []
cat = []
def splitter(data):
m = re.split('[, =]', data)
if 'dog' in m:
dog['age'] = (m[7])
dog['colour'] = (m[11])
dog['name'] = (m[13])
elif 'cat' in m:
cat['age'] = (m[7])
cat['colour'] = (m[9])
cat['declawed'] = (m[11])
cat['friendly'] = (m[13])
cat['name'] = (m[15])
else:
return()
I have also tried to create dictionaries to store the data I want to call to, but everything I have tried does not successfully take the splitted data and assign it to a value within my dictionary. Any help would be appreciated.
Lets say you got a string that represent data like this :
"animal=dog, age=13, colour=brown, name=Jeff"
The fisrt thing you would have to do is to parse it to a dictionary like object with a simple function like this one :
def parser(stringToParse):
remove_space = stringToParse.replace(" ", "")
addQuotes = {i.split('=')[0]: i.split('=')[1]
for i in remove_space.split(',')}
return addQuotes
Then you would get an object and you could get its corresponding class by any of the corresponding attribute (lets say your class is based on the "animal" attribute, you could define a simple function to to that :
def getConstructor(classname):
return globals()[classname]
All in one :
import json
class dog():
def __init__(self, age, colour, name):
self.age = age
self.colour = colour
self.name = name
class cat():
def __init__(self, age, colour, declawed, friendly, name):
self.age = age
self.colour = colour
self.declawed = declawed
self.friendly = friendly
self.name = name
def parser(stringToParse):
remove_space = stringToParse.replace(" ", "")
addQuotes = {i.split('=')[0]: i.split('=')[1]
for i in remove_space.split(',')}
return addQuotes
def getConstructor(classname):
return globals()[classname]
def buildIt(any_animal):
my_animal = parser(any_animal)
my_animal_constructor = getConstructor(my_animal["animal"])
if my_animal_constructor.__name__ == "dog":
return dog(my_animal["age"], my_animal["colour"], my_animal["name"])
my_new_animal = buildIt("animal=dog, age=13, colour=brown, name=Jeff")
print(my_new_animal.colour)
In this example i build a dog from the input. If you try to print its coulour you get : "brown"
Of course you will have to implement the if statement for the other class in order to get the cat (and other) class work too...
EDIT
Also, if you want to improve your code you should implement it as an Object oriented one as suggested in Yaron Grushka's answer (create an Animal parent class and makes cat and dog inherit from it)
First of all, I would suggest that in general for cases like these that you use inheritance. You can have a parent class called Animal which has all the common attributes such as age, name and color. Then you can create the Cat and Dog classes that inherit from the parent class, each having unique attributes (such as declawed for cats). Like so:
class Animal():
def __init__(self, name, age, colour):
self.name = name
self.age = age
self.colour = colour
class Cat(Animal):
def __init__(self, name, age, colour, declawed, friendly):
super().__init__(name, age, colour)
self.declawed = declawed # Can make this a boolean with an if/else
self.friendly = friendly # Same
For splitting, you can actually use the split function that Python offers, and use commas as the separator. Then make it into a dictionary. e.g:
def create_animal(data):
details = data.split(",");
attributes = {}
for detail in details:
pair = detail.split("=")
attributes[pair[0]] = pair[1]
print(attributes)
if attributes["animal"] == "cat":
animal = Cat(attributes[" name"], attributes[" age"], attributes[" colour"], attributes[" declawed"], attributes[" friendly"])
else: # Dog creation, same idea...
return animal
a = create_animal("animal=cat, age=9, colour=white, declawed=yes, friendly=yes, name=Jimmy")
print(a.name)
# =>"Jimmy"

How can i add my objects into a list to choose them randomly to blit onto the screen?

When i try to put my objects into a list, i can not get an output with object names, it gives a weird output like "_ main _.object at 0x029E7210". I want to select my objects randomly to blit ONE of them onto the screen. But i could not figure this out.
car_main = pygame.image.load("car_main.png")
car_red_ = pygame.image.load("car_red.png")
car_blue = pygame.image.load("car_blue.png")
class cars:
def __init__(self,x,y,car_type,w=50,h=100,s=5):
self.x = x
self.y = y
self.w = w
self.h = h
self.s = s
self.car_type = car_type
def draw(self):
dp.blit(self.car_type,(self.x,self.y))
car1 = cars(x,y,car_main)
car2 = cars(x,y,car_red)
car3 = cars(x,y,car_blue)
car_list = [car1,car2,car3]
rc = random.choice(car_list)
print(rc)
# output> __main__.object at 0x02A97230
When I change
car_list = [car1,car2,car3] with;
car_list = [car1.car_type,car2.car_type,car3.car_type]
# output > Surface(50x100x32 SW)
But I want to see an output as my object names. Not as a string type ("car_main"). I want to get an output as the object name (car_main) directly. Because in the main loop, i will choose one of them to blit onto the screen everytime when the loop renews itself.
You need to define __str__ for your class Car to let it properly handle object to string:
class Car:
def __str__(self):
for k, var in globals().items():
if var == self:
return k
# default
return "Car"
Note1: Usually use uppercased Car for a class and car for an instance.
Note2: Look up variable strings in globals is not reliable. You may not want to make all variables global, and manually search them in scope is tedious. Actually why don't you give your Car a name attribute? Then you nicely have:
class Car:
def __init__(self, name):
self.name=name
def __str__(self):
return self.name
car = Car(name='first car')
print(car) # 'first car'
More read about "magic methods": https://rszalski.github.io/magicmethods/#representations
Add a __str()__ magic method to your car class like so:
def __str__(self):
return f'car with x of {self.x}, y of {self.y}, and type of {self.car_type}'

Simple OOP query. Python: saving object names in __init__

As close to the title as possible. I am very new to OOP (and coding in general) and would like to create a program that plays Blackjack. I want to save the objects I create into a list automatically so once it's created I can use the list to cycle through them (I want to create player objects, but save the variable names (right word???) to a list so once it's created using user input I can automatically access them.
So far I've built this:
ROSTER = []
class Player():
"""player in the game"""
def __init__(self, name, score= 0):
self.name = name
self.score = score
ROSTER.append(self.name)
But of course this only gives me the names put into the variable self.name... how can I capture the variable names (right term once again?). self.name won't (afaik) let me access the individual objects via:
excuse the crap formatting plz. =/
Also, if I'm using the wrong terms plz correct me. Learning on your own is kinda hard as far as mastering all the terms.
EDIT: sorry, my post was confusing. The code I posted was meant to show a dead end, not what I am looking for, and my terminology is pretty bad (I feel like a foreigner most of the time). When I said variable names, I think I should have said 'object names' (?) so:
p1 = Player("bob")
p2 = Player("sue")
I want ["p1","p2"] (or if a string format will give me problems when I try to call them, whatever the appropriate way is.)
Once again, sorry for the super confusing first post. Hopefully this edit is a little clearer and more focused.
You could put self in the roster instead. I.e.:
ROSTER = []
class Player():
def __init__(self, name, score = 0):
self.name = name
self.score = score
ROSTER.append(self)
Then you would use the ROSTER list like this:
>>> p1 = Player("Jane")
>>> p2 = Player("John")
>>> ROSTER
[<__main__.Player instance at 0x10a937a70>, <__main__.Player instance at 0x10a937a28>]
>>> for p in ROSTER:
... print p.name, p.score
...
Jane 0
John 0
Or, perhaps better, you could make ROSTER a dictionary:
ROSTER = dict()
class Player():
def __init__(self, name, score = 0):
self.name = name
self.score = score
ROSTER[self.name] = self
That way you can access the player objects by name using ROSTER[name], and you can cycle through them with ROSTER.values(). For example:
>>> p1 = Player("Jane")
>>> p2 = Player("John")
>>> print ROSTER["Jane"].name, ROSTER["Jane"].score
Jane 0
>>> print ROSTER["John"].name, ROSTER["John"].score
John 0
>>> for p in ROSTER.values():
... print p.name, p.score
...
Jane 0
John 0
Are you talking about this?
ROSTER = []
class Player():
def __init__(self, name, score= 0):
self.name = name
self.score = score
ROSTER.append(self)
a=Player('Jack',100)
b=Player('Blackk',1000)
c=Player('Mike')
for x in ROSTER:
print(x.name,x.score)
output:
Jack 100
Blackk 1000
Mike 0

Categories