Calling a Python method from one class to another - python

class A:
def getTotal(self)
self.__total = 0
for each in self.__hand:
if each.getValue() == 1 and self.__total > 10:
self.__total += 1
elif each.getValue() == 1 and self.__total <10:
self.__total += 11
elif each.getValue() != 1:
self.__total += each.getValue()
return self.__total
class B:
def getTotal(self):
return A.getTotal()
This isn't working for me. How can I get class A to return a total when called from class B's method

You cannot just call A.getTotal() because it is an unbound method; there is nothing to bind self to as you didn't give it an instance of A, where you'd have state. You need to call it on an instance of A. We just need to find one first.
From the comments I understand B to the the player, and A is a hand of cards for that player. Presumably in a game a player can play end up with more hands as multiple rounds are played.
In that case you'd have a reference from the player to the hand, like self.hand. That'd be an instance of A and you can call getTotal() on that instead:
class B:
def getTotal(self):
return self.hand.getTotal()
It may be confusing here that A also has a __hand attribute; that's perhaps not the best name for that attribute, as it is the list of cards for the current hand. It could perhaps better be named __cards in that case.
B.getTotal() is not strictly needed; you could also just use instance_of_B.hand.getTotal(), e.g. reach right into the instance attributes and call getTotal() directly on the hand. But that'd perhaps reveal too much about how the class handles hands, and perhaps you want to handle different cases, like return 0 if there is the possibility that at some points in the program is no hand at all.
And another thing: __total in A.getTotal() is a local variable; you don't need to use an attribute on self there. Remove the self. prefix, and just name it total:
def getTotal(self)
total = 0
for each in self.__hand:
if each.getValue() == 1:
if total > 10:
total += 1
else:
total += 11
else:
total += each.getValue()
return total
I simplified the function logic a little too.

Related

How would I set attributes of a method within a class?

In a Python/Qt program, i want to notify the user when a monitored value is outside the expected range. To avoid false positives, the value should be outside the range several times in a row, and we should only notify the user once so it can be acted upon or dismissed.
Below would be my approach, but I am accumulating way too many class attributes that are only used in a single function. What is a cleaner way to implement this?
Is there a way for _counter and _enabled to attributes of the method check_value() only instead of the class TestScreen()?
class TestScreen(QDialog):
def __init__(self):
self._counter = 0
self._enabled = True
def check_value(self):
if self._enabled:
# don't act on the first out of bounds value
if value > target_value:
self._counter += 1
# notify the user when multiple out of bounds values measured
# only notify once
if self._counter > 5:
self._counter = 0
self._enabled = False
QMessageBox.Error("Value too high")

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])

Is there a way to fix Name Error due to scope?

