Come ottenere l'ereditarietà usando solo moduli e funzioni vanilla in Python?

1

Python non applica alcun paradigma. Ti dà la libertà. Python fornisce l'incapsulamento a livello di modulo.

Se ho un modulo A e ho un modulo B con la stessa interfaccia. Come faccio ad ereditare B da A e sovrascrivere alcune funzionalità fornite da B?

    
posta Amogh Talpallikar 23.09.2015 - 12:22
fonte

2 risposte

5

Quando importate Y module, che importa le funzioni dal modulo X con from X import * , tutte le funzioni da X sono disponibili così come erano in Y (Potete farcela, come incollare il contenuto di X in Y ).

E, quando hai più difetti, Python prenderà l'ultimo. Quindi, per sovrascrivere le funzioni, devi solo ridefinire quelle funzioni dopo aver importato X .

Inoltre, quando vuoi utilizzare la funzione originale da X in Y , puoi aggiungere import X adizionale e accedervi con X.functionName() .

Esempio:

module1.py

def foo():
   print "this will be overridden"
def bar():
   print "this will be preserved"

module2.py

from module1 import *
import module1

def foo():
   print "This was foo:"
   module1.foo()
   print "But it was overridden."

run.py

import module2

module2.foo()
module2.bar()

Risultato dell'esempio:

-> % python run.py 
This was foo:
this will be overridden
But it was overridden.
this will be preserved
    
risposta data 23.09.2015 - 12:38
fonte
2

Qual è il punto dei moduli?

I moduli sono un meccanismo per strutturare un'applicazione in parti distinte e riutilizzabili. Per esempio. un programma di utilità della riga di comando potrebbe calcolare il codice che analizza l'argomento in un modulo separato che può essere utilizzato anche in altri strumenti da riga di comando. Poiché un modulo ha un'interfaccia pubblica chiara e può nascondere i dettagli di implementazione all'interno del modulo, anche l'applicazione complessiva diventa meno strettamente accoppiata.

Posso scambiare un modulo con un altro modulo con la stessa interfaccia?

Non necessariamente, e non è questo il punto dei moduli. Tuttavia, ci sono varie tecniche per implementare i moduli. Uno consiste nel definire una struttura di puntatori di funzioni che definiscono il tipo dell'intero modulo. Quando un modulo viene caricato, fornisce un'istanza di questo modulo. Questa tecnica viene spesso utilizzata per simulare i moduli in JavaScript:

// a mathModule may be anything that supplies "add" and "sub" functions
var mathModule = (function () {
  var privateVar = "secret";
  function add(a, b) { return a + b; }
  function sub(a, b) { return a - b; }
  function privateFunction() { return "very" + privateVar; }

  // return an “object” (Python: dict) that defines the public interface
  return {
    add: add,
    sub: sub,
  };
})();

Questa tecnica può anche essere adattata a C per fornire moduli reali e può essere facilmente tradotta in Python. Tuttavia, nessuna delle due lingue ha lambda e C non ha alcuna chiusura, quindi l'esempio tradotto sarebbe più limitato.

Dato che qui abbiamo a che fare con i puntatori di funzione, abbiamo un livello importante di indirezione. In linea di principio, potremmo avere più istanze della stessa interfaccia modulo in una volta e possiamo facilmente passare a un'implementazione diversa se offre la stessa interfaccia.

Questo suona molto simile alla programmazione orientata agli oggetti, ma mancano alcuni dettagli importanti: per OOP, la struttura dell'istanza deve contenere anche dati oltre ai metodi e deve passare la struttura dell'istanza come argomento indietro in qualsiasi metodo che è stato invocato. Discuto creando un sistema di oggetti JavaScript fuori dalle chiusure in un post del blog in modo più approfondito.

I moduli di Python funzionano così?

Tipo di. In effetti, i moduli di Python si comportano molto come un oggetto normale, tranne per il fatto che non abbiamo un argomento self . Tuttavia, Python manca la parte in cui definiamo effettivamente un'interfaccia chiara per il nostro modulo. Laddove l'esempio di JavaScript sopra riportato può esplicitamente scegliere se esporre una funzione attraverso l'interfaccia pubblica, Python rende accessibile qualsiasi simbolo nel file scope quando import ing in un modulo. Ci sono trucchi che limitano questo problema (come la definizione delle funzioni nei moduli helper e quindi l'importazione selettiva dell'interfaccia pubblica nel __init__.py del modulo), ma in Python finiamo per definire l'interfaccia attraverso la nostra documentazione, non attraverso il codice.

