Same Python object created on each iteration [duplicate] - python

This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 7 months ago.
I have code that creates a "Box" object, with a unique number assigned to each Box, then a unique number assigned as the contents for each Box. multiple boxes then make up a "Room" object.
Here's the thing, when I run multiple iterations to create a new Room object, each room iteration creates the exact same room object. By running this code, you see that box 0 prints the same for every iteration. Why is that, and how can I get a unique combination of box contents on each iteration?
import random
class Box:
def __init__(self, box_number, box_content):
self.number = box_number
self.content = box_content
def __str__(self):
return f"Box: {self.number} | Content: {self.content}"
class Room:
boxes = []
def __init__(self, num_of_boxes):
box_contents = [*range(num_of_boxes)]
random.shuffle(box_contents)
for i in range(num_of_boxes):
self.boxes.append(Box(i + 1, box_contents[i] + 1))
def run_cycle():
room = Room(5)
print(room.boxes[0])
for _ in range(5):
run_cycle()

class Room:
def __init__(self, num_of_boxes):
self.boxes = []
...
def 'boxes' inside init belong to an instance.
Your code's all Room instance has same box attrib which belong to class.

Related

Is it possible for objects of the same type/class to have different data attributes? [duplicate]

This question already has answers here:
One int for every python object [duplicate]
(3 answers)
Closed 4 years ago.
Sorry, if the title seemed a little imprecise.
I'm wondering if there is way of making sure that every instance of an object can have a unique serial number?
class Airplane:
def __init__(self, name, passenger_hold):
self.name = name
self.passenger_hold = passenger_hold
airplane1 = Airplane("Airbus A320", 100)
airplane2 = Airplane("Boeing 747", 250)
How can I make sure that the first airplane has the serial number 0, the second one 1 and so on?
Use a global counter, internal to your class that would give you a new value each time the constructor is called.
class Airplane:
counter = 0
def __init__(self, name, passenger_hold):
self.name = name
self.passenger_hold = passenger_hold
self.serial = Airplane.counter
Airplane.counter += 1

