Python3: How to save data, when the destructor is called - python

I actually have a question on Python3. I want to save the attributes of a class in a file, when the destructor is called. How can I do that?
I tried to do it like this:
def __del__:
file = open("test.dat", "w")
file.write("Hello")
file.close()
That code doesn't work. I've already read, that you shouldn't use it, but I actually didn't find a alternative which works. Can anybody help me?
Thanks in advance!

to use that code it needs to be part of a class:
class Test():
def __init__(self):
self.data = "Hello"
def __del__(self):
with open('text.txt', mode='w') as file:
file.write(self.data)
if __name__ == '__main__':
def something():
obj = Test() # obj is deleted when function exits as it becomes out of scope
something()
obj = Test()
del obj # also works because we explicitly delete the object
note that while this method does work, it cannot always be relied upon to always be executed, for example if a sys.exit is called

Related

Polymorphism and with statements

Given a class that export .csv files to a database:
import luigi
import csv
class CsvToDatabase(luigi.Task):
# (...)
def run(self):
## (...)
with open(self.input().some_attribute, 'r', encoding='utf-8') as some_dataframe:
y = csv.reader(some_dataframe, delimiter=';')
### (...) <several lines of code>
# (...)
I'm having problems trying to export a file with ISO-8859-1 encoding.
When I exclude the encoding argument from then open() function, everything works fine, but I cannot make permanent changes in the class definition (firm's other sectors uses it). So I thinked about the possibility of using polymorphism to solve it, like:
from script_of_interest import CsvToDatabase
class LatinCsvToDatabase(CsvToDatabase):
# code that uses everything in `run()` except the `some_dataframe` definition in the with statement
This possibility actually exists? How could I handle it without repeating the "several lines of code" inside the statement?
Thank you #martineau and #buran for the comments. Based on them, I will request a change in the base class definition that didn't affect other sector's work. It would look like this:
import luigi
import csv
class CsvToDatabase(luigi.Task):
# (...)
encoding_param = luigi.Parameter(default='utf-8') # as a class attribute
# (...)
def run(self):
## (...)
with open(self.input().some_attribute, 'r', encoding=self.encoding_param) as some_dataframe:
y = csv.reader(some_dataframe, delimiter=';')
### (...) <several lines of code>
# (...)
And finally, in my script, something like:
from script_of_interest import CsvToDatabase
class LatinCsvToDatabase(CsvToDatabase):
pass
LatinCsvToDatabase.encoding_param = None
You might consider as an alternative modifying the original class to add a new method get_cvs_encoding, which is used by the run method:
class CsvToDatabase(luigi.Task):
...
def get_cvs_encoding(self):
# default:
return 'utf-8'
def run(self):
## (...)
with open(self.input().some_attribute, 'r', encoding=self.get_cvs_encoding()) as some_dataframe:
y = csv.reader(some_dataframe, delimiter=';')
...
}
And then subclass this as follows:
class MyCsvToDatabase(CsvToDatabase):
def get_cvs_encoding(self):
return 'ISO-8859-1' # or None
And use an instance of the subclass. I just think this is neater and you can have multiple subclass instances "running" concurrently.
After you posted an answer, I think it's better to post some alternatives.
So, here is your current class, minimal example
class A:
def run(self):
# some code
with open('some_file.csv' , encoding='utf-8'):
pass
# more code
My idea, suggested in the comments - change class A and add parameter encoding to A.run() with default value utf-8. This way the change will not affect others (their existing code that use class A).
class A:
def run(self, encoding='utf-8'):
# some code
with open('some_file.csv' , encoding=encoding):
pass
# more code
Then in your code
a=A() # create instance of A
a.run(encoding=None)
Now, your idea to add class attribute. This is how you decided to change class A:
class A:
encoding='utf-8'
def run(self):
# some code
with open('some_file.csv', encoding=self.encoding):
pass
# more code
I don't think you need to subclass. With this new class A you can do
a=A() # create instance of the new class A
a.encoding=None # note, in run() you use self.encoding and this will work. still A.encoding is utf-8 and others will not be affected.
And if you insist to subclass the new class A
class B(A):
encoding=None
Then in your code you can use instance of class B.

