Confusion in order of Destructor call in Python - python

I was going through this website http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python, I wrote the exactly similar code.
But in my code, the destructor is getting called once the object goes out of scope. But the code mentioned in the link above, the destructor gets called after the end of the code. How?
Here are codes;
Code from the link
class FooType(object):
def __init__(self, id):
self.id = id
print self.id, 'born'
def __del__(self):
print self.id, 'died'
def make_foo():
print 'Making...'
ft = FooType(1)
print 'Returning...'
return ft
print 'Calling...'
ft = make_foo()
print 'End...'
Output is :
Calling...
Making...
1 born
Returning...
End...
1 died <----- Destructor called
My Code:
abc = [1,2,3]
class myclass(object):
def __init__(self):
print "const"
abc = [7,8,9]
a = 4
def __del__(self):
print "Dest"
def hello():
abc = [4,5]
print abc
my = myclass()
print my.abc, my.a
print "I am before Dest"
return "Done"
ret = hello()
print ret
print abc
output:
[4, 5]
const
[7, 8, 9] 4
I am before Dest
Dest<---------- Destructor
Done
[1, 2, 3]

Because the object is returned by the function it is still in scope in the main program. In your example the object never leaves the function, so it goes out of scope when the function returns.

Related

Python - Log the things a string has previously been

If this is my code:
x = 1
x = 2
x = 3
How can I “log” the things x has been and print them? If my explanation was dumb, then here’s what I expect:
>>> # Code to print the things x has been
1, 2, 3
>>>
How can I achieve this?
Since assignment overwrites the value of the object (in your example 'x'), it is not possible to do exactly what you want. However, you could create an object, of which the value can be changed and its history remembered. For example like this:
#!/usr/bin/env/python3
class ValueWithHistory():
def __init__(self):
self.history = []
self._value = None
#property
def value(self):
return self._value
#value.setter
def value(self, new_value):
self.history.append(new_value)
self._value = new_value
def get_history(self):
return self.history
def clear_history(self):
self.history.clear()
def main():
test = ValueWithHistory()
test.value = 1
print(test.value)
test.value = 2
print(test.value)
test.value = 3
print(test.value)
print(test.get_history())
if __name__ == '__main__':
main()
This prints:
1
2
3
[1, 2, 3]
Of course, you could also use a set instead of a list to only remember each unique value once, for example.
You can order a second thread to observe the string and print the changes:
from threading import Thread
def string_watcher():
global my_string
global log
temp = ''
while True:
if my_string != temp:
log.append(my_string)
temp = my_string
t = Thread(target=string_watcher, daemon=True)
t.start()
This checks weather the string „my_string“ was manipulated and appends it to the list „log“, if it has been changed. With this you should be able to perform
Print(log)
At any moment of the runtime

output is not printing string in python

i have made a program but the output that i'm getting is
(<q3v3.Student instance at 0x023BB620>, 'is doing the following modules:', ' <q3v3.Module instance at 0x023BB670> <q3v3.Module instance at 0x023BB698>')
For example , the above output should give me Alice is doing following module : biology, chemistry
Help
this is my full code:
class Student :
def __init__(self,students):
self.students= students
print self.students
#def __str__(self): # when i used this i've got error type TypeError: __str__ returned non-string (type NoneType)
#print str(self.students)
class Module:
def __init__(self,modules):
self.modules = modules
print self.modules
#def __str__(self):
#print str(self.modules)
class Registrations (Student,Module):
def __init__(self):
self.list= []
self.stulist = []
self.modulist= []
def __iter__(self):
return iter(self.list)
def __str__(self):
return str(self.list)
def add(self,students,modules):
self.list.append((students,modules))
#print (self.list)
def students(self,modules):
for i in self.list:
if i[1] == modules:
self.modulist.append((i[0]))
return iter(self.modulist)
def __str__(self):
return str(self.students)
def modules(self,students):
for i in self.list:
if i[0] == students:
self.stulist.append((i[1]))
return iter(self.stulist)
def __str__(self):
return str(self.modules)
i need to import my program to be able to run it to this :
from q3v4 import *
james = Student('james')
alice = Student('alice')
mary = Student('mary')
agm = Module('agm')
ipp = Module('ipp')
r = Registrations()
r.add(james,agm)
r.add(alice,agm)
r.add(alice,ipp)
mstr = ''
for m in map(str,r.modules(alice)):
mstr = mstr+' '+m
print(alice, 'is doing the following modules:', mstr)
sstr = ''
for s in map(str,r.students(agm)):
sstr = sstr+' '+s
print(agm, 'has the following students:', sstr)
print(r)
You could define a __str__ method in your Student class, and do something like this:
def __str__(self):
return self.name # Here the string you want to print
Are you using Python 2? If so, print is a keyword, not a function. There are two ways to solve your problem:
Write print foo, bar instead of print(foo, bar).
The difference is that print(foo, bar) is actually printing out the tuple (foo, bar), which uses the repr() representation of each element, rather than its str().
At the very top of your file, write from __future__ import print_function. This will magically convert print from a keyword into a function, causing your code to work as expected.
If you are using Python 3, my answer is irrelevant.

Creating new object in python is not returning a new object

