Missing parameter error even though parameter is provided - python

I'm writing a text-based Blackjack game in Python 3.5 and have created the following classes and corresponding methods:
import random
class Card_class(object):
def __init__(self):
pass
def random_card(self):
suites = ['clubs', 'spades', 'diamonds', 'hearts']
denomination = ['ace', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'jack', 'queen', 'king']
card = (suites[random.randint(0, 3)], denomination[random.randint(0, 13)])
return card
def card_check(self, card, cards_distributed):
if card not in cards_distributed:
cards_distributed.append(card)
return True
else:
return False
class Player_hand(object):
def __init__(self):
pass
def cards_held(self, card, cards_holding):
cards_holding.append(card)
return cards_holding
class Distribute_class(object):
def __init_(self):
pass
def initial_card_distribute(self, cards_distributed, player_cards_holding = [], cards_held = 0):
player = ''
while cards_held < 2:
player = Card_class()
result_card = player.random_card()
if not player.card_check(result_card, cards_distributed):
continue
else:
Player_hand.cards_held(result_card, player_cards_holding)
break
return player_cards_holding
I'm attempting to test my code using
distributed_cards = []
player1 = Distribute_class()
player1_hand = player1.initial_card_distribute(distributed_cards)
player1_hand
But I'm given the following error:
TypeError: cards_held() missing 1 required positional argument:
'cards_holding'
The terminal window which presents the error says the error comes from the line containing Player_hand.cards_held(result_card, player_cards_holding) in the final class, Distribute_class, listed above. Does this line not recognize that I had given it a default parameter of player_cards_holding = [] defined in the method within the same class? Or is there some sort of other problem coming from the fact that the method generating the error, "cards_held", is being called from another class?

You are fundamentally misunderstanding how classes work. The problem is that this line:
Player_hand.cards_held(result_card, player_cards_holding)
Is using the instance method cards_held without passing it an instance (which would be self in your method signature). So, to use the method initialize a Player_hand object like so: ph = Player_hand() and then use
ph.cards_held(result_card, player_cards_holding)
What happens under the hood is that ph get's passed implicitly to cards_held.
Also, watch out for the mutable default argument and don't use it unless you understand how it works.
But fundamentally, you are using classes but not correctly. None of your classes have data attributes! All of these could just be module-level functions and work just as well.

Related

Can't make my program generate a random card (1-13)