Python: Use a variable when calling a definition [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 7 years ago.
I'm currently making a game for CompSci class, and I want to shorten our random monster fights. Is there a way to make it so when I call a def I can change the name depending on a random variable? This is the snippet I'm talking about
Loop = True
MonsterType = random.randint(1,20)
Monster*()
battle()
I have
def Monster1
def Monster2
def Monster3
.
.
.
def Monster20
I want the * in the first snippet to be the variable MonsterType, is there a way to have it do that? I.e. When it runs, if MonsterType = 15, then it'll be Monster13() that's called.
Yes there is a way, Create a dictionary and map each function to an integer:
import random
def monster1():
print "Hello monster1"
def monster2():
print "Hello monster2"
def monster3():
print "Hello monster3"
def monster4():
print "Hello monster4"
d = {1:monster1, 2:monster2, 3:monster3, 4:monster4}
#When you need to call:
monsterType = random.randint(1, 4)
d[monsterType]()
After reading what you're trying to accomplish, I believe that using OOP is going to be better for your situation:
# A class for a bunch of monsters
class Mob(object):
def __init__(self, num_monsters):
self._num_monsters = num_monsters
self._monsters = []
for i in range(self._num_monsters):
# randomly create a type of monster based on the number of monsters
self._monsters.append(Monster(random.randint(0, self._num_monsters)))
# Each monster has a type that is attacks with
class Monster(object):
def __init__(self, monster_type):
self._type = monster_type
def attack(self):
print(self._type)
test = Mob(20)
sounds like you you use a bit of OOP. I would recommend:
class Monster(object):
def __init__(self, name):
self.name = name
def do_work(self):
print self.name
then create a manager that can generate n number of monster objects, and provide a static method that can manage what object to call:
class MonsterManager(object):
monsters = []
def call_monster(self, index):
monsters[i].do_work()
with this, you can have a better useage, like so:
manager = MonsterManager();
manager.monsters.append(Monster('Monster1')
manager.monsters.append(Monster('Monster2')
# etc
# then call a single monster
manager.call_monster(i)

Create classes in a loop

I want to define a class and then make a dynamic number of copies of that class.
Right now, I have this:
class xyz(object):
def __init__(self):
self.model_type = ensemble.RandomForestClassifier()
self.model_types = {}
self.model = {}
for x in range(0,5):
self.model_types[x] = self.model_type
def fit_model():
for x in range(0,5):
self.model[x] = self.model_types[x].fit(data[x])
def score_model():
for x in range(0,5):
self.pred[x] = self.model[x].predict(data[x])
I want to fit 5 different models but I think Python is pointing to the same class 5 times rather than creating 5 different classes in the model dictionary.
This means that when I use the "score_model" method, it is just scoring the LAST model that was fit rather than 5 unique models.
I think that I just need to use inheritance to populate the model[] dictionary with 5 distinct classes but I'm not sure how to do that?
In your orignal code, you created one instance and used it five times. Instead, you want to initialize the class only when you add it to the model_types array, as in this code.
class xyz(object):
def __init__(self):
self.model_type = ensemble.RandomForestClassifier
self.model_types = {}
self.model = {}
for x in range(0,5):
self.model_types[x] = self.model_type()
def fit_model():
for x in range(0,5):
self.model[x] = self.model_types[x].fit(data[x])
def score_model():
for x in range(0,5):
self.pred[x] = self.model[x].predict(data[x])
In python everything is an object, so your variable can point to a class as well, and then your variable can be treated as a class.

Python OOP, using loops to number objects as they are created

I'm stumped on a python problem. I'm writing a program that receives a command from Scratch (MIT) and then should create a new object, in this case named PiLight. The object only need to be created when the command is received so it doesn't have to loop, just be able to executed repeatedly and have the number increment each time it is executed.A list will not work for me due to the requirements of the program and talking between Scratch. I'm trying to figure out a way for the constructor, once initialized, to print out a statement something like
class Newpilight:
def __init__(self):
print "Pilight" + pilnumber + " created"
pilnumber should be 1 for 1st object, 2 for 2nd, etc
From there I need the creation of the object to change the number in the name of the object as well
PiLight(PiLnumber) = Newpilight()
I tried messing around with for loops but just ended up making more of a mess
Use number generator as class variable
from itertools import count
class NewPilight(object):
nums = count()
def __init__(self):
self.num = self.nums.next()
print "Pilight {self.num} created".format(self=self)
Then using in code:
>>> pl1 = NewPilight()
Pilight 0 created
>>> pl2 = NewPilight()
Pilight 1 created
>>> pl3 = NewPilight()
Pilight 2 created
>>> pl3.num
2
The trick is to have the nums (what is actually a generator of numbers, not list of numbers) as class property and not property of class instance. This way it is globally shared by all class instances.
class NewPilight:
def __init__(self, number):
self.number = number
print "Pilight" + number + " created"
for x in range(5):
NewPilight(x)
if you need to keep objects:
all_pilights = []
for x in range(5):
all_pilights.append( NewPilight(x) )
and now you have access to objects as
print all_pilights[0].number
print all_pilights[1].number
print all_pilights[2].number
class NewPiLight(object):
global_pilnumber = 0 # Since this is on the class definition, it is static
def __init__(self):
print "Pilight %s created" % NewPiLight.global_pilnumber
self.pilnumber = NewPiLight.global_pilnumber # Set the variable for this instance
NewPiLight.global_pilnumber += 1 # This increments the static variable

Creating dynamic variables for a class from within the class

For context, I'm working on an inventory system in an RPG, and I'm prototyping it with python code.
What I don't understand is how to make separate variables for each instance of an item without declaring them manually. For a short example:
class Player(object):
def __init__(self):
self.Items = {}
class Item(object):
def __init__(self):
self.Equipped = 0
class Leather_Pants(Item):
def __init__(self):
#What do i place here?
def Pick_Up(self, owner):
owner.Items[self.???] = self #What do i then put at "???"
def Equip(self):
self.Equipped = 1
PC = Player()
#Below this line is what i want to be able to do
Leather_Pants(NPC) #<-Create a unique instance in an NPC's inventory
Leather_Pants(Treasure_Chest5) #Spawn a unique instance of pants in a treasure chest
Leather_Pants1.Pick_Up(PC) #Place a specific instance of pants into player's inventory
PC.Items[Leather_Pants1].Equip() #Make the PC equip his new leather pants.
If I did something silly in the above code, please point it out.
What I want to do if the code doesn't make it clear is that I want to be able to dynamically create variables for all items as I spawn them, so no two items will share the same variable name which will serve as an identifier for me.
I don't mind if I have to use another class or function for it like "Create_Item(Leather_Pants(), Treasure_Chest3)"
What's the best way to go about this, or if you think I'm doing it all wrong, which way would be more right?
As a general rule, you don't want to create dynamic variables, and you want to keep data out of your variable names.
Instead of trying to create variables named pants0, pants1, etc., why not just create, say, a single list of all leather pants? Then you just do pants[0], pants[1], etc. And none of the other parts of your code have to know anything about how the pants are being stored. So all of your problems vanish.
And meanwhile, you probably don't want creating a Leather_Pants to automatically add itself to the global environment. Just assign it normally.
So:
pants = []
pants.append(Leather_Pants(NPC))
pants.append(Leather_Pants(chests[5]))
pants[1].pickup(PC)
The pants don't have to know that they're #1. Whenever you call a method on them, they've got a self argument that they can use. And the player's items don't need to map some arbitrary name to each item; just store the items directly in a list or set. Like this:
class Player(object):
def __init__(self):
self.Items = set()
class Item(object):
def __init__(self):
self.Equipped = 0
class Leather_Pants(Item):
def __init__(self):
pass # there is nothing to do here
def Pick_Up(self, owner):
self.owner.Items.add(self)
def Equip(self):
self.Equipped = 1
Abernat has tackled a few issues, but I thought I weigh in with a few more.
You appear to be using OOP, but are mixing a few issues with your objects. For example, my pants don't care if they are worn or not, I care though for a whole host of reasons. In python terms the Pants class shouldn't track whether it is equipped (only that it is equippable), the Player class should:
class CarryableItem:
isEquipable = False
class Pants(CarryableItem):
isEquipable = True
class Player:
def __init__(self):
self.pants = None # Its chilly in here
self.shirt = None # Better take a jumper
self.inventory = [] # Find some loot
def equip(self,item):
if is.isEquipable:
pass # Find the right slot and equip it,
# put the previously equipped item in inventory, etc...
Also, its very rare that an item will need to know who its owner is, or that its been grabbed, so verbs like that again should go onto the Player:
class Player:
maxCarry = 10
def take(Item):
if len(inventory) < maxCarry:
inventory.append(item)
Lastly, although we've tried to move most verbs on to actors which actually do things, sometimes this isn't always the case. For example, when instantiating a chest:
import random
class StorageItem:
pass
class Chest(StorageItem):
__init__(self):
self.storage = random.randint(5)
self.loot = self.spawnLoot()
def spawnLoot(self):
for i in range(self.storge):
# Lets make some loot
item = MakeAnItem # Scaled according to type level of dungeon, etc.
loot.append(item)
def remove(item):
self.loot[self.loot.index(item)]=None
Now the question about what to do when a Player wants to plunder a chest?
class Player:
def plunder(storage):
for item in storage.loot:
# do some Ui to ask if player wants it.
if item is not None and self.wantsItem(item) or \
(self.name="Jane" and self.wantsItem(item) and self.doesntWantToPay):
self.take(item)
storage.remove(item)
edit: Answering the comment:
If you are curious about calculating armor class, or the like, that again is a factor of the user, not the item. For example:
class Player:
#property
def clothing(self):
return [self.pants,self.top]
#property
def armorClass(self):
ac = self.defence
for item in self.clothing:
def = item.armorClass
if self.race="orc":
if item.material in ["leather","dragonhide"]:
def *= 1.5 # Orcs get a bonus for wearing gruesome dead things
elif item.material in ["wool","silk"]:
def *= 0.5 # Orcs hate the fineries of humans
ac += def
return ac
pants = Pants(material="leather")
grum = Player(race="orc")
grum.equip(pants)
print grum.armorClass
>>> 17 # For example?

Categories