10

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?

fjdumont
  • 1,517
  • 1
  • 9
  • 22
  • To answer the second question in your edit: making `ftp_client` an instance attribute defeats the purpose of using globals. the new `FTP` object should be assigned to the `ftp_client` variable after declaring it as global first. after that, it can be referenced from anywhere without the need to declare it as global, e.g. `ftp_client.quit()`. – ekhumoro Oct 13 '11 at 15:50

5 Answers5

8

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

madjar
  • 12,691
  • 2
  • 44
  • 52
  • 1
    I edited the question based on your answer, please have a look :) – fjdumont Oct 13 '11 at 13:57
  • 1
    After doing `global ftp_client`, you can access it using `ftp_client`. Be careful, because `self.ftp_client` will refer to the attribute of the instance, which is different of the global `ftp_client`. – madjar Oct 13 '11 at 14:01
  • Thanks for answering that quickly. Honestly, reasearching the docs could've helped me as well. Lazy me... – fjdumont Oct 13 '11 at 14:04
7

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.

yak
  • 8,851
  • 2
  • 29
  • 23
3

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()
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • Now that's a smart solution! Thanks for clarifying that `global` is only required during assignment. I just +1'd your answer because I feel naughty removing the solution mark from madjars answer... – fjdumont Oct 13 '11 at 14:48
0

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.

Martin Evans
  • 45,791
  • 17
  • 81
  • 97
0

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
    ...
Ian Oxley
  • 10,916
  • 6
  • 42
  • 49