I've got a class that manages a video camera and file stream. Each instance of the class is a frame that has a bunch of methods, attributes, etc. I'm doing all the initialization outside a function so it happens once and sets a few variables that are global to all instances of the class. Those global variables include Video and File objects that i need to close properly at shutdown from my main program.
I'm newish to Python and mostly Java/C/C++ background and I remember having functions that were associated with the class that i could call that accessed global (static) class variables. Can i do something similar in Python?
Here is my pseudo code for my class:
class MyClass:
global_variable = 1
openFile = file.open()
def someMethod( self ):
do stuff
def cleanUpStuff():
self.openFile.release()
Now in my main program I need to call cleanUpStuff() to close out my camera and files properly. I'm creating hundreds of instances of MyClass objects that I use and get destroyed by the garbage collection at some point. So i need a way to call at the Class level.
while True:
a = MyClass()
a.someMethod()
if something:
break
MyClass.cleanUpStuff()
Thanks to John this appears to work exactly like i want. I didn't know about "decorators" so i have no idea why this works exactly but that is a path i can follow now at least i've solved this problem...
#classmethod
def cleanUpStuff( MyClass ):
MyClass.openFile.release()
Related
I have a quite a bit of confusion on how to use classes. I understand what they are, and why they should be used, just not how. For example, we're given a pre-made class (I'll call it class Class_1(object) to keep things simple) with a few functions (methods, right?) and variables in it.
class Class_1(object):
var_1= [a,b,c]
var_2= [x,y,z]
var_3= {n:[o,p],g:[h,i]}
def method_1(self):
'''here's a method'''
(As a side note, the Class_1(object) does have the __init__(self): method already done.)
Now, in a separate program, I've imported the file that contains that class at the top of the program, but how do I use methods or variables from the class? For example, if I want to check a user input against a value in var_1, how would I do that?
I've gotten better with functions in general, but calling on classes and methods is as clear as mud.
Edit: Realized I said "methods" instead of "variables" when I actually need both.
To use the class, you need to create an class instance from the separate file:
import filename1
class1 = filename1.Class_1()
With the instance, you can then access the member variables:
value1 = class1.method_1
I have a problem with dealing with class variables in Python. I have a code as the following.
class TempClass:
resource = xlwings.Book() # xlwings is a library manipulating Excel file.
#...
Here, to clear 'resource', I need to execute
resource.close()
Is there any built-in function called when a class (not object) is cleared, so that I can write the above code in that function? Or is there any way to clear 'resource'?
My Python version is 3.6
Don't use a class variable. A class variable is alive as long as the class exists probably as long as your python interpreter isn't shutdown.
Normally with ressources that need to be closed you can simply use a contextmanager (for example contextlib.closing):
import contextlib
# I don't have xlwings so I create some class that acts like it:
class Book(object):
def __init__(self):
print('init')
def close(self):
print('close')
The actual "context" can be created and used like this. Inside the block the resource is alive and it's closed after the block ends. I use prints to show where each method is called:
print('before the context')
with contextlib.closing(Book()):
print('inside the context')
print('after the context')
Which prints:
before the context
init
inside the context
close
after the context
Trying to do some optimization here on a class. We're trying not to change too much the class definitions. In essence we are instantiating a ClassA N times but one of the methods has a nasty file read.
for x in range(0, N):
cl = ClassA()
cl.dostuff(x)
The class looks like this:
class ClassA:
def dostuff(self, x):
#open nasty file here
nastyfile = open()
do something else
We could bring that file read out of the class and put in before the loop as the file will not change. But is there a way we can ensure that we only ever open the nasty file once for instances of the class. I.e. so for example on the first instantiate of the class it is defined for all future instances of the class without having to read in again. Is there a way to do this in the current form without really changing the structure too much of the existing code base.
One question relates to the interpreter - i.e. is python smart enough to cache variables just as nastyfile, so that we do as we are, or is the quick and dirty solution the following:
nastyfile = open()
for x in range(0, 1):
cl = ClassA()
cl.dostuff(x)
Looking for a pythonic way to do this.
You could encapsulate opening the file in a classmethod.
class ClassA():
#classmethod
def open_nasty_file(cls):
cls.nasty_file = open('file_path', 'file_mode')
def do_stuff(self):
if not hasattr(self, 'nasty_file'):
self.open_nasty_file()
This approach relies on the fact that attribute look-ups will try finding the attribute on the class if not found on the instance.
You could put this check/instantiation in the __init__ function if you want it opened when the first instance is instantiated.
Note that this method will leave the file open, so it will need to be closed at some point.
You could have a class method that opens the file when the first instance asks for it. I've wrapped it in a lock so that it is thread safe.
import threading
class ClassA:
_nasty_file = None
_nasty_file_lock = threading.Lock()
def dostuff(self, x):
#open nasty file here
nastyfile = get_nasty_file()
do something else
#classmethod
def get_nasty_file(cls):
with cls._nasty_file_lock:
if cls._nasty_file is None:
with open('nastyfile') as fp:
cls._nasty_file = fp.read()
return cls._nasty_file
Instances can access and modify class attributes by themselves. So you can just set up an attribute on the class and provide it with a default (None) value, and then check for that value before doing anything in dostuff. Example:
class A():
nastyfileinfo=None
def dostuff(self,x):
if A.nastyfileinfo: print('nastyfileinfo already exists:',A.nastyfileinfo)
if not A.nastyfileinfo:
print('Adding nastyfileinfo')
A.nastyfileinfo='This is really nasty' ## open()
print('>>>nastyfileinfo:',A.nastyfileinfo)
## Continue doing your other stuff involving x
for j in range(0,10):
A().dostuff(j)
nastyfileinfo is also considered an attribute of the instance, so you can reference it with instance.nastyfileinfo, however if you modify it there it will only update for that one specific instance, whereas if you modify it on the class, all other instances will be able to see it (provided they didn't change their personal/self reference to nastyfileinfo).
instants=[]
for j in range(0,10):
instants.append(A())
for instance in instants:
print(instance.nastyfileinfo)
instants[5].dostuff(5)
for instance in instants:
print(instance.nastyfileinfo)
My text editor of choice is extensible through python plugins. It requires me to extend classes and override its methods. The general structure looks similar the snippet below. Note that the function signature is fixed.
ftp_client is supposed to be shared by instances of both classes.
ftp_client = None
class FtpFileCommand(sublime_plugin.TextCommand):
def run(self, args):
global ftp_client # does it reference the variable of the outer scope?
self.ftp_client = ftplib.FTP('foo')
# login and stuff
class FtpFileEventListener(sublime_plugin.EventListener):
def run(self, args):
global ftp_client # same for this
self.ftp_client.quit() #
Both of these classes are supposed to have one variable in common. What is the best practice in order to share variables?
Edit based on madjars answer:
FtpFileCommand.run is called first, instanciates ftp_client and works like a charm. FtpFileEventListener.run is called later and, can reference ftp_client perfectly but it is still None. Using the global keyword, does it add the variable as a member to self?
Yep, that's exactly how global works.
It seems to me you are doing it right, as it's done this way in some modules of the python standard library (fileinput, for example).
In this code:
global ftp_client # does it reference the variable of the outer scope?
self.ftp_client = ftplib.FTP('foo')
you declare ftp_client as a global variable. This means it lives at the module level (where your classes are for example).
The second line is wrong. You wanted to assign to the global variable but instead you set an instance attribute of the same name.
It should be:
global ftp_client
ftp_client = ftplib.FTP('foo')
But let me suggest a different approach. A common practice is to put such stuff inside the class, since it is shared by all instances of this class.
class FtpFileCommand(sublime_plugin.TextCommand):
ftp_client = None
def run(self, args):
FtpFileCommand.ftp_client = ftplib.FTP('foo')
# login and stuff
Notice that the method doesn't use self so it might as well be a class method:
class FtpFileCommand(sublime_plugin.TextCommand):
ftp_client = None
#classmethod
def run(cls, args):
cls.ftp_client = ftplib.FTP('foo')
# login and stuff
This way you will get the class as the first argument and you can use it to access the FTP client without using the class name.
If there's only a single shared variable, then a global is the simplest solution. But note that a variable only needs to be declared with global when it is being assigned to. If the global variable is an object, you can call its methods, modify its attributes, etc without declaring it as global first.
An alternative to using global variables is to use class attributes which are accessed using classmethods. For example:
class FtpFile(object):
_client = None
#classmethod
def client(cls):
return cls._client
#classmethod
def setClient(cls, client):
cls._client = client
class FtpFileCommand(FtpFile, sublime_plugin.TextCommand):
def run(self, args):
client = self.client()
class FtpFileEventListener(FtpFile, sublime_plugin.EventListener):
def run(self, args):
client = self.client()
Could you add a constructor to each class then pass ftp_client as an argument?
class FtpFileCommand(sublime_plugin.TextCommand):
...
def __init__(self, ftp_client):
self.ftp_client = ftp_client
...
class FtpFileEventListener(sublime_plugin.EventListener):
...
def __init__(self, ftp_client):
self.ftp_client = ftp_client
...
Yak... THANK YOU SO MUCH FOR THIS!!!
You declare ftp_client as a global variable. This means it lives at
the module level (where your classes are for example).
I was having a difficult time trying to write my program "properly" where I'm utilizing classes and functions and couldn't call any of the variables. I recognized that global would make it available outside of the class. When I read that I thought... If it lives outside of the class then the variable I need to retrieve from the py script that I'm importing that module from would be:
module.variable
And then within that module, I declared another global variable to call it from the main script... so example...
#Main Script main.py
import moduleA
print(moduleA.moduleA.variable)
#ModuleA code moduleA.py
import moduleB
class classA():
def functionA():
global moduleA_variable
call.something.from.moduleB.classB.functionB()
moduleA_variable = moduleB.moduleB_variable
ModuleB code moduleB.py
class classB():
def functionB():
global moduleB_variable
moduleB_variable = retrieve.tacos()
I hope my explanation also helps someone. I'm a beginner with python and struggled with this for a while. In case it wasn't clear... I had separate custom modules made up of a few different .py files. Main was calling moduleA and moduleA was calling moduleB. I had to return the variable up the chain to the main script. The point of me doing it this way, was to keep the main script clean for the most part, and set myself up for executing repetitive tasks without having to write pages of crap. Basically trying to reuse functions instead of writing a book.
class Ball:
a = []
def __init__(self):
pass
def add(self,thing):
self.a.append(thing)
def size(self):
print len(self.a)
for i in range(3):
foo = Ball()
foo.add(1)
foo.add(2)
foo.size()
I would expect a return of :
2
2
2
But I get :
2
4
6
Why is this? I've found that by doing a=[] in the init, I can route around this behavior, but I'm less than clear why.
doh
I just figured out why.
In the above case, the a is a class attribute, not a data attribute - those are shared by all Balls(). Commenting out the a=[] and placing it into the init block means that it's a data attribute instead. (And, I couldn't access it then with foo.a, which I shouldn't do anyhow.) It seems like the class attributes act like static attributes of the class, they're shared by all instances.
Whoa.
One question though : CodeCompletion sucks like this. In the foo class, I can't do self.(variable), because it's not being defined automatically - it's being defined by a function. Can I define a class variable and replace it with a data variable?
What you probably want to do is:
class Ball:
def __init__(self):
self.a = []
If you use just a = [], it creates a local variable in the __init__ function, which disappears when the function returns. Assigning to self.a makes it an instance variable which is what you're after.
For a semi-related gotcha, see how you can change the value of default parameters for future callers.
"Can I define a class variable and replace it with a data variable?"
No. They're separate things. A class variable exists precisely once -- in the class.
You could -- to finesse code completion -- start with some class variables and then delete those lines of code after you've written your class. But every time you forget to do that nothing good will happen.
Better is to try a different IDE. Komodo Edit's code completions seem to be sensible.
If you have so many variables with such long names that code completion is actually helpful, perhaps you should make your classes smaller or use shorter names. Seriously.
I find that when you get to a place where code completion is more helpful than annoying, you've exceeded the "keep it all in my brain" complexity threshold. If the class won't fit in my brain, it's too complex.