Variable scope outside of classes - python

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.

Related

Provide object methods into the namespace of a python environment

I am trying to provide wrappers for short-cutting every-day commands. Python environments are very useful to do that.
Is it possible to provide all methods of an object to the local namespace within a new environment?
class my_object:
def method_a():
...
class my_environment:
...
def __enter__(self):
some_object = my_object()
# something like `from some_object import *` ??
return(some_object)
...
with my_environment() as some_object:
# standard syntax:
some_object.method_a()
# shortcut:
method_a() # how to make this possible?
It will be rather complex, and IMHO will not be worth it. The problem is that in Python, local variables are local to a function and not to a bloc. So what you are asking for would require that:
__enter__ declares nonlocal variables for all of the methods from some_object and saves their previous value if any
__exit__ restore the previous values if any of those variables, or deletes them if they did not previously existed
Possible but not really Pythonic IMHO (the reason why I have not proposed any code...). After all, inside a method Python requires the object to be explicitely passed, and requires it to be prepended to any internal method call or attribute access. So my advice is to stick to the standard syntax here...
What you are looking for is class hierarchy. On the way, please be careful with the conventions for class names.
class MyObject:
def method_a():
...
class MyEnvironment(MyObject):
...
def __enter__(self):
return self
...
with MyEnvironment() as some_object:
# standard syntax:
some_object.method_a()
The shortcut you are looking doesn't make much sense because the method_a() was defined as a method, therefore it should be called together with the instance.
Maybe #staticmethod can serve your case better.
class MyEnvironment:
#staticmethod
def method_a():
...
MyEnvironment.method_a()

Global or static function in python classes?

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()

How do I create an instance of a nested class from within that class in Python?

class ExampleClass():
def Example(self):
self.nestedinstance = self.NestedClass()
self.nestedinstance.makenew()
class NestedClass():
def makenew(self):
newclass = NestedClass()
ExampleClass().Example()
When I run the above code I get the exception: name 'NestedClass' is not defined
Is there a different way to do this with a nested class or have I done something wrong?
You ideally want to be using classmethods if creating a new instance, this is an example of how you'd do it:
class NestedClass():
#classmethod
def makenew(cls):
newclass = cls()
Alternatively if you wanted to create an instance using the current instance (for example if you needed to pass in some arguments), then you can get the class by using type(self) or self.__class__.
class NestedClass():
def makenew(self):
newclass = type(self)()
Without knowing your use case though, this may not be what you're after.
Your error comes from how python handles classes.
When it encounters a class statement, the body of the class is run, and the names it defines are placed in a separate namespace, which will eventually become the class __dict__. The class object is not created and bound to its name until (well) after the body has run. That means that when you put class NestedClass: inside the body of class ExampleClass:, ExampleClass does not exist yet, and neither does NestedClass. Indirectly because of this, all the new class namespaces live in the top level available namespace (e.g. global or function), and are not actually nested within one another.
As a consequence of this order of operations, class bodies are not aware of the namespaces of surrounding classes at all. So the namespace of NestedClass looks out to the global namespace, not to the __dict__ of ExampleClass, as you might expect coming from say Java. A class defined in a function would be able to see the functions local namespace before globals, but still not that of an enclosing class.
And so, the line newclass = NestedClass() raises an error. The name NestedClass does not exist in the function's namespace, or in the global namespace. There are three simple workarounds available:
Use the staticly scoped __class__:
newclass = __class__()
Refer to the class by its global name:
newclass = ExampleClass.NestedClass()
Don't use nested classes in Python. This is generally the preferred approach. Just move NestedClass to the top level. Then your makenew method will work without modification, and ExampleClass.Example can refer to NestedClass directly instead of as self.NestedClass.

Python Decorated Function Can't Update Attribute from Another Class

