Quando ho iniziato a costruire progetti nel mondo reale a Python, ero entusiasta solo di far funzionare le cose. Avevo lezioni che chiamavano altre lezioni, i servizi si giravano all’interno dei costruttori e tutto ciò che in qualche modo teneva insieme.
Ma in fondo, sapevo che qualcosa non andava.
Perché?
Il mio codice sembrava goffo. Il take a look at è stato un incubo. Cambiare una parte ha rotto altri tre. L’aggiunta di una piccola funzionalità mi farebbe cambiare 10 file diversi nel pacchetto. Non riuscivo a spiegare perché – fino a quando uno sviluppatore di software program esperto non abbia esaminato il mio codice e mi ha chiesto, “Hai sentito parlare di iniezione di dipendenza?”
Non l’ho fatto.
Così sono sceso dalla tana del coniglio. E ciò che ho trovato ha cambiato il modo in cui penso a strutturare il codice. In questo submit, ti guiderò attraverso cosa Iniezione di dipendenza è, come funziona a Python e perché potresti voler iniziare a usare una libreria come dipendenza-iniettore. Useremo esempi del mondo reale, linguaggio semplice e codice pratico per comprendere il concetto.
Se sei all’inizio del tuo viaggio Dev o stai semplicemente cercando di ripulire il codice disordinato, questo è per te.
Quindi, che diamine è l’iniezione di dipendenza?
Abbattiamolo.
Iniezione di dipendenza (DI) è solo un termine di fantasia per “Dammi quello di cui ho bisogno, non farmi crearlo da solo.“
Invece di una classe o funzione creando le proprie dipendenze (come altre classi, database, consumer, ecc.) iniettare quelle dipendenze dall’esterno. Questo rende più facile scambio loro fuori, specialmente nei take a look at, e promuove accoppiamento sciolto.
Una rapida analogia del mondo reale
Immagina di gestire una caffetteria. Ogni volta che qualcuno ordina il caffè, non vai a costruire una macchina da caffè da zero. Ne hai già uno e lo passi al barista.
Questo è di. Il barista (la tua classe) non crea la macchina del caffè (la dipendenza per servire il caffè); Lo riceve dall’esterno.
Senza di: un esempio doloroso
class DataProcessor:
def __init__(self):
# DataProcessor creates its personal database connection
self.db = MySQLDatabase(host="localhost", person="root", password="secret")
def process_data(self):
knowledge = self.db.question("SELECT * from knowledge")
# Course of the information...
return processed_data
Vedi il problema?
Funziona, ma ecco il problema. Nostro DataProcessor
è strettamente accoppiato a uno specifico Database MySQL implementazione.
- Vuoi cambiare database? Dovrai modificare questa classe.
- Vuoi testarlo con un database finto? Buona fortuna con quello.
Con DI: pulito e flessibile
Ora, ecco lo stesso codice con iniezione di dipendenza.
class DataProcessor:
def __init__(self, database):
# Database is injected from outdoors
self.db = database
def process_data(self):
knowledge = self.db.question("SELECT * from knowledge")
# Course of the information ...
return processed_data
# Creating and injecting dependencies
db = MySQLDatabase(host="localhost", person="root", password="secret")
processor = DataProcessor(db)
Questo è tutto! Il processore ora prende la sua dipendenza dal database come parametro invece di crearlo internamente. Questo semplice cambiamento fa la differenza.
Utilizzando una libreria DI in Python
La forma più semplice di DI in Pitone sta solo passando dipendenze attraverso i costruttori (come abbiamo visto sopra). Questo si chiama Iniezione del costruttore e funziona alla grande per casi semplici. Ma in applicazioni più grandi, la scrittura manualmente le dipendenze diventano disordinate.
Ecco dove il dipendenza-iniettore La biblioteca brilla. Ti dà contenitori, fornitori e un bel modo per gestire le tue dipendenze.
Innanzitutto, installiamo il pacchetto:
pip set up dependency-injector
Ora, vediamolo in azione nel mondo reale. Immagina che stiamo costruendo una pipeline di ingegneria dati che:
- Estrae dati da numerous fonti.
- Lo trasforma.
- Lo carica in un knowledge warehouse.
Ecco come lo struttudiamo con DI:
from dependency-injector import containers, suppliers
from database import PostgresDatabase, MongoDatabase
from providers import DataExtractor, DataTransformer, DataLoader
class Container(containers.DeclarativeContainer):
config = suppliers.Configuration()
# Database dependencies
postgres_db = suppliers.Singleton(
PostgresDatabase,
host=config.postgres.host,
username=config.postgres.username,
password=config.postgres.password
)
mongo_db = suppliers.Singleton(
MongoDatabase,
connection_string=config.mongo.connection_string
)
# Service dependencies
extractor = suppliers.Manufacturing unit(
DataExtractor,
source_db=mongo_db
)
transformer = suppliers.Manufacturing unit(
DataTransformer
)
loader = suppliers.Manufacturing unit(
DataLoader,
target_db=postgres_db
)
# Predominant software
etl_pipeline = suppliers.Manufacturing unit(
ETLpipeline,
extractor=extractor,
transfomer=transformer,
loader=loader
)
Abbattiamo ciò che sta accadendo qui:
Contenitori
UN contenitore è come un registro delle dipendenze della tua domanda. Sa come creare ogni componente e quali dipendenze ha bisogno di ogni componente.
Fornitori
Fornitori Dì al contenitore come creare oggetti. Esistono diversi tipi:
- Fabbrica: Crea una nuova istanza ogni volta (buono per la maggior parte dei servizi)
- Singleton: Crea solo un’istanza (bene per le connessioni, per esempio)
- Configurazione: Fornisce valori di configurazione
- E molti altri … (Dai un’occhiata all’elenco completo seguendo il hyperlink sopra)
Usando il contenitore
# Load configuration from a file
container = Container()
container.config.from_yaml('config.yaml')
# Create the pipeline with all dependencies resolved routinely
pipeline = container.etl_pipeline()
pipeline.run()
La magia qui è che il nostro ETLPipeline
La classe non ha bisogno di sapere come creare estrattori, trasformatori o caricatori. Usa solo quello che è stato dato:
class ETLPipeline:
def __init__(self, extractor, transformer, loader):
self.extractor = extractor
self.transformer = transformer
self.loader = loader
def run(self):
knowledge = self.extractor.extract()
transformed_data = self.transformer.rework(knowledge)
self.loader.load(transformed_data)
Avvolgimento
L’iniezione di dipendenza potrebbe sembrare qualcosa di cui solo le grandi app aziendali di Huge Enterprise hanno bisogno. Ma se hai mai provato a scrivere take a look at o refactor sul tuo codice e volevi urlare, DI può salvare la sanità mentale.
La libreria di dipendenza-iniettore di Python ti dà un modo elegante per strutturare il tuo codice, mantenere le cose liberamente accoppiate e divertirsi in realtà.
Pensieri finali
La prossima volta che scrivi una lezione che ha bisogno di “cose” per lavorare, mettere in pausa e chiedere: Dovrei invece iniettarlo?
È un semplice cambiamento nella mentalità, ma porta a codice più pulito, take a look at migliori e sviluppatori più felici (hai incluso).
Se lo hai trovato utile, digli o condividilo con un compagno di squadra che è in codice spaghetti. E se vuoi più articoli come questo – vero discorso, veri esempi – seguimi qui su Dzone.
Scriviamo meglio Python, un servizio pulito alla volta.