dynamic class instantiation confusion in python - python

I asked a similar, yet lousy, question very late last night (Access to instance variable, but not instance method in Python) that caused a fair bit of confusion. I'd delete it if I could, but I can't.
I now can ask my question more clearly.
Background: I'm trying to build a black-jack game to learn python syntax. Each hand is an instance of the Hand class and I'm now at the point where I'm trying to allow for hands to be split. So, when it comes time for a hand to be split, I need to create two new hand instances. Given that further splits are possible, and I want to reuse the same methods for re-splitting hands. I therefore (I think) need to dynamically instantiate the Hand class.
Following is a code snippet I'm using to block out the mechanics:
import os
os.system("clear")
class Hand():
instances=[]
def __init__(self, hand_a, name):
Hand.instances.append(self)
self.name = name
self.hand_a = hand_a
def show_hand(self):
ln = len(self.hand_a)
for x in range(ln):
print self.hand_a[x]
class Creation():
def __init__(self):
pass
def create_name(self):
hil = len(Hand.instances)
new_name = 'hand_' + str(hil + 1)
return(new_name)
def new_instance(self):
new_dict = {0: 'Ace of Clubs', 1: '10 of Diamonds'}
new_hand_name = {}
new_hand_name.setdefault(self.create_name(), None)
print new_hand_name
new_hand_name[0] = Hand(new_dict, self.create_name())
print new_hand_name[0]
hand = Hand("blah", 'hand')
hand_z = Hand("blah_z", 'hand_z')
creation = Creation()
creation.new_instance()
here is the output:
{'hand_3': None}
<__main__.Hand instance at 0x10e0f06c8>
With regard to the instance created by the following statement:
new_hand_name[0] = Hand(new_dict, self.create_name)
Is new_hand_name[0] new the variable that refers to the instance?
Or, is hand_3 the variable?
i.e. when calling an instance method, can I use hand_3.show_hand()?

