代码之家  ›  专栏  ›  技术社区  ›  tux21b

为什么ioc/di在python中不常见?

  •  254
  • tux21b  · 技术社区  · 15 年前

    在爪哇 IoC / DI 是一种非常普遍的实践,广泛应用于Web应用程序中,几乎所有可用的框架和JavaEE。另一方面,也有很多大型python web应用程序,但是除了zope(我听说zope对代码来说应该非常糟糕)之外,ioc在python世界中似乎并不常见。(如果你认为我错了,请举出一些例子)。

    当然,对于Python来说,有几种流行的Java IOC框架的克隆, springpython 例如。但它们似乎都没有实际应用。至少,我从来没有遇到过 Django sqlalchemy + <insert your favorite wsgi toolkit here> 基于web的应用程序,使用类似的东西。

    在我看来,ioc有着合理的优势,例如可以很容易地替换django默认用户模型,但是在python中广泛使用接口类和ioc看起来有点奇怪,而不是pythonic。但是也许有人有更好的解释,为什么ioc在python中没有被广泛使用。

    14 回复  |  直到 7 年前
        1
  •  166
  •   Restuta    8 年前

    我不认为国际奥委会 那个 在蟒蛇中不常见。什么 然而,DI/IOC并不常见 框架/容器 .

    想想看:di容器是做什么的?它允许你

    1. 将独立的组件连接到一个完整的应用程序中…
    2. …在运行时。

    我们有“连接在一起”和“运行时”的名称:

    1. 脚本编写
    2. 动态

    因此,di容器只不过是动态脚本语言的解释器。实际上,让我重申一下:典型的Java/.net DI容器只不过是一个糟糕的动态脚本语言的蹩脚的解释器,它具有难看的、有时基于XML的语法。

    当你用python编程的时候,当你有一个漂亮的、出色的脚本语言可以使用的时候,你为什么要使用一个丑陋的、糟糕的脚本语言呢?实际上,这是一个更普遍的问题:当你用几乎任何一种语言编程时,当你拥有Jython和IronPython时,为什么要使用一种丑陋的、糟糕的脚本语言?

    所以,概括一下: 实践 在相同的原因中,DI/IOC在Python中与Java一样重要。这个 实施 然而,di/ioc是内置在语言中的,而且通常非常轻量级,以至于它完全消失了。

    (这里有一个简单的类比:在汇编中,一个子程序调用是一个相当重要的处理-你必须保存你的局部变量和寄存器到内存中,保存你的返回地址在某处,改变你调用的子程序的指令指针,安排它以某种方式跳回子例程完成后,将参数放在被调用方可以找到它们的地方,依此类推。IOW:在汇编语言中,“子例程调用”是一种设计模式,在出现像Fortran这样有内置子例程调用的语言之前,人们正在构建自己的“子例程框架”。您是否会说,子例程调用在python中是“不常见的”,仅仅因为您不使用子例程框架?)

    顺便说一句:举一个例子,看看把di带入它的逻辑结论是什么样子的 Gilad Bracha Newspeak Programming Language 以及他在这方面的著作:

        2
  •  39
  •   TM. Randy Simon    15 年前

    其中一部分是模块系统在python中的工作方式。只需从模块中导入,就可以免费获得某种“singleton”。在模块中定义一个对象的实际实例,然后任何客户机代码都可以导入它并实际获得一个工作的、完全构造/填充的对象。

    这与Java相反,在这里您不导入对象的实际实例。这意味着您必须自己实例化它们(或者使用某种ioc/di风格的方法)。您可以通过使用静态工厂方法(或实际的工厂类)来减轻必须自己实例化所有内容的麻烦,但是每次实际创建新的方法时仍然会产生资源开销。

        3
  •  35
  •   Daniel Newby    15 年前

    django很好地利用了控制反转。例如,数据库服务器由配置文件选择,然后框架向数据库客户端提供适当的数据库包装器实例。

    不同之处在于python具有一流的类型。数据类型(包括类)本身就是对象。如果您想使用某个特定的类,只需命名该类。例如:

    if config_dbms_name == 'postgresql':
        import psycopg
        self.database_interface = psycopg
    elif config_dbms_name == 'mysql':
        ...
    

    以后的代码可以通过编写以下内容来创建数据库接口:

    my_db_connection = self.database_interface()
    # Do stuff with database.
    

    代替Java和C++需要的样板工厂功能,Python用一行或两行普通代码来完成它。这就是函数式编程与命令式编程的优势所在。

        4
  •  12
  •   bcarlso    15 年前

    已经有好几年没有使用过python了,但我想说的是,它更多的是与它是一种动态类型语言有关。对于一个简单的例子,在Java中,如果我想测试一些适当地写入标准的东西,我可以使用任何打印流中的DI和PASS来捕获正在编写和验证的文本。然而,当我在ruby中工作时,我可以动态地替换stdout上的'puts'方法来进行验证,这样di就完全不在图片中了。如果我创建抽象的唯一原因是测试使用它的类(思考文件系统操作或爪哇中的时钟),那么DI/IOC会在解决方案中造成不必要的复杂性。

        5
  •  9
  •   zsims    10 年前

    ioc/di是一个设计概念,但不幸的是,它常常被当作一个适用于某些语言(或打字系统)的概念。我很想看到依赖注入容器在python中变得更加流行。这里有Spring,但这是一个超级框架,它似乎是Java概念的直接端口,而不用太多地考虑“Python方式”。

    考虑到python 3中的注释,我决定对一个功能齐全但简单的依赖注入容器进行分析: https://github.com/zsims/dic . 它基于.NET依赖项注入容器中的一些概念(如果您曾经在该空间中玩过的话,IMO是非常棒的),但它与Python概念发生了变化。

        6
  •  8
  •   Max Malysh    7 年前

    ioc和di在成熟的python代码中非常常见。由于duck类型,您不需要框架来实现di。

    最好的例子是如何使用 settings.py :

    # settings.py
    CACHES = {
        'default': {
            'BACKEND': 'django_redis.cache.RedisCache',
            'LOCATION': REDIS_URL + '/1',
        },
        'local': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'snowflake',
        }
    }
    

    django rest框架大量使用di:

    class FooView(APIView):
        # The "injected" dependencies:
        permission_classes = (IsAuthenticated, )
        throttle_classes = (ScopedRateThrottle, )
        parser_classes = (parsers.FormParser, parsers.JSONParser, parsers.MultiPartParser)
        renderer_classes = (renderers.JSONRenderer,)
    
        def get(self, request, *args, **kwargs):
            pass
    
        def post(self, request, *args, **kwargs):
            pass
    

    让我提醒一下( source ):

    “依赖注入”是5美分概念的25美元术语。[…]依赖项注入意味着为对象提供其实例变量。[…]。

        7
  •  7
  •   Community CDub    8 年前

    实际上,用di编写足够干净和紧凑的代码是相当容易的(我想知道,它会不会 蟒蛇的 然后,但无论如何:)),例如,我实际上采用了这种编码方式:

    def polite(name_str):
        return "dear " + name_str
    
    def rude(name_str):
        return name_str + ", you, moron"
    
    def greet(name_str, call=polite):
        print "Hello, " + call(name_str) + "!"
    

    γ

    >>greet("Peter")
    Hello, dear Peter!
    >>greet("Jack", rude)
    Hello, Jack, you, moron!
    

    是的,这可以被看作是参数化函数/类的一种简单形式,但是它可以完成它的工作。所以,也许python默认的电池也足够了。

    另外,我还在 Dynamically evaluating simple boolean logic in Python .

        8
  •  6
  •   Paolo Moretti    9 年前

    我支持“j_ rg w mittag”的回答:“di/ioc的python实现是如此轻量级,以至于它完全消失了”。

    为了支持这个语句,看看著名的Martin Fowler从Java移植到Python的例子: Python:Design_Patterns:Inversion_of_Control

    从上面的链接可以看到,python中的“container”可以用8行代码编写:

    class Container:
        def __init__(self, system_data):
            for component_name, component_class, component_args in system_data:
                if type(component_class) == types.ClassType:
                    args = [self.__dict__[arg] for arg in component_args]
                    self.__dict__[component_name] = component_class(*args)
                else:
                    self.__dict__[component_name] = component_class
    
        9
  •  6
  •   jhonatan teixeira    7 年前

    看起来人们真的不知道依赖注入和控制反转意味着什么了。

    使用控制反转的实践是拥有依赖于其他类或函数的类或函数,但与其用函数类代码创建实例,不如将其作为参数接收,这样就可以实现松耦合。这有很多好处,因为它更具可测试性,有助于构建liskov替换原则。

    你看,通过使用接口和注入,你的代码变得更容易维护,因为你可以很容易地改变行为,因为你不必重写类的一行代码(可能是DI配置中的一两行)来改变它的行为,因为类所等待的接口可以独立变化,只要它们遵循接口。保持代码解耦和易于维护的最佳策略之一是至少遵循单一的责任性、替换和依赖性反转原则。

    如果您可以自己在包中实例化一个对象并导入它来自己注入它,那么DI库有什么好处?选择的答案是正确的,因为Java没有程序段(类之外的代码),所有这些都进入了无聊的配置XML,因此需要一个类来实例化和注入依赖于懒惰的加载方式的依赖关系,这样就不会影响您的性能,而Python只需编写代码即可。对代码的“过程”(类外代码)部分的注入

        10
  •  4
  •   Martin Swanepoel    10 年前

    我认为由于python的动态特性,人们并不经常看到需要另一个动态框架。当类从新样式的“object”继承时,可以动态创建新变量( https://wiki.python.org/moin/NewClassVsClassicClass )

    在普通的python中:

    #application.py
    class Application(object):
        def __init__(self):
            pass
    
    #main.py
    Application.postgres_connection = PostgresConnection()
    
    #other.py
    postgres_connection = Application.postgres_connection
    db_data = postgres_connection.fetchone()
    

    不过看看 https://github.com/noodleflake/pyioc 这可能就是你要找的。

    在PYOC

    from libs.service_locator import ServiceLocator
    
    #main.py
    ServiceLocator.register(PostgresConnection)
    
    #other.py
    postgres_connection = ServiceLocator.resolve(PostgresConnection)
    db_data = postgres_connection.fetchone()
    
        11
  •  2
  •   zakmck    7 年前

    我的2美分是,在大多数Python应用程序中,你不需要它,即使你需要它,也有可能是许多Java仇恨者(以及那些认为是开发者的无能的小提琴手)认为它是不好的东西,只是因为它在Java中很受欢迎。

    当有复杂的对象网络时,ioc系统实际上是有用的,其中每个对象可能是多个其他对象的依赖关系,而反过来又是自身对其他对象的依赖关系。在这种情况下,您需要定义所有这些对象一次,并有一种机制根据尽可能多的隐式规则将它们自动组合在一起。如果您还需要由应用程序用户/管理员以简单的方式定义配置,那么这就是希望ioc系统能够从简单的xml文件(即配置文件)中读取其组件的另一个原因。

    典型的python应用程序要简单得多,只是一堆脚本,没有这么复杂的体系结构。就我个人而言,我知道ioc实际上是什么(与那些在这里写下某些答案的人相反),而且在我有限的python经验中,我从来没有感觉到需要它(而且我也没有在任何地方使用spring,而不是当它所提供的优势不能证明它的开发开销是合理的时)。

    也就是说,有些python情况下,ioc方法实际上是有用的,实际上,我在这里读到django使用它。

    上面的推理可以应用于Java世界中的面向方面编程,不同之处在于AOP真正值得的情况的数量甚至更有限。

        12
  •  -1
  •   santiagobasulto    13 年前

    我同意@jorg的观点,即di/ioc在python中是可能的、更容易的、甚至更漂亮的。缺少的是支持它的框架,但也有一些例外。举几个我想到的例子:

    • django注释允许您将自己的注释类与自定义逻辑和表单连接起来。 [More Info]

    • django允许您使用自定义配置文件对象附加到您的用户模型。这不完全是国际奥委会的做法,但却是一个好办法。就我个人而言,我想像comments框架那样替换hole用户模型。 [More Info]

        13
  •  -2
  •   Kylotan    15 年前

    在我看来,像依赖注入这样的东西是一个僵硬和过于复杂的框架的症状。当代码主体变得太重而不易更改时,您发现自己必须选择其中的一小部分,为它们定义接口,然后允许人们通过插入这些接口的对象来更改行为。这一切都很好,但最好一开始就避免这种复杂性。

    这也是静态类型语言的症状。当你唯一需要表达抽象的工具是继承的时候,那就是你在任何地方使用的工具。尽管如此,C++还是非常相似,但从来没有引起Java开发人员对建设者和接口的着迷。以写作为代价的灵活和可扩展的梦想很容易让人忘却。 far too much generic code with little real benefit . 我认为这是一种文化。

    通常,我认为python人员习惯于为工作选择合适的工具,这是一个连贯而简单的整体,而不是一个真正的工具(有上千个可能的插件),它可以做任何事情,但却提供了一系列可能的配置排列。在必要的地方仍然有可互换的部分,但是由于duck类型的灵活性和语言的相对简单性,不需要定义固定接口的大形式主义。

        14
  •  -2
  •   Jason Ching    9 年前

    与Java中的强类型性质不同。python的duck类型行为使得传递对象变得非常容易。

    Java开发人员专注于构造对象的类结构和关系,同时保持事物的灵活性。国际奥委会对实现这一目标极为重要。

    python开发人员正在集中精力完成工作。当他们需要的时候,他们就把课程连接起来。他们甚至不用担心班级的类型。只要它能呱呱叫,它就是鸭子!这种性质不给国际奥委会留下任何空间。

    推荐文章