使用Python Dependency Injector构建CLI电影查询应用教程

使用Python Dependency Injector构建CLI电影查询应用教程

python-dependency-injector Dependency injection framework for Python python-dependency-injector 项目地址: https://gitcode.com/gh_mirrors/py/python-dependency-injector

前言

在现代软件开发中,依赖注入(Dependency Injection, DI)是一种重要的设计模式,它可以帮助我们编写更可测试、更松耦合的代码。本教程将展示如何使用Python Dependency Injector库构建一个命令行界面(CLI)的电影查询应用,通过实践理解依赖注入的核心概念和应用方式。

项目概述

我们将构建一个名为Movie Lister的CLI应用,它具有以下功能:

  • 从数据库中查询电影信息
  • 支持两种数据库格式:CSV和SQLite
  • 能够按导演姓名和发行年份筛选电影
  • 采用清晰的架构设计,便于扩展新的数据库格式

环境准备

首先需要创建项目环境:

mkdir movie-lister-tutorial
cd movie-lister-tutorial
python3 -m venv venv
source venv/bin/activate

安装所需依赖包:

pip install dependency-injector pyyaml pytest pytest-cov

项目结构

创建以下项目结构:

./
├── data/
│   └── fixtures.py
├── movies/
│   ├── __init__.py
│   ├── __main__.py
│   └── containers.py
├── config.yml
└── requirements.txt

数据准备

data/fixtures.py中创建示例数据:

import csv
import sqlite3
import pathlib

SAMPLE_DATA = [
    ("The Hunger Games: Mockingjay - Part 2", 2015, "Francis Lawrence"),
    ("Rogue One: A Star Wars Story", 2016, "Gareth Edwards"),
    ("The Jungle Book", 2016, "Jon Favreau"),
]

def create_csv(movies_data, path):
    # CSV创建逻辑

def create_sqlite(movies_data, path):
    # SQLite创建逻辑

def main():
    create_csv(SAMPLE_DATA, "data/movies.csv")
    create_sqlite(SAMPLE_DATA, "data/movies.db")

运行后会生成CSV和SQLite格式的数据库文件。

核心组件实现

1. 电影实体类

movies/entities.py中定义Movie类:

class Movie:
    def __init__(self, title: str, year: int, director: str):
        self.title = str(title)
        self.year = int(year)
        self.director = str(director)

2. 电影查找器接口

movies/finders.py中定义基础查找器:

from typing import Callable, List

class MovieFinder:
    def __init__(self, movie_factory: Callable[..., Movie]):
        self._movie_factory = movie_factory
    
    def find_all(self) -> List[Movie]:
        raise NotImplementedError()

3. CSV查找器实现

import csv

class CsvMovieFinder(MovieFinder):
    def __init__(self, movie_factory, path, delimiter):
        self._csv_file_path = path
        self._delimiter = delimiter
        super().__init__(movie_factory)
    
    def find_all(self):
        with open(self._csv_file_path) as csv_file:
            return [self._movie_factory(*row) for row in csv.reader(csv_file)]

4. 电影列表器

movies/listers.py中实现查询逻辑:

class MovieLister:
    def __init__(self, movie_finder):
        self._movie_finder = movie_finder
    
    def movies_directed_by(self, director):
        return [m for m in self._movie_finder.find_all() if m.director == director]
    
    def movies_released_in(self, year):
        return [m for m in self._movie_finder.find_all() if m.year == year]

依赖注入容器

movies/containers.py中配置依赖关系:

from dependency_injector import containers, providers

class Container(containers.DeclarativeContainer):
    config = providers.Configuration(yaml_files=["config.yml"])
    
    movie = providers.Factory(entities.Movie)
    
    csv_finder = providers.Singleton(
        finders.CsvMovieFinder,
        movie_factory=movie.provider,
        path=config.finder.csv.path,
        delimiter=config.finder.csv.delimiter,
    )
    
    lister = providers.Factory(
        listers.MovieLister,
        movie_finder=csv_finder,
    )

主程序入口

__main__.py中实现应用入口:

from dependency_injector.wiring import Provide, inject

@inject
def main(lister: MovieLister = Provide[Container.lister]):
    print("Francis Lawrence movies:")
    for movie in lister.movies_directed_by("Francis Lawrence"):
        print("\t-", movie)

if __name__ == "__main__":
    container = Container()
    container.wire(modules=[__name__])
    main()

扩展SQLite支持

添加SQLite查找器实现:

class SqliteMovieFinder(MovieFinder):
    def __init__(self, movie_factory, path):
        self._database = sqlite3.connect(path)
        super().__init__(movie_factory)
    
    def find_all(self):
        with self._database as db:
            return [self._movie_factory(*row) for row in db.execute("SELECT * FROM movies")]

更新容器配置:

sqlite_finder = providers.Singleton(
    finders.SqliteMovieFinder,
    movie_factory=movie.provider,
    path=config.finder.sqlite.path,
)

lister = providers.Factory(
    listers.MovieLister,
    movie_finder=sqlite_finder,  # 切换为SQLite查找器
)

总结

通过本教程,我们实现了一个完整的CLI电影查询应用,并展示了:

  1. 如何使用Python Dependency Injector管理依赖
  2. 如何通过接口隔离不同实现
  3. 如何通过配置灵活切换组件
  4. 依赖注入如何提高代码的可测试性和可维护性

这种架构设计使得添加新的数据库格式变得非常简单,只需实现新的Finder类并在容器中配置即可,无需修改其他业务逻辑代码。这正是依赖注入模式的优势所在。

python-dependency-injector Dependency injection framework for Python python-dependency-injector 项目地址: https://gitcode.com/gh_mirrors/py/python-dependency-injector

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余纳娓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值