I have an issue using a decorator from a imported package in a class I created. I created two classes and a main(). An instance of class A is created in main() and an instance of class B is created in class A. Class B needs to update attributes of the instance created in main(). Furthermore, I need to use decorators from the imported package.
I don’t know how to get around the inability to reference the attributes of the the instance created in main() from the decorated confirm_connect function in the instance of class B. The only way I have made it work is to declare the instance created in main() as global and remove all the self references in class B. However, making it global causes other issues in my application. (Making the instance of socketio as global within class B is tolerable, but I prefer not having it either.)
The function confirm_connect receives a message from the server. If I define the function as def conform_connect(self, data), I get the error message connect_confirm() missing 1 required positional argument: 'data'/. If I remove self from the declaration, then I get the error NameError: name 'self' is not defined.
Here is my script. How can I make my script do what I need it to do?
import socketio
class A():
def __init__(self):
self.pin = None
def connect_to_server(self):
self.io = B()
self.io.connect_to_server()
def start_the_process(self):
self.b = B(self)
self.b.connect_to_server()
def do_something_with_pin(self):
print(self.pin)
class B():
global sio
sio = socketio.Client()
def __init__(self, a):
self.a = a
def connect_to_server(self):
sio.connect('http://my_url_is_this.org')
sio.emit('manual_connection_parameter', {'foo': 'bar'})
#sio.event
def connect_confirm(data):
self.a.pin = data
self.a.do_something_with_pin()
def main():
a = A()
a.start_the_process()
if __name__ == '__main__':
main()
If you understand how decorators work, then you understand that
#sio.event
def connect_confirm(self, data):
self.a.pin = data
self.a.do_something_with_pin()
is simply syntactic sugar for
def connect_confirm(self, data):
self.a.pin = data
self.a.do_something_with_pin()
connect_confirm = sio.event(connect_confirm)
And the reported problem is that sio.event expects a 1-argument, plain callback function that will receive the data; so with a self parameter it doesn't meet those expectations (and without a self parameter, the expectations of the method aren't met).
The insight is that (since 3.x; 2.x did things differently under the hood) a method defined in a class is just a function; it's the process of looking up that method from an instance that makes methods do the special things with self that they do.
So when you decorate the method, you end up registering totally the wrong thing as a callback. The socketio.Client doesn't know anything about your B instance and can't work with it, no matter what you do.
The solution is to instead use the bound instance method of your instance for the callback, which requires us to invoke the decorator manually as described at the beginning.
In the __init__, we can do something like:
def __init__(self, a):
self.a = a
sio.event(self.connect_confirm)
And then we can define that method normally:
def connect_confirm(self, data):
self.a.pin = data
self.a.do_something_with_pin()
Notice how in the __init__ context we can now write self. when we do the "decoration", so we tell the socketio.Client to use the connect_confirm of this instance as a callback. We don't need to do anything with the "decorated" result, so we don't assign it anywhere.
It's worth considering what this kind of thing looks like from the API's point of view. The socketio.Client class implementation presumably includes something like:
class Client:
# ... lots of other stuff...
def event(self, callback):
self._event_callback = callback
return callback
def _some_internal_logic(self):
if _special_situation_occurs():
self._event_callback(self._get_data_for_callback())
If the implementation didn't return callback, it would be obvious what you needed to do in your situation, since the decorator syntax wouldn't have been available. (Well, you can use anything as a decorator; but getting back None or another non-function is not very useful most of the time.)

Call a function outside any method but inside a class?

I'm trying to call a function to initialize my global class-level variables. Note that for some reason, I cannot write a constructor to do the job. (reason: I'm writing a test case for Tempest)
Code:
class my_class(ParentClass):
username = "abc"
password = "123"
hostname = self._get_hostname() # Error: self is undefined
hostname = _get_hostname() # Error: _get_hostname is undefined
def myFunct(self):
...
...
def _get_hostname(self):
...
...
Is it even possible in python to initialize global class-level variables with class functions?
Note 1: I have also tried to perform hostname initialization after the _get_hostname() function is written, but of no use.
Note 2: I can perform hostname initialization within a function such as:
def myFunction(self):
hostname = self._get_hostname()
# do something with hostname
...
But that would defeat the whole purpose of using a global class-level variable. I want to use a class-level variable for re-usage in other functions.
You cannot call instance methods for an instance that doesn't exist yet. If you want for it to be independent of any instance, and the functions you call don't require any information from the instance, you can use an #staticmethod decorator so you can call get hostname statically.
First: they are not global variables; they are class variables. (Globals are defined at the module scope.)
If _get_hostname() doesn't actually use self at all, you can do something a little sketchy: make sure it is defined before you use it, then call it with a fake first argument.
class my_class(ParentClass):
def _get_hostname(self):
...
hostname = _get_hostname(None) # Doesn't matter what
This works because when you call it inside the class statement, you can still treat it as a regular function. It just has to be defined before you use it, like any function.
If it actually does require a reference to an object (or even just the class), this won't work, because the class doesn't exist yet. The best you can do is either define a function prior to the class statement that doesn't use the class itself and call it. (This is also what you would do
if the previous option was possible but ... distasteful.)
def _get_hostname():
...
class my_class(ParentClass):
hostname = _get_hostname()
or defer initializing the class variable until after the class is defined. In this case, _get_hostname should probably be declared as a static method or a class method so that you don't have to instantiate the class just to initialize the class variable.
class my_class(ParentClass):
#classmethod
def _get_hostname1(cls):
...
#staticmethod
def _get_hostname2():
...
my_class.hostname = my_class._get_hostname1()
my_class.hostname = my_class._get_hostname2()

Categories

Resources