I'm trying to create a class that will have the following:
Class name: Card
Data attributes:
__value
__face_value
Methods:
__init__() – initialize value to 0 and face_value to an empty string
deal() – generates a random card, assigns the value, calls the set_face_value method
set_value() – assigns an integer card value to the value attribute
set_face_value() – sets the face_value based on the what the value attribute is
get_value() – returns the value of the attribute value
get_face_value() – returns the value of the attribute face_value
__str__() – returns the state of the object
This program, will use the "deal" method in my class and will generate a random number, than it will send it to get_face_value to return the face for the card for example: like 2 = Ace, 10 = ten.
This is my program so far:
import random
class Card:
def __init__(self):
self.__value = 0
face_value = {}
def deal(self, get_face_value):
return random.randit(1,13)
def set_value(self):
return self.__value
def get_value(self, find_face_value):
return self.__value
def find_face_value(self):
self.__value = {'1': 'Ace', 'A': 'Ace', 'J': 'Jack', '11': 'Jack', 'Q': 'Queen', '12': 'Queen', 'K': 'King', '13': 'King'}
def __str__(self):
return self.__value
def main():
card = Card()
card.deal()
print (card)
So the idea of this program is that it will generate a random card every time I execute it but I cant seen to figure it out how to put the finish product. What can I do to generate a random card. Here is my error everytime I execute it:
TypeError: deal() missing 1 required positional argument: 'face_value'
Your methods def deal(self, get_face_value) and def get_value(self, find_face_value) require a positional argument (get_face_value and find_face_value respectively). But going by what those methods do, I reckon you don't need them.
In fact, I'm not sure why you would include them there and I suspect the concepts of parameters and arguments are not clear to you.
Additionally, your deal and set_value methods don't seem to do what you probably want them to do. "Getter" methods like your get_value are supposed to return something, but "setter" methods like set_value are supposed to set those values, not return them (or at least not exclusively). With the code you showed us, get_value and set_value do the exact same thing, and deal doesn't set the card's value to the random number you generate, but simply returns that value after generating it (and doing nothing else with it).
Going by your pseudo code, what you want is probably similar to the following:
import random
class Card:
def __init__(self):
self.value = 0
self.face_value = "" # You mention this should be a string. In your example it's an empty dictionary instead.
def deal(self):
self.set_value(random.randint(1, 13) # You originally typoed "randit".
def set_value(self, value):
self.value = value
# Here we set the value attribute and then call the set_face_value.
self.set_face_value()
def set_face_value(self):
# Here we use the card's own value attribute to determine what face value to give it, using a dictionary lookup.
faces = {} # I'll leave compiling the lookup dictionary to you.
# Just notice that the key has to be an integer of the ones you generated above
# in the format 1: "Ace", 2: "Two", 3: "Three" etc.
self.face_value = faces[self.value]
def __str__(self):
return self.face_value
def main():
card = Card()
card.deal()
print(card)
Of course there are many ways to make this code much better but I preferred to keep it both simple and somewhat similar to yours to show you what was wrong with it.

AttributeError: 'NoneType' object has no attribute 'foo'

I'm getting this error:
[...], line 28, in <module>
PlayerDamage = Dice * int(set_p_w.player_damage)
AttributeError: 'NoneType' object has no attribute 'player_damage'
When I run this code:
import player
Dice = random.randrange(1, 7)
set_p_w = player.set_player_weapon()
PlayerDamage = Dice * set_p_w.player_damage
This is how player.set_player_weapon() looks like:
def set_player_weapon():
import items
player_weapon = items.WBrokenSword
player_damage = player_weapon.damage
I searched everywhere and tried a bunch of different solutions, but nothing helped me. What is wrong with my code?
From the code you posted, player.set_player_weapon() doesn’t return anything. So set_p_w is nothing. You are interacting with set_p_w as if it is an object, but set_player_weapon() doesn’t create an object, it just sets two local variables (player_weapon and player_damage) and then discards them when the function ends.
The simplest way to get this to work is to have your player.set_player_weapon() method return a tuple with that information so it can be stored in the a variable outside the function: (player_weapon, player_damage).
Tuple Method
def set_player_weapon():
import items
player_weapon = items.WBrokenSword
player_damage = player_weapon.damage
return (player_weapon, player_damage)
player_weapon_damage = player.set_player_weapon()
PlayerDamage = Dice * player_weapon_damage[0]
A better way would be to make an class for Player which has the attributes player_weapon and player_damage as well as methods like def set_player_weapon() that set and change its attributes.

When to NOT use the self convention in Python?

I've just recently wrapped my head around the self convention in Python and have begun making more complex code. However, an experienced programmer and friend of mine told me that to use self for every variable in a class method is wasteful.
I understand that self will cause the variable to become attributed to that class. So would it be true that, unless the need arises, it is good practice to avoid using self?
Below is some code that fetches League of Legends information from an API and stores each variable in self.var_name to illustrate how I'm (perhaps unnecessarily) using self.
async def getChampInfo(self, *args):
""" Return play, ban, and win rate for a champ """
self.uri = "http://api.champion.gg/v2/champions/{}?api_key={}"
self.champ = " ".join(args)
self.champID = lu.getChampID(self.champ)
self.res = requests.get(self.uri.format(
self.champID, League.champion_gg_api_key)).json()
self.role = self.res[0]["role"]
self.role_rate = self.res[0]["percentRolePlayed"]
self.play_rate = self.res[0]["playRate"]
self.win_rate = self.res[0]["winRate"]
self.ban_rate = self.res[0]["banRate"]
There are cases where using self is not needed.
Off the top of my head:
when the variable is only used in 1 function, or is created inside a function/method and only used in that function/method
when the variable doesn't need to be shared between methods
when the variable doesn't need to be exposed to other classes/scopes/contexts
Another partial answer is that when creating metaclass/factories/composition something like this might make more sense to move away from the convention of using self like:
class Factory(object):
def __init__(cls, *args, **kwargs):
thing = cls(args, kwargs)
I might be missing some stuff here, but those are what i can think of at the moment.
related:
https://stackoverflow.com/a/7722353/2026508
What is the purpose of self?
self will cause a variable to become attributed to an instance of the class, not the class itself. I don't know if you meant that or not, but it's certainly worth thinking about.
Variables in the class-wide scope can be divided into two categories: class and instance variables. Class variables are defined at the beginning of the class definition, outside of any method. If a variable is constant for all instances, or it is only used in class/static methods, it should be a class variable. Often, such variables are true constants, though there are numerous cases where they aren't. Instance variables are generally defined in __init__, but there are numerous cases where they should be defined elsewhere. That being said, if you don't have a good reason not to, define instance variables in __init__, as this keeps your code (and class) organized. It is perfectly acceptable to give them placeholder values (such as None), if you know the variable is essential to the state of the instance but its value is not determined until a certain method is called.
Here's a good example:
class BaseGame:
"""Base class for all game classes."""
_ORIGINAL_BOARD = {(0,0): 1, (2,0): 1, (4,0): 1, (6,0): 1, (8,0): 1,
(1,2): 1, (3,2): 1, (5,2): 1, (7,2): 1, (2,4): 1,
(4,4): 1, (6,4): 1, (3,6): 1, (5,6): 1, (4,8): 0}
_POSSIBLE_MOVES = {(0,0): ((4,0),(2,4)),
(2,0): ((4,0),(2,4)),
(4,0): ((-4,0),(4,0),(2,4),(-2,4)),
(6,0): ((-4,0),(-2,4)),
(8,0): ((-4,0),(-2,4)),
(1,2): ((4,0),(2,4)),
(3,2): ((4,0),(2,4)),
(5,2): ((-4,0),(-2,4)),
(7,2): ((-4,0),(-2,4)),
(2,4): ((4,0),(2,4),(-2,-4),(2,-4)),
(4,4): ((-2,-4,),(2,-4)),
(6,4): ((-4,0),(-2,4),(-2,-4),(2,-4)),
(3,6): ((-2,-4),(2,-4)),
(5,6): ((-2,-4),(2,-4)),
(4,8): ((-2,-4),(2,-4))}
started = False
def __call__(self):
"""Call self as function."""
self.started = True
self.board = __class__._ORIGINAL_BOARD.copy()
self.peg_count = 14
self.moves = []
#staticmethod
def _endpoint(peg, move):
"""Finds the endpoint of a move vector."""
endpoint = tuple(map(add, peg, move))
return endpoint
#staticmethod
def _midpoint(peg, move):
"""Finds the midpoint of a move vector."""
move = tuple(i//2 for i in move)
midpoint = tuple(map(add, peg, move))
return midpoint
def _is_legal(self, peg, move):
"""Determines if a move is legal or not."""
endpoint = self._endpoint(peg, move)
midpoint = self._midpoint(peg, move)
try:
if not self.board[midpoint] or self.board[endpoint]:
return False
else:
return True
except KeyError:
return False
def find_legal_moves(self):
"""Finds all moves that are currently legal.
Returns a dictionary whose keys are the locations of holes with
pegs in them and whose values are movement vectors that the pegs
can legally move along.
"""
pegs = [peg for peg in self.board if self.board[peg]]
legal_moves = {}
for peg in pegs:
peg_moves = []
for move in __class__._POSSIBLE_MOVES[peg]:
if self._is_legal(peg, move):
peg_moves.append(move)
if len(peg_moves):
legal_moves[peg] = peg_moves
return legal_moves
def move(self, peg, move):
"""Makes a move."""
self.board[peg] = 0
self.board[self._midpoint(peg, move)] = 0
self.board[self._endpoint(peg, move)] = 1
self.peg_count -= 1
self.moves.append((peg, move))
def undo(self):
"""Undoes a move."""
peg, move = self.moves.pop()
self.board[peg] = 1
self.board[self._midpoint(peg, move)] = 1
self.board[self._endpoint(peg, move)] = 0
self.peg_count += 1
def restart(self):
"""Restarts the game."""
self.board = __class__._ORIGINAL_BOARD.copy()
self.peg_count = 14
self.moves.clear()
_ORIGINAL_BOARD and _POSSIBLE_MOVES are true constants. While started is not a constant, as its value depends on whether the __call__ method was invoked or not, its default value, False, IS constant for all instances, so I declared it as a class variable. Notice that in __call__ (don't worry about why I used __call__ instead of __init__), I redefined it as an instance variable, as __call__ starts the game, and therefore when it is invoked, the instance's state has changed from the class default, "not started", to "started".
Also notice that the other methods besides __call__ regularly change the value of the instance variables, but that they are not initially defined in said methods, as there is no compelling reason for them to be.

TypeError: 'NoneType' object is not callable when creating an instance of object

Hi next thing is bothering me:
I'm trying to use the next class:
class GameStatus(object):
"""Enum of possible Game statuses."""
__init__ = None
NotStarted, InProgress, Win, Lose = range(4)
def getStatus(number):
return{
0: "NotStarted",
1: "InProgress",
2: "Win",
3: "Lose",
}
in another class(both in same py file).
In this another class in his method init i do next thing:
class Game(object):
"""Handles a game of minesweeper by supplying UI to Board object."""
gameBoard = []
gs = ''
def __init__(self, board):
self.gameBoard = board
gs = GameStatus() //THIS IS THE LINE
And when i try to run the game i get next error message:
File "C:\Users\Dron6\Desktop\Study\Python\ex6\wp-proj06.py", line 423, in __init__
gs = GameStatus()
TypeError: 'NoneType' object is not callable
What am i doing wrong?
You are setting the GameStatus initializer to None:
class GameStatus(object):
__init__ = None
Don't do that. Python expects that to be a method. If you do not want to have a __init__ method, do not specify it at all. At most, make it an empty function:
class GameStatus(object):
def __init__(self, *args, **kw):
# Guaranteed to do nothing. Whatsoever. Whatever arguments you pass in.
pass
If you wanted to create an enum-like object, take a look at How can I represent an 'Enum' in Python?
For Python 2.7, you could use:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
reverse = dict((value, key) for key, value in enums.iteritems())
enums['reverse_mapping'] = reverse
return type('Enum', (), enums)
GameStatus = enum('NotStarted', 'InProgress', 'Win', 'Lose')
print GameStatus.NotStarted # 0
print GameStatus.reverse_mapping[0] # NotStarted
Ok so after small research i found the problem.
The code i got was:
class GameStatus(object):
"""Enum of possible Game statuses."""
__init__ = None
NotStarted, InProgress, Win, Lose = range(4)
I needed to convert nymbers to their value.
So i build:
def getStatus(number):
return{
0: "NotStarted",
1: "InProgress",
2: "Win",
3: "Lose",
}
And couldnt use it, because i couldn't create an object, and this mothod wasn't static.
The solution: Add #staticmethod before the method.
Plus i had one small error with the return switch, the correct version which works is:
#staticmethod
def getStatus(number):
return{
0: "NotStarted",
1: "InProgress",
2: "Win",
3: "Lose",
}[number]
Thanks for all who tried to help.

Get the return value from a function in a class in Python

I am trying to simply get the value out of my class using a simple function with a return value, I'm sure its a trivial error, but im pretty new to python
I have a simply class set up like this:
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(num):
self.score = num
# Enemy Info
def getEnemies():
return self.num_enemies
# Lives Info
def getLives():
return self.getLives
etc.....
Than I create an instance of the class as such:
scoreObj = score()
for enemies in range(0, scoreObj.getEnemies):
enemy_sprite.add(enemy())
I get the error saying that an integer is expected, but it got an instancemethod
What is the correct way to get this information?
Thanks!
scoreObj.getEnemies is a reference to the method. If you want to call it you need parentheses: scoreObj.getEnemies().
You should think about why you are using a method for this instead of just reading self.num_enemies directly. There is no need for trivial getter/setter methods like this in Python.
The first parameter for a member function in python is a reference back to the Object.
Traditionally you call it "self", but no matter what you call the first parameter, it refers back to the "self" object:
Anytime I get weird errors about the type of a parameter in python, I check to see if I forgot the self param. Been bit by this bug a few times.
class score():
#initialize the score info
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
# Score Info
def setScore(self, num):
self.score = num
# Enemy Info
def getEnemies(self):
return self.num_enemies
# Lives Info
def getLives(foo): #foo is still the same object as self!!
return foo.num_lives
#Works but don't do this because it is confusing
This code works:
class score():
def __init__(self):
self.score = 0
self.num_enemies = 5
self.num_lives = 3
def setScore(self, num):
self.score = num
def getEnemies(self):
return self.num_enemies
def getLives(self):
return self.getLives
scoreObj = score()
for enemy_num in range(0, scoreObj.getEnemies()):
print enemy_num
# I don't know what enemy_sprite is, but
# I commented it out and just print the enemy_num result.
# enemy_sprite.add(enemy())
Lesson Learned:
Class functions must always take one parameter, self.
That's because when you call a function within the class, you always call it with the class name as the calling object, such as:
scoreObj = score()
scoreObj.getEnemies()
Where x is the class object, which will be passed to getEnemies() as the root object, meaning the first parameter sent to the class.
Secondly, when calling functions within a class (or at all), always end with () since that's the definition of calling something in Python.
Then, ask yourself, "Why am I not fetching 'scoreObj.num_lives' just like so instead? Am I saving processing power?" Do as you choose, but it would go faster if you get the values directly from the class object, unless you want to calculate stuff at the same time. Then your logic makes perfect sense!
You made a simple mistake:
scoreObj.getEnemies()
getEnemies is a function, so call it like any other function scoreObj.getEnemies()

Categories

Resources