So I've been learning some Python by trying to code a game, and as I was making an object that moves between lists in other objects I appended the object into another list. Here is the code:
#Superclass for all materials
class Material():
def __init__(self,quantity,durability):
self.quantity=quantity
self.durability=durability
def damage(self,damage):
if damage>self.quantity:
damage=self.quantity
self.quantity=self.quantity-(damage-self.durability)
return damage
class Bio(Material):
def __init__(self,quantity):
super().__init__(quantity,0)
class Metal(Material):
def __init__(self,quantity):
super().__init__(quantity,40)
class Heap():
def __init__(self):
self.contents=[]
def deposit(self,material):
v=False
for i in self.contents:
if type(i) == type(material):
i.quantity=i.quantity+material.quantity
v=True
break
if v is False:
self.contents.append(material)
#Superclass for all organs
class Organ():
def __init__(self,material):
self.material=material
self.max=material.quantity
self.kill_me=False
def donate(self,amount,target,location): #Target has to be another Organ
stuff=self.material.damage(amount)
target.material.quantity=target.material.quantity+stuff
target.spill(location)
if self.material.quantity <= 0:
self.kill_me=True
def spill(self,location):
if self.material.quantity > self.max:
extra=self.material
extra.quantity=self.material.quantity-self.max
self.material.quantity=self.max
location.inhabitants[0].deposit(extra)
class Unit():
def __init__(self,organs,owner): #organs is a list of organ objects
self.organs=organs
self.owner=owner
self.kill_me=False
def cleanse(self):
for organ in self.organs:
if organ.kill_me == True:
self.organs.remove(organ)
def move(self,location,destination):
destination.inhabitants.append(self)
if self in destination.inhabitants and self in location.inhabitants:
print('Oh no')
else:
self.kill_me=True
class Location():
def __init__(self):
self.inhabitants=[]
self.inhabitants.append(Heap())
def cleanse(self):
for i in self.inhabitants:
if issubclass(type(i),Unit) == True and i.kill_me == True:
self.inhabitants.remove(i)
#Testing Code
crust=Location()
orbit=Location()
test=Organ(Bio(50))
twotest=Organ(Bio(50))
shuttle=Unit([test,twotest],True)
crust.inhabitants.append(shuttle)
shuttle.move(crust,orbit)
crust.cleanse()
for i in crust.inhabitants:
print(i)
if type(i) == Unit:
print(i.kill_me)
print('. . .')
for i in orbit.inhabitants:
print(i)
if type(i) == Unit:
print(i.kill_me)
print('. . .')
crust.inhabitants[1].kill_me=True
for i in crust.inhabitants:
print(i)
if type(i) == Unit:
print(i.kill_me)
print('. . .')
for i in orbit.inhabitants:
print(i)
if type(i) == Unit:
print(i.kill_me)
This is the output:
Oh no
<__main__.Heap object at 0x000002432F97B608>
<__main__.Unit object at 0x000002432F97B748>
False
. . .
<__main__.Heap object at 0x000002432F97B5C8>
<__main__.Unit object at 0x000002432F97B748>
False
. . .
<__main__.Heap object at 0x000002432F97B608>
<__main__.Unit object at 0x000002432F97B748>
True
. . .
<__main__.Heap object at 0x000002432F97B5C8>
<__main__.Unit object at 0x000002432F97B748>
True
I guess this happened because I didn't remove the "shuttle" from its previous location. However, trying to change the "kill_me" value changes both, rendering "cleanse" useless. However, I finished writing the possessing of the Unit.move location data (setting it to remove itself from the previous list) and it seems to have worked.
Regardless, I still have a few pressing questions:
How did that object become part of two lists?
Is there any practical use for this behavior?
Is there any built-in function that can allow an object to know what list it is nested in, or will I always have to feed that into the object's methods as I have been doing? (I've not been storing the location class using init & "self" because the location will likely change over time so I don't want to store an outdated version of it)
Finally, since I'm a beginner, is there any pressing thing in my code that I should definitely be aware of that will cause untold problems later or am I fine for now?
Related
If I have a Numeric Property in two of my screens that count how many times a person clicked the correct icon and I want to print it in a separate class how would I do this? I have tried using the print() function and using print(StringProperty(str())) but I dont get a number value printed. When I print the correct_counter in VerifyAA() and VerifyBB() the correct value is printed.
class A(Screen):
correct_counter1 = NumericProperty(0)
def verifyAA(self, *args):
if self.AA == V.Check1 or self.AA == V.Check2 or self.AA == V.Check3:
print("You got it!!!")
self.correct_counter1 = self.correct_counter1 + 1
print(self.correct_counter1)
self.ids.aa.disabled = True
class B(Screen):
correct_counter2 = NumericProperty(0)
def verifyBB(self, *args):
if self.BB == VV.Check1 or self.BB == VV.Check2 or self.BB == VV.Check3:
print("You got it!!!")
self.correct_counter2 = self.correct_counter2 + 1
print(self.correct_counter2)
self.ids.bb.disabled = True
class Answers(Screen):
print(A.correct_counter1)
print(StringProperty(str(B.correct_counter2)))
This is what gets printed respectively:
<NumericProperty name=correct_counter>
<StringProperty name=>
You need to understand the distinction between the definition of a class and an instance of a class. When you print A.correct_counter1 you print the NumericProperty object itself, defined at class level. That's the only sensible result - it wouldn't make sense for it to print the value of the property, because it doesn't have a value. For instance, if you wrote instance1 = A(correct_counter1=1); instance2 = A(correct_counter1=20) what value would you have expected print(A.correct_counter1) to print? The answer has to be neither, the value of the property is defined in each case for the class instance, not the class itself.
The correct solution is that you must print the value of the property via an instance of your class. For instance, if you write instance = A(); print(instance.correct_counter1) you will print a number like you expect. The best way to do this depends on the structure of your program and the relationship between the classes. Your example isn't a runnable program so it isn't possible to make a specific suggestion. If you have an actual example where you want to do this but can't work out how, post that as a new question.
To get the value of an attribute I need to call method.attribute.attribute instead of method.attribute, why is this? Calling method.attribute results in a memory address. How should/can I change my code to make method.attribute work?
Most issues regarding this center around calling print(f) instead of print(f())
class MyList:
"""stores a list and does other stuff eventualy"""
this_list = []
def __init__(self, *args):
for arg in args:
self.this_list.append(arg)
def print_list(self):
"""prints the atribute:"description" from the stored objects in the list"""
for x in range(len(self.this_list)):
print(MyClassObj(self.this_list[x]).description, sep="\n")
This is the code that is supposed to print the value of the attribute description
class MyClassObj:
"""test object to be stores in the "MyList" object."""
def __init__(self, description):
self.description = description
This is the object that contains the attribute I want to get.
class CallList:
"""creates the objects, lists and calls the print method"""
#staticmethod
def main():
test1, test2 = MyClassObj("Test1"), MyClassObj("Test2")
list1 = MyList(test1, test2)
list1.print_list()
Main() is called outside the above classes.
The output I get is
<__main__.MyClassObj object at 0x007908F0>
<__main__.MyClassObj object at 0x00790910>
Process finished with exit code 0
If i change line:
print(MyClassObj(self.this_list[x]).description.description, sep="\n")
I get the expected result:
Test1
Test2
Process finished with exit code 0
So the question is why and how should I alter my code?
in print_list self.this_list[x] is already a MyClassObj so MyClassObj(self.this_list[x]) creates a new MyClassObj having a MyClassObj as its description.
Because there is no way defined to convert a MyClassObj to a string for print Python uses a default conversion showing the memory address.
I am trying to remove an object from memory in python and I am coming across an object that it is not being removed. From my understanding if there is no references to the object the garbage collector will de-allocate the memory when it is run. However after I have removed all of the references if I run
bar = Foo()
print gc.get_referrers(bar)
del bar
baz = gc.collect()
print baz
I get a reply of
[< frame object at 0x7f1eba291e50>]
0
So how come does it not delete the object?
I get the same reply for all of the instances of objects if i do
bar = [foo() for i in range(0, 10)]
for x in range(0,len(bar))
baz = bar[x]
del bar[x]
print gc.get_referrers(baz)
How do I completely remove all referrers from an object/any idea what the frame object that is on all is?
I thought it would be the object frame(?) that contains a list of all objects in the program but I have not been able to confirm that/find a way to rid objects from being referenced by said mystical(to me) object fram.
Any help would be greatly appreciated
Edit:
Okay I rewrote the code to the simple form pulling out everything except the basics
import random, gc
class Object():
def __init__(self):
self.n=None
self.p=None
self.isAlive=True
def setNext(self,object):
self.n=object
def setPrev(self, object):
self.p=object
def getNext(self):
return self.n
def getPrev(self):
return self.p
def simulate(self):
if random.random() > .90:
self.isAlive=False
def remove(self):
if self.p is not None and self.n is not None:
self.n.setPrev(self.p)
self.p.setNext(self.n)
elif self.p is not None:
self.p.setNext(None)
elif self.n is not None:
self.n.setPrev(None)
del self
class Grid():
def __init__(self):
self.cells=[[Cell() for i in range(0,500)] for j in range(0,500)]
for x in range(0,100):
for y in range(0,100):
for z in range(0,100):
self.cells[x][y].addObject(Object())
def simulate(self):
for x in range(0,500):
for y in range(0,500):
self.cells[x][y].simulate()
num=gc.collect()
print " " + str(num) +" deleted today."
class Cell():
def __init__(self):
self.objects = None
self.objectsLast = None
def addObject(self, object):
if self.objects is None:
self.objects = object
else:
self.objectsLast.setNext(object)
object.setPrev(self.objectsLast)
self.objectsLast = object
def simulate(self):
current = self.objects
while current is not None:
if current.isAlive:
current.simulate()
current = current.getNext()
else:
delete = current
current = current.getNext()
if delete.getPrev() is None:
self.objects = current
elif delete.getNext() is None:
self.objectsLast = delete.getPrev()
delete.remove()
def main():
print "Building Map..."
x = Grid()
for y in range (1,101):
print "Simulating day " + str(y) +"..."
x.simulate()
if __name__ == "__main__":
main()
gc.get_referrers takes one argument: the object whose referers it should find.
I cannot think of any circumstance in which gc.get_referrers would return no results, because in order to send an object to gc.get_referrers, there has to be a reference to the object.
In other words, if there was no reference to the object, it would not be possible to send it to gc.get_referrers.
At the very least, there will be a reference from the globals() or from the current execution frame (which contains the local variables):
A code block is executed in an execution frame. An execution frame contains some administrative information (used for debugging), determines where and how execution continues after the code block's execution has completed, and (perhaps most importantly) defines two namespaces, the local and the global namespace, that affect execution of the code block.
See an extended version of the example from the question:
class Foo(object):
pass
def f():
bar = [Foo() for i in range(0, 10)]
for x in range(0, len(bar)):
# at this point there is one reference to bar[x]: it is bar
print len(gc.get_referrers(bar[x])) # prints 1
baz = bar[x]
# at this point there are two references to baz:
# - bar refernces it, because it is in the list
# - this "execution frame" references it, because it is in variable "baz"
print len(gc.get_referrers(bar[x])) # prints 2
del bar[x]
# at this point, only the execution frame (variable baz) references the object
print len(gc.get_referrers(baz)) # prints 1
print gc.get_referrers(baz) # prints a frame object
del baz
# now there are no more references to it, but there is no way to call get_referrers
f()
How to test it properly?
There is a better trick to detect whether there are referers or not: weakref.
weakref module provides a way to create weak references to an object which do not count. What it means is that even if there is a weak reference to an object, it will still be deleted when there are no other references to it. It also does not count in the gc.get_referrers.
So:
>>> x = Foo()
>>> weak_x = weakref.ref(x)
>>>
>>> gc.get_referrers(x) == [globals()] # only one reference from global variables
True
>>> x
<__main__.Foo object at 0x000000000272D2E8>
>>> weak_x
<weakref at 0000000002726D18; to 'Foo' at 000000000272D2E8>
>>> del x
>>> weak_x
<weakref at 0000000002726D18; dead>
The weak reference says that the object is dead, so it was indeed deleted.
Okay thanks to cjhanks and user2357112 I came up with this answer
The problem being that if you run the program the gc does not collect anything after each day even though there were things deleted
To test if it is deleted I instead run
print len(gc.get_objects())
each time I go through a "day" doing this shows how many objects python is tracking.
Now with that information and thanks to a comment I tired changing Grid to
class Grid():
def __init__(self):
self.cells=[[Cell() for i in range(0,500)] for j in range(0,500)]
self.add(100)
def add(self, num):
for x in range(0, 100):
for y in range(0, 100):
for z in range(0, num):
self.cells[x][y].addObject(Object())
def simulate(self):
for x in range(0,500):
for y in range(0,500):
self.cells[x][y].simulate()
num=gc.collect()
print " " + str(num) +" deleted today."
print len(gc.get_objects())
and then calling Grid.add(50) halfway through the process. My memory allocation for the program did not increase (watching top in Bash) So my learning points:
GC was running without my knowledge
Memory is allocated and never returned to system until the the program is done
Python will reuse the memory
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()
I have seen other examples of this happening on StackOverflow, but I didn't understand any of the answers (I'm still a new programmer,) nor did the other examples I saw look quite like mine, else I wouldn't post this question.
I'm running Python 3.2 on Windows 7.
I have never had this happen to me before and I've done classes this way many times, so I don't really know what is different this time. The only difference is that I didn't make all of the Class file; I was given a template to fill in and a test file to try it on. It worked on the test file, but is not working on my file. I have been calling on the methods in the class in the exact same way as the test file (e.g. Lineup.size())
This is my Class:
class Queue:
# Constructor, which creates a new empty queue:
def __init__(self):
self.__items = []
# Adds a new item to the back of the queue, and returns nothing:
def queue(self, item):
self.__items.insert(0,item)
return
# Removes and returns the front-most item in the queue.
# Returns nothing if the queue is empty.
def dequeue(self):
if len(self.__items) == 0:
return None
else:
return self.__items.pop()
# Returns the front-most item in the queue, and DOES NOT change the queue.
def peek(self):
if len(self.__items) == 0:
return None
else:
return self.__items[(len(self.__items)-1)]
# Returns True if the queue is empty, and False otherwise:
def is_empty(self):
return len(self.__items) == 0
# Returns the number of items in the queue:
def size(self):
return len(self.__items)
# Removes all items from the queue, and sets the size to 0:
def clear(self):
del self.__items[0:len(self.__items)]
return
# Returns a string representation of the queue:
def __str__(self):
return "".join(str(i) for i in self.__items)
This is my program:
from queue import Queue
Lineup = Queue()
while True:
decision = str(input("Add, Serve, or Exit: ")).lower()
if decision == "add":
if Lineup.size() == 3:
print("There cannot be more than three people in line.")
continue
else:
person = str(input("Enter the name of the person to add: "))
Lineup.queue(person)
continue
elif decision == "serve":
if Lineup.is_empty() == True:
print("The lineup is already empty.")
continue
else:
print("%s has been served."%Lineup.peek())
Lineup.dequeue()
continue
elif (decision == "exit") or (decision == "quit"):
break
else:
print("%s is not a valid command.")
continue
And this is my error message when I enter "add" as my decision variable:
line 8, in
builtins.AttributeError: 'Queue' object has no attribute 'size'
So, what is going on here? What is different about this one?
Python 3 already has a queue module (which you might want to take a look at). When you import queue, Python finds that queue.py file before it finds your queue.py.
Rename your queue.py file to my_queue.py, change your import statements to from my_queue import Queue, and your code will work as you intend.
try rename size for other name or implement a counter to the list __items some like
def get_size(self):
cnt = 0
for i in self.__items:
cnt++
return cnt