I have a function that creates a player object but when referencing the object, I get a NameError. I think it is happening due to local scope but global should fix it...
I just started out OOP and this code is working in the python shell but it is not working in script mode.
endl = lambda a: print("\n"*a)
class Score:
_tie = 0
def __init__(self):
self._name = ""
self._wins = 0
self._loses = 0
def get_name(self):
print
self._name = input().upper()
def inc_score(self, wlt):
if wlt=="w": self._wins += 1
elif wlt=="l": self._loses += 1
elif wlt=="t": _tie += 1
else: raise ValueError("Bad Input")
def player_num(): #Gets number of players
while True:
clear()
endl(10)
print("1 player or 2 players?")
endl(5)
pnum = input('Enter 1 or 2: '.rjust(55))
try:
assert int(pnum) == 1 or int(pnum) == 2
clear()
return int(pnum)
except:
print("\n\nPlease enter 1 or 2.")
def create_player(): #Creates players
global p1
p1 = Score()
yield 0 #stops here if there is only 1 player
global p2
p2 = Score()
def pr_(): #testing object
input(p1._wins)
input(p2._wins)
for i in range(player_num()):
create_player()
input(p1)
input(p1._wins())
pr_()
wherever I reference p1 I should get the required object attributes but I'm getting this error
Traceback (most recent call last):
File "G:/Python/TicTacTwo.py", line 83, in <module>
input(p1)
NameError: name 'p1' is not defined
Your issue is not with global but with the yield in create_player(), which turns the function into a generator.
What you could do:
Actually run through the generator, by executing list(create_player()) (not nice, but works).
But I suggest you re-design your code instead, e.g. by calling the method with the number of players:
def create_player(num): #Creates players
if num >= 1:
global p1
p1 = Score()
if num >= 2:
global p2
p2 = Score()
If you fix this issue, the next issues will be
1) input(p1) will print the string representation of p1 and the input will be lost, you probably want p1.get_name() instead.
2) input(p1._wins()) will raise TypeError: 'int' object is not callable
I will redesign the app to introduce really powerful python constructs that may help you when getting into OOP.
Your objects are going to represent players, then don't call them Score, call them Player.
Using _tie like that makes it a class variable, so the value is shared for all the players. With only two participants this may be true but this will come to hurt you when you try to extend to more players. Keep it as a instance variable.
I am a fan of __slots__. It is a class special variable that tells the instance variables what attributes they can have. This will prevent to insert new attributes by mistake and also improve the memory needed for each instance, you can remove this line and it will work but I suggest you leave it. __slots__ is any kind of iterable. Using tuples as they are inmutable is my recomendation.
Properties are also a really nice feature. They will act as instance attribute but allow you to specify how they behave when you get the value (a = instance.property), assign them a value (instance.property = value), or delete the value (del instance.property). Name seems to be a really nice fit for a property. The getter will just return the value stored in _name, the setter will remove the leading and trailing spaces and will capitalize the first letter of each word, and the deletter will set the default name again.
Using a single function to compute a result is not very descriptive. Let's do it with 3 functions.
The code could look like this:
# DEFAULT_NAME is a contant so that we only have to modify it here if we want another
# default name instead of having to change it in several places
DEFAULT_NAME = "Unknown"
class Player:
# ( and ) are not needed but I'll keep them for clarity
__slots__ = ("_name", "_wins", "_loses", "_ties")
# We give a default name in case none is provided when the instance is built
def __init__(self, name=DEFAULT_NAME):
self._name = name
self._wins = 0
self._loses = 0
self._ties = 0
# This is part of the name property, more specifically the getter and the documentation
#property
def name(self):
""" The name of the player """
return self._name
# This is the setter of the name property, it removes spaces with .strip() and
# capitalizes first letters of each word with .title()
#name.setter
def name(self, name):
self._name = name.strip().title()
# This is the last part, the deleter, that assigns the default name again
#name.deleter
def name(self):
self._name = DEFAULT_NAME
def won(self):
self._wins += 1
def lost(self):
self._loses += 1
def tied(self):
self._ties += 1
Now that's all we need for the player itself. The game should have a different class where the players are created.
class Game:
_min_players = 1
_max_players = 2
def __init__(self, players):
# Check that the number of players is correct
if not(self._min_players <= players <= self._max_players):
raise ValueError("Number of players is invalid")
self._players = []
for i in range(1, players+1):
self._players.append(Player(input("Insert player {}'s name: ".format(i))))
#property
def players(self):
# We return a copy of the list to avoid mutating the inner list
return self._players.copy()
Now the game would be created as follows:
def new_game():
return Game(int(input("How many players? ")))
After that you would create new methods for the game like playing matches that will call the players won, lost or tied method, etc.
I hope that some of the concepts introduced here are useful for you, like properties, slots, delegating object creation to the owner object, etc.

Change object's variable from different file

I want to access object (specifically its variables) from functions defined in different file. Let's see an example:
File 1 - grail.py
import enemies
class Encounter:
def __init__(self):
self.counter = 1
self.number = 0
self.who = "We've encountered no one."
def forward(self):
if self.counter == 1:
enemies.knightofni()
elif self.counter == 2:
enemies.frenchman()
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
knight = Encounter()
for i in range(4):
print(str(knight.number) + " " + knight.who)
knight.forward()
File 2 - enemies.py (I probably need something in this file)
def knightofni():
Object.number = 1
Object.who = "We've encountered Knight of Ni."
def frenchman():
Object.number = 4
Object.who = "We've encountered French."
Output should show:
0 We've encountered no one.
1 We've encountered Knight of Ni.
4 We've encountered French.
42 We've found the Grail!
I know you can achieve the output by returning something from functions in file enemies.py, for example function frenchman() could look like:
def frenchman():
return [4, "We've encountered French."]
and in grail.py I should change code to collect what the frenchman() returns:
...
elif self.counter == 2:
spam = enemies.frenchman()
self.number = spam[0]
self.who = spam[1]
...
but it uses additional resources, makes the code longer, and more cumbersome in more complicated situations.
Is there a way to do the job directly on the object's variables but keeping functions in separate file?
EDIT
There are already answers to this question but maybe I will add clarification seeing doubt in one of the answers (citing comment to this answer):
I want it to be possible to add other "enemies" without making lengthy code in this place (so forward() is kind of a wrapper, place where it is decided what to do in different situations). It is also more readable if this functions are in different file.
Think of situation where there would be 100 "enemies" and each would need to change 100 variables which are lists with 1M entries each. Is there a better way than putting "enemies" into other file and changing variables directly in the file?
Problem
You need to hand over the object as argument.
In the function:
def knightofni(obj):
obj.number = 1
obj.who = "We've encountered Knight of Ni."
and when using it in the class:
enemies.knightofni(self)
Do the same for frenchman().
Full code
grail.py
import enemies
class Encounter:
def __init__(self):
self.counter = 1
self.number = 0
self.who = "We've encountered no one."
def forward(self):
if self.counter == 1:
enemies.knightofni(self)
elif self.counter == 2:
enemies.frenchman(self)
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
knight = Encounter()
for i in range(4):
print(str(knight.number) + " " + knight.who)
knight.forward()
and enemies.py:
def knightofni(obj):
obj.number = 1
obj.who = "We've encountered Knight of Ni."
def frenchman(obj):
obj.number = 4
obj.who = "We've encountered French."
Output:
0 We've encountered no one.
1 We've encountered Knight of Ni.
4 We've encountered French.
42 We've found the Grail!
It is possible to do this, though I don't know why you would really want to do it this way.
In your forward and __init__ methods you'll notice that you are passing in self, which is the instance of Encounter you are operating on. That is why you can do self.number = 42 and get the correct number when you call knight.number.
Since self is just an object you can pass it into the functions in 'enemies.py'.
Try:
# grail.py
def forward(self):
if self.counter == 1:
enemies.knightofni(self)
elif self.counter == 2:
enemies.frenchman(self)
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
#enemies.py
def knightofni(that):
that.number = 1
that.who = "We've encountered Knight of Ni."
def frenchman(that):
that.number = 4
that.who = "We've encountered French."