I am new to python.
Why am I not getting a new object when I call tempMyObject = myObject()?
class myObject(object):
x = []
def getMyObject():
tempMyObject = myObject()
print "debug: %s"%str(tempMyObject.x)
tempMyObject.x.append("a")
return tempMyObject
#run
a = getMyObject()
b = getMyObject()
My debug prints out:
debug: []
debug: ["a"]
I don't understand why both of these debug arrays are not null, can someone please enlighten me?
EDIT: I found the mistake i put in python code on my post. I am using the .append("a") in my function
You have created x as a class variable rather than an instance variable. To associate the variable with a particular instance of a class, do something like this:
class myObject(object):
def __init__(self): # The "constructor"
self.x = [] # Assign x to this particular instance of myObject
>>> debug: []
>>> debug: []
For a little better explanation of what's going on, have a look at this little mockup that demonstrates the same thing, a little more explicitly (if also more verbosely).
class A(object):
class_var = [] # make a list attached to the A *class*
def __init__(self):
self.instance_var = [] # make a list attached to any *instance* of A
print 'class var:', A.class_var # prints []
# print 'instance var:', A.instance_var # This would raise an AttributeError!
print
a = A() # Make an instance of the A class
print 'class var:', a.class_var # prints []
print 'instance var:', a.instance_var # prints []
print
# Now let's modify both variables
a.class_var.append(1)
a.instance_var.append(1)
print 'appended 1 to each list'
print 'class var:', a.class_var # prints [1]
print 'instance var:', a.instance_var # prints [1]
print
# So far so good. Let's make a new object...
b = A()
print 'made new object'
print 'class var:', b.class_var # prints [1], because this is the list bound to the class itself
print 'instance var:', b.instance_var # prints [], because this is the new list bound to the new object, b
print
b.class_var.append(1)
b.instance_var.append(1)
print 'class var:', b.class_var # prints [1, 1]
print 'instance var:', b.instance_var # prints [1]
There a few bits missing in your code, like the class initialiser first and foremost. The correct code is as follows:
class myObject(object):
def __init__(self):
self.x=[] #Set x as an attribute of this object.
def getMyObject():
tempMyObject = myObject()
print "debug: %s"%str(tempMyObject.x) #Just after object initialisation this is an empty list.
tempMyObject.x = ["a"]
print "debug2: %s"%str(tempMyObject.x) #Now we set a value to it.
return tempMyObject
#run
a = getMyObject()
b = getMyObject()
Now the debug will first print out an empty list and then, once it was set, "a". Hope this helps. I recommend looking at basic python classes tutorial.

Why does my synchronous job code does not work

I have wrote below code segment to initiate function in every 2 seconds. But, it seems it is not work. Why?
from threading import Timer
class A :
value = None
def AX(self):
value = 12
obj = B()
Timer(1,obj.BY, [self.value]).start()
class B:
def BY(self,value):
print "refreshed :", value
if __name__=='__main__':
obj = A()
obj.AX()
You also need to set value on self, or use the local variable as the argument:
def AX(self):
self.value = 12
obj = B()
Timer(1, obj.BY, [self.value]).start()
or:
def AX(self):
value = 12
obj = B()
Timer(1, obj.BY, [value]).start()
This prints:
>>> A().AX()
>>> refreshed : 12
Note that the Timer() instance will only call obj.BY once, it doesn't repeatedly call the function.
You need to reset the timer every time BY is called to do that:
class B:
def BY(self,value):
print "refreshed :", value
Timer(2, self.BY, [value]).start()
which will create a loop. If you need that loop to terminate at some point, you'll need to test for that condition separately, in BY:
class B:
somecondition = False
def BY(self,value):
print "refreshed :", value
if self.somecondition:
return # do not set the timer again
Timer(2, self.BY, [value]).start()

how to use 'pickle'

my code(i was unable to use 'pickle'):
class A(object):
def __getstate__(self):
print 'www'
return 'sss'
def __setstate__(self,d):
print 'aaaa'
import pickle
a = A()
s = pickle.dumps(a)
e = pickle.loads(s)
print s,e
print :
www
aaaa
ccopy_reg
_reconstructor
p0
(c__main__
A
p1
c__builtin__
object
p2
Ntp3
Rp4
S'sss'
p5
b. <__main__.A object at 0x00B08CF0>
who can tell me how to use.
What are you trying to do? It works for me:
class A(object):
def __init__(self):
self.val = 100
def __str__(self):
"""What a looks like if your print it"""
return 'A:'+str(self.val)
import pickle
a = A()
a_pickled = pickle.dumps(a)
a.val = 200
a2 = pickle.loads(a_pickled)
print 'the original a'
print a
print # newline
print 'a2 - a clone of a before we changed the value'
print a2
print
print 'Why are you trying to use __setstate__, not __init__?'
print
So this will print:
the original a
A:200
a2 - a clone of a before we changed the value
A:100
If you need setstate:
class B(object):
def __init__(self):
print 'Perhaps __init__ must not happen twice?'
print
self.val = 100
def __str__(self):
"""What a looks like if your print it"""
return 'B:'+str(self.val)
def __getstate__(self):
return self.val
def __setstate__(self,val):
self.val = val
b = B()
b_pickled = pickle.dumps(b)
b.val = 200
b2 = pickle.loads(b_pickled)
print 'the original b'
print b
print # newline
print 'b2 - b clone of b before we changed the value'
print b2
which prints:
Why are you trying to use __setstate__, not __init__?
Perhaps __init__ must not happen twice?
the original b
B:200
b2 - b clone of b before we changed the value
B:100
You are able to pickle (meaning, this code works as it should). You just seem to get a result, you don't expect. If you expect the same 'output', try:
import pickle
a = A()
s = pickle.dumps(a)
e = pickle.loads(s)
print s, pickle.dumps(e)
Your example isn't, well, a typical 'pickling' example. Usually pickled objects are saved somewhere persistently or sent over the wire. See e.g. pickletest.py: http://www.sthurlow.com/python/lesson10/.
There are advanced uses of pickling, see for example David Mertz XML object serialisation article: http://www.ibm.com/developerworks/xml/library/x-matters11.html
In a nutshell, in your example, e equals a.
Don't have to care about these strang strings, you can dumps these strings to save to anywhere, just remember when you loads them, you got 'a' object again.

Categories