First, to answer your questions: new_hand_name[0] is the variable that refers to the instance- more specifically, it is the value in the new_hand_name dictionary accessed by the key 0. The new_hand_name dictionary, if you printed it, would look like:
{'hand_3': None, 0: <__main__.Hand instance at 0x10e0f06c8>}
Adding the value of "hand_3" to the dictionary is unnecessary, but for that matter, so is the dictionary.
What you really want to do has nothing to do with dynamic instantiation of new classes, which has nothing to do with your problem. The problem is that a Hand might represent a single list of cards, but might also represent a list of lists of cards, each of which have to be played separately. One great way to solve this is to make a separation between a player and a hand, and allow a player to have multiple hands. Imagine this design (I'm also leaving out a lot of the blackjack functionality, but leaving a little in to give you an idea of how to work this in with the rest of the program).
def draw_random_card():
"""
whatever function returns a new card. Might be in a Deck object, depends on
your design
"""
# some code here
class Player:
def __init__(self):
self.hands = []
def deal(self):
"""add a random hand"""
self.hands.append(Hand([draw_random_card(), draw_random_card()]))
def split(self, hand):
"""split the given hand"""
self.hands.remove(hand)
self.hands += hand.split()
class Hand:
def __init__(self, cards):
self.cards = cards
def hit(self):
"""add a random card"""
self.cards.append(draw_random_card())
def split(self):
"""split and return a pair of Hand objects"""
return [Hand(self.cards[0], draw_random_card()),
Hand(self.cards[1], draw_random_card())]
Isn't that simpler?
In response to your comment:
You can refer to any specific hand as self.hands[0] or self.hands[1] within the Players class.
If you want to keep track of a particular hand, you can just pass the hand itself around instead of passing a character string referring to that hand. Like this:
def process_hand(hand):
"""do something to a hand of cards"""
h.hit()
print h.cards()
h.hit()
h = Hand(cards)
process_hand(h)
This is important: modifications you make to the hand in the function work on the actual hand itself. Why put the extra step of passing a string that you then have to look up?
Also note that information specific to each hand, such as the bet, should probably be stored in the Hand class itself.
If you are sure you want to refer to each hand with a specific name (and again, it's not necessary in this case), you just use a dictionary with those names as keys:
self.hands = {}
self.hands["hand1"] = Hand([card1, card2])
self.hands["hand2"] = Hand([card1, card2])
print self.hands["hand1"]
But again, there is probably no good reason to do this. (And note that this is very different than instantiating a new variable "dynamically". It would be a good idea to look into how dictionaries work).

Related

Swapping attribute of two instances

So I am trying to make a turn-based card game, and one of the cards, when played , can swap the hand of the "caster" and the "castee" (not sure if that's even a word). I created a Player class, and instances Jim, Pam, Dwight and Michael. Whenever that card is played I have to swap the hand attribute between two players.
class Player:
def __init__(self, name, hand=[]):
self.name = name
self.hand = [self.draw_card() for n in range(0, 2)]
I've tried creating a method which takes the "castee", which is the self in this case, and swaps hand with the caster, but it doesn't work. Here caster is the name of the other instance, e.g. it could be Michael.hand instead of caster.hand:
def hand_swap(self):
self.hand, caster.hand = caster.hand, self.hand
I've also tried the following with no luck:
def hand_swap(self):
myhand = self.hand.copy()
casterhand = caster.hand.copy()
setattr(caster, "hand", myhand)
setattr(self, "hand", casterhand)
After every loop, players and their hands are printed in a dictionary. The code doesn't throw up any errors, but no hand has been swapped and everything remains the same.
Is there something wrong with my code or is it not so straightforward when it comes to having one instance change another instance's attributes?
edit1: what "caster" is
edit2: how I check that it fails
If you add caster to hand_swap() you will achieve your goal.
def hand_swap(self, caster):
self.hand, caster.hand = caster.hand, self.hand
Then to swap hands
pam.hand_swap(michael)

Python dice class that returns individual rolls

I'm trying to make a Die() class, with a DiceShaker() subclass. The DiceShaker() subclass is supposed to take multiple copies of the Die() class, roll them, and then return the individual rolls. Here is my code for Die().
class Die(object):
'a class representing a Die'
def __init__(self, sides=6):
'initialize a die, default 6 sides'
self.sides = sides
def roll(self):
'roll the dice'
self.dieRoll = random.randint(1, self.sides)
def get(self):
'get the current value of the die'
return self.dieRoll
Here is my code for DiceShaker().
class DiceShaker(Die):
'DiceShaker class'
def __init__(self,dieCount=1, dieSides=6, dieList = [], dieList2 = []):
'initialize a dice shaker'
Die.__init__(self,sides = 6)
self.dieCount = dieCount
self.dieSides = dieSides
self.dieList = dieList
self.dieList2 = dieList2
def shake(self):
'shake all the dice in the shaker'
counter = 0
dieList = []
while counter != self.dieCount:
self.dieList.append(Die.roll(self))
counter = counter + 1
return self.dieList
def getIndividualRolls(self):
'get a lsit of integers of all the individual die rolls'
for items in self.dieList:
self.dieList2.append(Die.get(self))
return self.dieList2
getIndividualRolls() is supposed to return a list with all of the dice that were passed through shake(), but the list printed is always just one number.
Example:
d=DiceShaker(3)
d.shake()
[None, None, None]
d.getIndividualRolls()
[3, 3, 3]
I have shake() returning [none] just so I know that the correct number of dice are going through, but I can't figure out how why getIndividualRolls() keeps printing out duplicates. Can anyone help me figure out what I'm doing wrong?
This will be too long for a comment:
As an aside, you should really be learning on Python 3. Python 2 is on the way out. Everything below works in Python 3.
Why is your DiceShaker a subclass of Die? This makes no sense. Subclassing is for is a relationships. DiceShaker isn't a type of Die (just like a ShoppingCart isn't a type of CartItem). Your relationship is compositional. DiceShaker has Dies (which you reflect with your dieList). In this case dieList is all you need here. You don't need to inherit from Die.
The underlying reason why you get the same number rolled is because you inherited from die. By subclassing Die, you made DiceShaker behave like a Die. That means you could've called ds.roll() and ds.get() (where ds = DiceShaker()) and it would behave exactly the same as if you called it on a Die. This is effectively what you've done when you've wrote Dice.get(self). That's equivalent to self.get() (since DiceShaker extends Dice). But DiceShaker is only one Die, not multiple. So calling get() will always return the same thing. What you've effectively done is the following:
die = Die()
die.roll()
for other_die in self.dieList:
self.dieList2.append(die.get()) # note you're getting the same die each time
So to fix this, you don't need to inherit from Die. In fact you shouldn't. Instead compose DieShaker and Die. This means DieShaker should delegate to the Dies it contains (by calling get on them, not itself).
from random import randint
class Die:
def __init__(self, sides=6):
self.sides = sides
def roll(self):
self._rolled_value = randint(1, self.sides)
def get_value_rolled(self):
return self._rolled_value
class DieShaker:
def __init__(self, num_die=1, num_die_sides=6):
self.dice = [Die(num_die_sides) for _ in range(num_die)]
def shake(self):
for die in self.dice:
die.roll()
def get_values_rolled(self):
return [die.get_value_rolled() for die in self.dice]
Note how in get_values_rolled (equivalent to your DieShaker.getIndividualRolls), we call get_value_rolled (equivalent to your Die.get) on each Die that our shaker contains (in the list self.dice).
Also note that I cleaned up your code in the following ways:
It is convention in Python to use snake_case for variables/properties and functions
I renamed a few of the functions to make their relationship more clear (get_value_rolled and get_values_rolled)
I used list comprehensions (ex. [Die(num_die_sides) for _ in range(num_die)] is the same thing as your while loop that appends to your self.dieList, but is more pythonic)
I replaced while with a for. Typically in python you want to use a for when you're working with iterables (list list).
I removed some of the optional arguments to your DieShaker constructor. For your purposes, it doesn't make much sense to allow them to be passed in (If you want to wade into the more technical, there is a reason why you'd do this, but for your purposes now, don't). It looks like you may think you need everything to be a parameter to __init__, but you don't. Instead of this (which actually has a serious issue unrelated to the style/semantics):
Bad:
def __init__(self, num_die=1, die_sides=6, dice = []):
self.dice = dice
You probably should do this:
def __init__(self, num_die=1, die_sides=6):
self.dice = []
I removed your second list. You only need to maintain a list of die (since after calling roll() on each die, it will return the same thing for get_value() each time)
Your DieShaker class should just be a wrapper around a list of Die objects.
class Die(object):
'a class representing a Die'
def __init__(self, sides=6):
'initialize a die, default 6 sides'
self.sides = sides
# You should make sure that by the time someone has created a Die object,
# all the attributes they might want to access have been defined
self.roll()
def roll(self):
'roll the dice'
self.dieRoll = random.randint(1, self.sides)
# Note I got rid of Die.get
# Anyone who can call Die.get can see Die.dieRoll
class DiceShaker(object):
def __init__(self, dice=()): # Note we avoid mutable default arguments
self.dice = list(dice) # Presumably it would be nice to add/remove dice
def shake(self):
for die in self.dice:
die.roll()
# return self.get_dice()
# It doesn't really make sense for this to return anything
def get_dice(self):
return [die.dieRoll for die in self.dice]

How to get one object in a class to modify another in the same class in python?

I have a class in python, and I'd like to be able to do an operation to one object and have that operation in turn identify another object to change and in turn modify it. To make this concrete:
Say we have a bunch of locations (the objects), and each location has some things (call them elements). I would like to tell location 1 to move one of its elements to the right by location.move_right(element). It should then add that element to location 2. (in my actual problem, the "location" would need to calculate which location it will move to, and so I don't know a priori where it's going.)
Here is the working code I have. Within the class I've placed a dictionary that will hold all objects that have been created. I can imagine this could cause trouble for garbage collection etc. I suspect there is a better way. Is there?
class location(object):
locations = {}
def __init__(self, coordinate):
self.locations[coordinate]=self
self.coord = coordinate
self.elements = set()
def add_element(self, element):
self.elements.add(element)
def move_right(self, element):
self.elements.remove(element)
new_location = self.locations[self.coord+1]
new_location.add_element(element)
x = location(1)
y=location(2)
x.add_element('r')
x.move_right('r')

Variable referring to class members - Python

I'm trying to create a way to apply a prefix to an item which would modify the item's existing stats. For example in the code below I am trying to apply the 'huge' prefix to the 'jar' item. I'd like to make the code reusable so that I could have different prefixes ('fast', 'healthy') that would modify different item stats.
Is it possible to hold the name of a class member in a variable?
If so, is there any reason I shouldn't?
If not, what alternatives are there?
class Prefix(object):
def __init__(self, word, stat, valu):
self.word = word
self.stat = stat
self.valu = valu
class Item(object):
def __init__(self, name, size):
self.name = name
self.size = size
def apply_prefix(self, prefix):
self.prefix.stat += prefix.valu # <-- Here is my issue
self.name = prefix.word + ' ' + self.name
# My hope is to make the code reusable for any stat
def print_stats(self):
print self.name, self.size
def main():
jar = Item('jar', 10)
huge_prefix = Prefix('huge', 'size', 5)
jar.apply_prefix(huge_prefix)
jar.print_stats()
You're trying to dynamically refer to some attribute. You do that by using getattr. And if you want to set the attribute, well... that's setattr :)
def apply_prefix(self, prefix):
target_attr = getattr(self,prefix.stat) #dynamically gets attr
setattr(self,prefix.stat,target_attr+prefix.valu)
As to whether this is the best coding style: it depends. There are some instances that code is made more clear by use of getattr. Since right now you only have two stats, it seems excessive to need this kind of dynamic attribute referencing, since I could easily do:
bogus_prefix = Prefix('huge','bogus',3)
Which is a valid Prefix, but throws an AttributeError when I try to apply it. That's not the most straightforward thing to debug.
However, there are bonuses to the getattr approach: if you add more stats, you don't have to change a bit (haha) of code in Prefix.
Other alternatives? There are always options in Python. :-)
The way I'd do it is to make Prefix just a dict of word:value pairs. Then apply_prefix would loop over the word keys, updating as many values as I wanted in one shot. It's a similarly dynamic approach, but a bit more scalable.

