Q:

¿Cómo puedo utilizar, en una función, una instancia de objeto creada en otra función? - python3 & tkinter

Estoy intentando hacer una herramienta para conectarme a un FTP y descargarme archivos de este usando python y tkinter. De momento la conexión al FTP juntamente con descarga de archivos y la interfaz gràfica la he podido hacer.
El problema me sucede cuando quiero hacer un button para conectarme al FTP y otro para desconectarme de él. Cada button/pulsador va asocidado a un función. En el button de conectar la función asociada es:
def connectFtp():
try:
    ip = ipFtpInput.get()
    port = portInput.get()
    user = userInput.get()
    passwd = passwordInput.get()
    ftp = FTP(ip)
    ftp.login(user=user,passwd=passwd)
    ftp.dir()
    print("Connected to FTP - success")

except ftplib.all_errors as e:
    #print(errno.errorcode)
    errString = str(e).split()[1]
    finalErrorCode = errString.split(']')[0]
    print(finalErrorCode)`

Para el button de desconectar el código es:
def disconnectFtp():
try: 
    ftp.quit()
except ftplib.all_errors as e:
    print(e)
finally:
    print('Disconnecting from FTP')
    ftp.close()

El error que obtengo al pulsar el button para desconectar es:

UnBoundLocalError: local variable 'ftp' referenced before assignment

Creo que es debido a que 'ftp' lo instancio dentro de la función connectFtp() y lo intento usar después en la función disconnectFtp(), es por eso que pregunto como puedo utilizar la instancia ftp dentro de la función disconnectFtp.
La verdad es que soy bastante novato y puede que este sacando una conclusión equivocada o preguntando un estupidez... igualmente agradecería me pudierais dar una respuesta!
Muchas gracias!!

A:

Dices en un comentario que ftp no es una variable sino una instancia de la clase FTP. Bueno, pero es que una instancia de una clase es también una variable, por lo que no hay ningún impedimento en hacerla global, o convertirla en un atributo de un objeto.
Usando una variable global
En tu función connect_ftp() tienes una línea que dice:
    ftp = FTP(ip)

Ahí es donde se está asignando un valor a la variable ftp. Ya que esta asignación ocurre dentro de una función, la variable se considera local y no global. Puedes hacer que python la considere global "declarándola" así:
ftp = None        # Tenemos una variable global, con un valor por defecto

def connectFtp(): 
    global ftp    # Avisamos a python de que queremos usar la global, no la local
    try:
        ip = ipFtpInput.get()
        port = portInput.get()
        user = userInput.get()
        passwd = passwordInput.get()
        ftp = FTP(ip)                     # Se re-asigna la global
        ftp.login(user=user,passwd=passwd)
        ftp.dir()
        print("Connected to FTP - success")

    except ftplib.all_errors as e:
        #print(errno.errorcode)
        errString = str(e).split()[1]
        finalErrorCode = errString.split(']')[0]
        print(finalErrorCode)

Entonces en la otra función ya puedes usarla también así:
def disconnectFtp():
    global ftp         # De nuevo avisamos de que queremos usar la global
    if ftp is None:    # Por si acaso no se le había asignado un nuevo valor
        return
    try: 
        ftp.quit()
    except ftplib.all_errors as e:
        print(e)
    finally:
        print('Disconnecting from FTP')
        ftp.close()

Usando objetos
La otra opción es crear una clase propia para juntar los métodos connect()  y disconnect() en un mismo objeto, que usen un atributo compartido para referirse a la misma conexión ftp.
No obstante tengo la sensación de que este código forma parte de una aplicación Tk en la que obtienes los datos de conexión (ip, puerto, contraseña...) de campos de un formulario, y luego asignas a botones las funciones que conectan y desconectan. En este caso el enfoque OOP requeriría, para hacerse bien, pasar como parámetro al constructor de nuestra clase las referencias a los widgets de los que se debe obtener la información de la conexión. El constructor guardaría esas referencias en el propio objeto para poder acceder a ellas en el connect.
La idea sería más o menos así, pero posiblemente no te funcione tal cual pues depende bastante de cómo tengas hecha el resto de la aplicación:
class ConexionFTP:
    def __init__(self, ipFtpInput, portInput, userInput, passwordInput):
        self.ipFtpInput = ipFtpInput
        self.portInput = portInput
        self.userInput = userInput
        self.passwordInput = passwordInput
        self.ftp = None

    def connect(self):
        try:
            ip = self.ipFtpInput.get()
            port = self.portInput.get()
            user = self.userInput.get()
            passwd = self.passwordInput.get()
            self.ftp = FTP(ip)
            self.ftp.login(user=user,passwd=passwd)
            self.ftp.dir()
            print("Connected to FTP - success")

        except ftplib.all_errors as e:
            errString = str(e).split()[1]
            finalErrorCode = errString.split(']')[0]
            print(finalErrorCode)

    def disconnect(self):
        if self.ftp is None:
            print("No se puede desconectar porque no se había conectado")
            return
        try: 
            self.ftp.quit()
        except ftplib.all_errors as e:
            print(e)
        finally:
            print('Disconnecting from FTP')
            self.ftp.close()

myftp = ConexionFTP(ipFtpInput, portInput, userInput, passwordInput)
myftp.connect()
myftp.disconnect()

Si todo esto forma parte de una aplicación Tk como sospeché, las dos últimas líneas no se usarían de esta forma, sino que se asignaría al command de diferentes botones los métodos myftp.connect y myftp.disconnect (no se ponen paréntesis en este caso porque no queremos invocarlos, sino obtener la referencia a ellos).