Quindi, per farla breve, Python ha spazi dei nomi che chiama "moduli", ma non offre alcuna incapsulazione.

Quindi, come posso ereditare un modulo?

Il concetto di "ereditarietà" non ha senso quando si discute dei moduli, poiché si tratta più di un termine OOP. Lì, " Subclass eredita da Base " significa (a) che Subclass è un sottotipo di Base e (b) che le istanze di Subclass possono in qualche modo utilizzare l'implementazione definita da Base . Come saprai, riutilizzare l'implementazione può avvenire tramite due meccanismi: l'ereditarietà dell'implementazione (in cui il meccanismo di ricerca del metodo per Subclass viene inizializzato con i metodi per Base ) e la composizione (dove Subclass delega a Base esempio).

Con la codifica module-as-dict, questo può essere illustrato come tale:

# The original module.
# Can be instantiated like 'mathModule = mathModule_load()'
def mathModule_load():
  private_var = "secret"
  def add(a, b): return a + b
  def sub(a, b): return a - b
  def private_function(): return "very" + private_var

  return {
    'add': add,
    'sub': sub,
  }

# Inherit the mathModule via composition
# Overrides "add" to type-check for int arguments
def checkedMathModule_load():
  base = mathModule_load() # the module instance we inherit

  def add(a, b):
    if type(a) is not int or type(b) is not int:
      raise TypeError("give me ints!")
    return base['add'](a, b)

  def sub(a, b):
    return base['sub'](a, b)

  # return our own module instance that meets the expected interface
  return {
    'add': add,
    'sub': sub,
  }

# Inherit the mathModule by changing the module definition
# Overrides "add" to emit debug info
def chattyMathModule_load():
  module = mathModule_load() # the module instance we inherit

  original_add = module['add']
  def my_add(a, b):
    print("DEBUG: add(%d, %d)" % (a, b))
    return original_add(a, b)
  module['add'] = my_add

  return module

OK, possiamo farlo ora con i moduli integrati?

* brontolone * Python può essere formalmente multi-paradigma, ma chiaramente favorisce le soluzioni OOP a problemi come questo. Possiamo rielaborare l'esempio precedente per usare i moduli built-in di Python, ma non è carino. Nota che i moduli di Python non hanno un concetto paragonabile di "istanze", poiché i moduli sono globali.

Invece dell'esplicita dict che memorizza la funzione del modulo, useremo la normale sintassi di Python per aggiungere simboli all'ambito del file.

I moduli sono organizzati nella seguente gerarchia di file:

mathModule/
  __init__.py
  detail.py
checkedMathModule/
  __init__.py
  detail.py
chattyMathModule/
  __init__.py
  detail.py

mathModule/__init__.py :

from .detail import add, sub
del detail

mathModule/detail.py :

private_var = "secret"

def add(a, b):
  return a + b

def sub(a, b):
  return a - b

def private_function():
  return "very" + private_var

checkedMathModule/__init__.py :

# this module offers the same interface as mathModule
from .detail import add, sub
del detail

checkedMathModule/detail.py :

import mathModule as base

def add(a, b):
  if type(a) is not int or type(b) is not int:
    raise TypeError("give me ints!")
  return base.add(a, b)

def sub(a, b):
  return base.sub(a, b)

chattyMathModule/__init__.py :

from mathModule import *

# now import the function we want to override
from .detail import my_add as add
del detail

chattyMathModule/detail.py :

from mathModule import add as original_add

def my_add(a, b):
  print("DEBUG: add(%d, %d)" % (a, b))
  return original_add(a, b)

Non sono sicuro che del detail non romperà nulla, ma senza di esso gli utenti potrebbero fare mathModule.detail.private_function() e ottenere qualcosa 'verysecret' , che interrompe completamente l'incapsulamento.

Queste tecniche sono un po 'di lavoro, ma sono molti sforzi inutili per qualcosa che potrebbe essere fatto molto più facilmente con oggetti regolari. Inoltre, Python è speciale perché i moduli sono rappresentati come oggetti modulo che possono essere passati intorno a come valori , mentre il sistema dei moduli della maggior parte delle lingue è molto più statico. Queste tecniche non sono rappresentative della programmazione modulare, ma piuttosto di un "Voglio fare OOP senza utilizzare le parti OOP del mio linguaggio", una mentalità che non è costruttiva per nulla se non comprendere meglio OOP.

    
risposta data 23.09.2015 - 14:38
fonte

Leggi altre domande sui tag