Access variable from Class in main

I'm trying to learn Python and I'm stuck with shared variables between classes.
I have a class that sets a variable like this:
class CheckStuff(threading.Thread):
def __init__(self, debug):
threading.Thread.__init__(self)
self.debug = debug
self.newGames = False
def get_new_games(self):
return self.newGames
def set_new_games(self, new_games):
self.newGames = new_games
def run(self):
# DO STUFF #
# ... #
self.set_new_games(True)
return
I would like to access new_games from my main, I tried like this:
if __name__ == "__main__":
debug = True
t1 = cs.CheckStuff(debug)
t1.start()
t1.join()
print(cs.CheckStuff(debug).get_new_games())
exit()
But this returns always False. Where am I wrong? Any hints appreciated
You're creating a new instance of CheckStuff, which hasn't done anything so its newGames attribute is False. The instance that did run is t1, you should use that:
print(t1.get_new_games())
(Note, there doesn't really seem to be a good reason to use threads here, but never mind.)
In the following line
print(cs.CheckStuff(debug).get_new_games())
you make a new instance of CheckStuff class, and in the constructor you set self.newGames = False. Then, by calling get_new_game() method you ask the object to return its newGame attribute, which is obviously False.

object has no attributes. New to classes in python

import praw
import time
class getPms():
r = praw.Reddit(user_agent="Test Bot By /u/TheC4T")
r.login(username='*************', password='***************')
cache = []
inboxMessage = []
file = 'cache.txt'
def __init__(self):
cache = self.cacheRead(self, self.file)
self.bot_run(self)
self.cacheSave(self, self.file)
time.sleep(5)
return self.inboxMessage
def getPms(self):
def bot_run():
inbox = self.r.get_inbox(limit=25)
print(self.cache)
# print(r.get_friends())#this works
for message in inbox:
if message.id not in self.cache:
# print(message.id)
print(message.body)
# print(message.subject)
self.cache.append(message.id)
self.inboxMessage.append(message.body)
# else:
# print("no messages")
def cacheSave(self, file):
with open(file, 'w') as f:
for s in self.cache:
f.write(s + '\n')
def cacheRead(self, file):
with open(file, 'r') as f:
cache1 = [line.rstrip('\n') for line in f]
return cache1
# while True: #threading is needed in order to run this as a loop. Probably gonna do this in the main method though
# def getInbox(self):
# return self.inboxMessage
The exception is:
cache = self.cacheRead(self, self.file)
AttributeError: 'getPms' object has no attribute 'cacheRead'
I am new to working with classes in python and need help with what I am doing wrong with this if you need any more information I can add some. It worked when it was all functions but now that I attempted to switch it to a class it has stopped working.
Your cacheRead function (as well as bot_run and cacheSave) is indented too far, so it's defined in the body of your other function getPms. Thus it is only accessible inside of getPms. But you're trying to call it from __init__.
I'm not sure what you're trying to achieve here because getPms doesn't have anything else in it but three function definitions. As far as I can tell you should just take out the def getPms line and unindent the three functions it contains so they line up with the __init__ method.
Here are few points:
Unless you're explicitly inheriting from some specific class, you can omit parenthesis:
class A(object):, class A():, class A: are equivalent.
Your class name and class method have the same name. I'm not sure does Python confuse about this or not, but you probably do. You can name your class PMS and your method get, for example, so you'll obtain PMS.get(...)
In the present version of indentation cacheRead and cacheSave functions are simply inaccessible from init; why not move them to generic class namespace?
When calling member functions, you don't need to specify self as the first argument since you're already calling the function from this object. So instead of cache = self.cacheRead(self, self.file) you have to do it like this: cache = self.cacheRead(self.file)

Accessing class file from multiple methods in Python

My question relates mostly to how you use the with keyword in a class in Python.
If you have a Class which contains a file object, how do you use the with statement, if at all.
For example, I don't use with here:
class CSVLogger:
def __init__(self, rec_queue, filename):
self.rec_queue = rec_queue
## Filename specifications
self.__file_string__ = filename
f = open(self.__file_string__, 'wb')
self.csv_writer = csv.writer(f, newline='', lineterminator='\n', dialect='excel')
If I then do things to the file in another method, For example:
def write_something(self, msg):
self.csv_writer(msg)
Is this suitable? Should I include the with somewhere? I'm just afraid that one the __init__ exits, the with exits and might close the file?
Yes, you are correct with automatically closes the file when its scope ends, so if you use with statement in your __init__() function, the write_something function would not work.
Maybe you can use the with statement in the main part of the program, and instead of opening the file in __init__() function you can pass in the file object as a parameter to the __init__() function. and then do all operations you would like to do in the file within the with block.
Example -
Class would look like -
class CSVLogger:
def __init__(self, rec_queue, filename, f):
self.rec_queue = rec_queue
## Filename specifications
self.__file_string__ = filename
self.csv_writer = csv.writer(f, newline='', lineterminator='\n', dialect='excel')
def write_something(self, msg):
self.csv_writer(msg)
The main program may look like -
with open('filename','wb') as f:
cinstance = CSVLogger(...,f) #file and other parameters
.... #other logic
cinstance.write_something("some message")
..... #other logic
Though if this complicates thing a-lot, you are better off not using the with statement and rather making sure that you close the file when when its need is over.

I have a Python code that is given below, when I run this global name PDBAttributes is not defined

#!/usr/bin/env python2.7
##-*- mode:python;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t;python-indent:2 -*-'
import noesy
import argparse
import library
parser =argparse.ArgumentParser(description="read pdb file",
add_help=True)
parser.add_argument("file",help="protein pdb file")
library.add_standard_args( parser )
args = parser.parse_args()
def read_structure(pdbfile):
struct=[]
for line in pdbfile:
if len(line):
struct.append(PDBAttributes.read_from_line(line))
return struct
pdb=read_structure(open(args.file,'r'))
class PDBAttributes:
def __init__(self, atomindex=1, atom=noesy.Atom(), atomx=1, atomy=1, atomz=1):
self._atomindex=atomindex
self._atom=atom
self._atomx=atomx
self._atomy=atomy
self._atomz=atomz
def __str__(self):
s='ATOM %(_atomindex)d %(_atom)s at %(_atomx)8.3f %(_atomy)8.3f %(_atomz)8.3f'%self.__dict__
return s
def atom(self):
return self._atom
def atomindex(self):
return self._atomindex
def atomx(self):
return self._atomx
def atomy(self):
return self._atomy
def atomz(self):
return self._atomz
#classmethod
def read_from_line(obj,line):
tags=line.split()
atomindex=int(tags[1])
atom=noesy.Atom(tags[2],int(tags[5]))
atomx=float(tags[6])
atomy=float(tags[7])
atomz=float(tags[8])
obj=PDBAttributes(atomindex, atom, atomx, atomy, atomz)
print obj
class AtomDistance(PDBAttributes):
def distance(self, atom1,atom2):
pass
The NameError you are getting is due to the order you have placed the code in your file.
When you call read_structure to create a value for the pdb variable, it tries to look for PDBAttributes, but it has not been defined yet. If you move that line lower down in the file (below the class definition) you'll avoid that error. Note that it is OK to have the declaration of read_structure above the PDBAttributes class definition, though you might want to move it lower too to make the code easier to understand.
Here's a very simple bit of code that demonstrates the same error:
def foo():
print(foo_text)
foo() # raises a NameError
foo_text = "foo"
Here's a fixed version:
def foo():
print(foo_text)
foo_text = "foo"
foo() # no error, prints "foo"
Move your call of read_structure to follow the definition of the PDBAttributes class.
Also, in the process of reformatting your post, I see that you have mixed tabs and spaces for your indentation. Try reformatting your code to use all spaces for indentation, the recommended form is 4-space indents.
Your definition of all those getter functions looks like Java written in Python - this is a lot of extra code that is often unnecessary in Python. The recommended approach is to omit these all-they-do-is-assign-a-value-to-an-attribute-with-the-same-name-but-with-a-leading-underscore methods and just use attributes with the public names. See Python is Not Java.

Categories