How to save buildings at specific coordinates in python?

So I do know how to save the class's x,y coordinates but I don't know how to save buildings at the coordinates my player has been at. I'll attempt to make this more clear.
I'm making a text-based. To move your player you either type left,right,up,or down. It will therefore change your x and y accordingly.
Ex: To move up it adds 1 to the y value of the player class. player.yPos += 1 . However if the player goes to the point 0,1 and then 0,2 but moves back down to 0,1 and there was a building at the point 0,1 how do I make sure it's still there when the player goes back? I've been thinking I'll have to store all of the player's x,y movements in to a list. I don't know how to make the positions of that list equal the object that will be there though. If this doesn't make sense I can attempt rewording.
P.S. Please use the most simple logical explanation possible. Generally when I read something on stackoverflow I want to jump off of a cliff with how involved it is. (Basically, dumb it down for me please!)
class player:
Cnuts = 0
statPoints = 0
pStrength = 0
pConstitution = 0
pQuickness = 0
pIntelligence = 0
pStamina = 0
playerName = ''
Weapon = 0
Armour = 0
Shield = 0
inventory = []
xPos = 0
yPos = 0
#Building Code
class Entity:
id = 0
xPos = 0
yPos = 0
name = ''
description = ''
def __init__(self, id, xLocation, yLocation, name, description):
self.xLocation = xLocation
self.yLocation = yLocation
self.id = id
self.name = name
self.description = description
class Structure(Entity):
pass
I haven't decided what goes in to the Structure/Building class because I don't know what else it needs other than what Entity already has. I have another class for monsters that also inherits from Entity which is why I have it.
#Map code
isExploring = True
def Map():
while isExploring == True:
pInput = input('What direction would you like to go?')
if pInput == 'Up':
player.yPos += 1
elif pInput == 'Down':
player.yPos -= 1
elif pInput == 'Left':
player.xPos -= 1
elif pInput == 'Right':
player.xPos += 1
elif pInput == 'Diagonal Left':
player.xPos
player.yPos
elif pInput == 'Diagonal Right':
pass
elif pInput == 'Down Diag Left':
pass
elif pInput == 'Down Diag Right':
pass
Thanks for the help in advance!
I don't see code for a building, but I'm guessing the building will eventually inherit from Entity (Player should also inherit this class). An entity object has self.xLocation and self.yLocation, so this makes it a bit easier to implement a location aware player. So what you do is that the class you make for building has to implement the __hash__ method, so something like this.
class Building(Entity):
def __hash__(self):
return hash(tuple(self.xLocatioin, self.yLocation))
def __eq__(self, other):
return isinstance(other, Building) and hash(self) == hash(other)
The function is called __hash__ because python recognizes this special keyword as meaning that the building object can be placed in a dictionary. So whenever you try to place a Building object in a set or use it as a key for a dictionary, python will automatically call it's __hash__ method, and use that value to determine the position in which to place the object in the set/dictionary. Implementing hash usually means implementing __eq__ which is another magic function that python automatically calls when you compare 2 objects using the == operator.
The player class will then store each building it has visited in a set, which can then be queried to determine if a building has been visited before
class Player(Entity):
def __init__(self, *args):
super.__init__(self, args)
self.visited = set()
self.currLocation = tuple(self.xLocatioin, self.yLocation)
def visit(self, entity):
if not beenHere(entity):
self.visited.add(entity)
self.currLocation = tuple(entity.xLocatioin, entity.yLocation)
def beenHere(self, entity):
return entity in self.visited
And there you have it, now the player object can determine which building it has visited before or not

Categories