tux*_*21b 296 python architecture design-patterns dependency-injection inversion-of-control
在Java中,IoC/DI是一种非常常见的做法,广泛用于Web应用程序,几乎所有可用的框架和Java EE.另一方面,也有很多大的Python Web应用程序,但除了Zope(我听说代码应该非常糟糕),IoC在Python世界中似乎并不常见.(如果你认为我错了,请说出一些例子).
当然有几个流行的Java IoC框架克隆可用于Python,例如springpython.但它们似乎都没有被实际使用.至少,我从来没有在一个stumpled Django的或SQLAlchemy的 + <insert your favorite wsgi toolkit here>
,它使用类似的东西,基于Web应用程序.
在我看来,IoC具有合理的优势,并且可以很容易地替换django-default-user-model,但是在Python中广泛使用接口类和IoC看起来有点奇怪而不是"pythonic".但也许有人有更好的解释,为什么IoC没有在Python中广泛使用.
Jör*_*tag 189
我实际上并不认为DI/IoC 在Python 中不常见.什么是不常见的,但是,是DI/IoC的框架/容器.
想想看:DI容器有什么作用?它允许你
我们有"连接在一起"和"在运行时"的名称:
因此,DI容器只不过是动态脚本语言的解释器.实际上,让我重新说一下:一个典型的Java/.NET DI容器只不过是一个糟糕的解释器,用于一个非常糟糕的动态脚本语言,它有一种非常丑陋的,有时候是基于XML的语法.
当你用Python编程时,为什么你会想要使用一种丑陋的,糟糕的脚本语言,当你拥有一个漂亮,优秀的脚本语言时?实际上,这是一个更普遍的问题:当您使用几乎任何语言进行编程时,为什么当您使用Jython和IronPython时,您是否想要使用一种丑陋,糟糕的脚本语言?
因此,回顾一下:出于完全相同的原因,DI/IoC 的实践在Python中与在Java中一样重要.然而,DI/IoC 的实现内置于语言中,并且通常非常轻巧,完全消失.
(这里有一个简单的比喻:在汇编中,子程序调用是一个非常重要的交易 - 你必须将你的局部变量和寄存器保存到内存,保存你的返回地址,将指令指针更改为你调用的子程序,安排它以某种方式跳回你的子程序完成后,将参数放在被调用者可以找到它们的地方,依此类推.IOW:在汇编中,"子程序调用"是一个设计模式,之前有类似的语言Fortran内置了子程序调用,人们正在构建自己的"子程序框架".你会说Python中的子程序调用"不常见",只是因为你没有使用子程序框架吗?)
顺便说一句:有关将DI 纳入其逻辑结论的示例,请查看Gilad Bracha的新闻编程语言及其关于该主题的着作:
TM.*_*TM. 46
部分原因是模块系统在Python中的工作方式.你可以免费获得一种"单身",只需从模块中导入即可.在模块中定义对象的实际实例,然后任何客户端代码都可以导入它并实际获得一个工作的,完全构造/填充的对象.
这与Java不同,在Java中您不导入实际的对象实例.这意味着你总是必须自己实例化它们(或者使用某种IoC/DI风格的方法).您可以通过使用静态工厂方法(或实际工厂类)来减轻必须自己实例化所有内容的麻烦,但是每次实际创建新方法时仍然会产生资源开销.
Max*_*ysh 35
IoC和DI在成熟的Python代码中非常常见.由于鸭子打字,你只需要一个框架来实现DI.
最好的例子是如何使用settings.py
以下方法设置Django应用程序:
# settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': REDIS_URL + '/1',
},
'local': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'snowflake',
}
}
Run Code Online (Sandbox Code Playgroud)
Django Rest Framework大量使用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
Run Code Online (Sandbox Code Playgroud)
让我提醒一下(来源):
对于5美分的概念,"依赖注入"是一个25美元的术语.[...]依赖注入意味着为对象提供其实例变量.[...].
Dan*_*wby 34
Django充分利用了控制反转.例如,配置文件选择数据库服务器,然后框架为数据库客户端提供适当的数据库包装器实例.
不同之处在于Python具有一流的类型.数据类型(包括类)本身就是对象.如果您想要某些东西使用特定的类,只需为该类命名即可.例如:
if config_dbms_name == 'postgresql':
import psycopg
self.database_interface = psycopg
elif config_dbms_name == 'mysql':
...
Run Code Online (Sandbox Code Playgroud)
稍后代码可以通过编写以下内容来创建数据库接口
my_db_connection = self.database_interface()
# Do stuff with database.
Run Code Online (Sandbox Code Playgroud)
而不是Java和C++需要的样板工厂函数,Python使用一行或两行普通代码.这是功能与命令式编程的强项.
bca*_*lso 12
几年没有使用过Python,但我会说它与动态类型语言有关,而不是其他任何东西.举一个简单的例子,在Java中,如果我想测试那些写入标准的东西,我可以使用DI并传入任何PrintStream来捕获正在编写的文本并进行验证.然而,当我在Ruby中工作时,我可以动态地替换STDOUT上的'puts'方法来进行验证,从而使DI完全脱离图片.如果我创建抽象的唯一原因是测试使用它的类(想想文件系统操作或Java中的时钟),那么DI/IoC会在解决方案中产生不必要的复杂性.
小智 9
IoC/DI是一个设计概念,但不幸的是,它通常被视为适用于某些语言(或打字系统)的概念.我很想看到依赖注入容器在Python中变得更加流行.有Spring,但这是一个超级框架,似乎是Java概念的直接端口,没有太多考虑"Python方式".
鉴于Python 3中的Annotations,我决定对一个功能齐全但简单的依赖注入容器进行破解:https://github.com/zsims/dic.它基于.NET依赖注入容器中的一些概念(如果您曾在该空间中玩过,IMO非常棒),但是使用Python概念进行了变异.
它认为人们真的不会得到依赖注入和控制反转意味着什么.
使用控制反转的实践是拥有依赖于另一个类或函数的类或函数,但不是在函数代码类中创建实例,而是最好将其作为参数接收,因此可以实现松散耦合.这具有许多好处,因为它具有更高的可测试性并且可以实现liskov替换原则.
您可以看到,通过使用接口和注入,您的代码变得更加可维护,因为您可以轻松地更改行为,因为您不必重写一行代码(可能是DI配置中的一行或两行) class来改变它的行为,因为实现你的类等待的接口的类可以独立地变化,只要它们遵循接口.保持代码解耦和易于维护的最佳策略之一是至少遵循单一的责任,替代和依赖倒置原则.
什么是DI库,如果你可以自己在一个包中自己实例化一个对象并导入它自己注入它是什么?选择的答案是正确的,因为java没有程序部分(类之外的代码),所有这些都进入无聊的配置xml,因此需要一个类来实例化并注入依赖于延迟加载的方式,这样你就不会流失你的性能,在python上,你只需在代码的"程序"(代码外)代码段中编写注入代码
实际上,使用DI编写足够干净和紧凑的代码是非常容易的(我想,它会保持pythonic然后,但无论如何:)),例如我实际上通过这种编码方式:
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) + "!"
Run Code Online (Sandbox Code Playgroud)
_
>>greet("Peter")
Hello, dear Peter!
>>greet("Jack", rude)
Hello, Jack, you, moron!
Run Code Online (Sandbox Code Playgroud)
是的,这可以被视为参数化函数/类的一种简单形式,但它可以完成它的工作.所以,也许Python的默认包含电池也足够了.
PS我还在动态评估Python中的简单布尔逻辑时发布了一个更大的这种天真方法的例子.
小智 7
我认为由于 python 的动态特性,人们通常不会看到需要另一个动态框架。当一个类从新样式的“对象”继承时,您可以动态创建一个新变量(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()
Run Code Online (Sandbox Code Playgroud)
但是,请查看https://github.com/noodleflake/pyioc,这可能就是您要查找的内容。
即在 pyioc
from libs.service_locator import ServiceLocator
#main.py
ServiceLocator.register(PostgresConnection)
#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()
Run Code Online (Sandbox Code Playgroud)
查看 FastAPI,它内置了依赖注入。例如:
from fastapi import Depends, FastAPI
async def get_db():
db = DBSession()
try:
yield db
except Exception:
db.rollback()
raise
finally:
db.close()
app = FastAPI()
@app.get("/items")
def get_items(db=Depends(get_db)):
return db.get_items()
Run Code Online (Sandbox Code Playgroud)
小智 5
我支持"JörgWMittag"回答:"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
Run Code Online (Sandbox Code Playgroud)