Can I override a class function without creating a new class in Python?

I'm making a game in pygame and I have made an 'abstract' class that's sole job is to store the sprites for a given level (with the intent of having these level objects in a list to facilitate the player being moved from one level to another)
Alright, so to the question. If I can do the equivalent of this in Python(code curtesy of Java):
Object object = new Object (){
public void overriddenFunction(){
//new functionality
};
};
Than when I build the levels in the game I would simply have to override the constructor (or a class/instance method that is responsible for building the level) with the information on where the sprites go, because making a new class for every level in the game isn't that elegant of an answer. Alternatively I would have to make methods within the level class that would then build the level once a level object is instantiated, placing the sprites as needed.
So, before one of the more stanch developers goes on about how anti-python this might be (I've read enough of this site to get that vibe from Python experts) just tell me if its doable.
Yes, you can!
class Foo:
def do_other(self):
print('other!')
def do_foo(self):
print('foo!')
def do_baz():
print('baz!')
def do_bar(self):
print('bar!')
# Class-wide impact
Foo.do_foo = do_bar
f = Foo()
g = Foo()
# Instance-wide impact
g.do_other = do_baz
f.do_foo() # prints "bar!"
f.do_other() # prints "other!"
g.do_foo() # prints "bar!"
g.do_other() # prints "baz!"
So, before one of the more stanch developers goes on about how anti-python this might be
Overwriting functions in this fashion (if you have a good reason to do so) seems reasonably pythonic to me. An example of one reason/way for which you might have to do this would be if you had a dynamic feature for which static inheritance didn't or couldn't apply.
The case against might be found in the Zen of Python:
Beautiful is better than ugly.
Readability counts.
If the implementation is hard to explain, it's a bad idea.
Yes, it's doable. Here, I use functools.partial to get the implied self argument into a regular (non-class-method) function:
import functools
class WackyCount(object):
"it's a counter, but it has one wacky method"
def __init__(self, name, value):
self.name = name
self.value = value
def __str__(self):
return '%s = %d' % (self.name, self.value)
def incr(self):
self.value += 1
def decr(self):
self.value -= 1
def wacky_incr(self):
self.value += random.randint(5, 9)
# although x is a regular wacky counter...
x = WackyCount('spam', 1)
# it increments like crazy:
def spam_incr(self):
self.value *= 2
x.incr = functools.partial(spam_incr, x)
print (x)
x.incr()
print (x)
x.incr()
print (x)
x.incr()
print (x)
and:
$ python2.7 wacky.py
spam = 1
spam = 2
spam = 4
spam = 8
$ python3.2 wacky.py
spam = 1
spam = 2
spam = 4
spam = 8
Edit to add note: this is a per-instance override. It takes advantage of Python's attribute look-up sequence: if x is an instance of class K, then x.attrname starts by looking at x's dictionary to find the attribute. If not found, the next lookup is in K. All the normal class functions are actually K.func. So if you want to replace the class function dynamically, use #Brian Cane's answer instead.
I'd suggest using a different class, via inheritance, for each level.
But you might get some mileage out of copy.deepcopy() and monkey patching, if you're really married to treating Python like Java.

Categories