Tại sao IoC / DI không phổ biến trong Python?


313

Trong Java IoC / DI là một thực tiễn rất phổ biến được sử dụng rộng rãi trong các ứng dụng web, gần như tất cả các khung có sẵn và Java EE. Mặt khác, cũng có rất nhiều ứng dụng web Python lớn, nhưng bên cạnh Zope (mà tôi đã nghe thấy nên rất kinh khủng khi viết mã) IoC dường như không phổ biến trong thế giới Python. (Vui lòng kể tên một số ví dụ nếu bạn nghĩ rằng tôi sai).

Tất nhiên, có một số bản sao của các khung công tác Java IoC phổ biến có sẵn cho Python, springpython chẳng hạn. Nhưng không ai trong số họ dường như được sử dụng thực tế. Ít nhất, tôi chưa bao giờ vấp phải một ứng dụng web dựa trên Django hoặc sqlalchemy + <insert your favorite wsgi toolkit here>sử dụng cái gì đó tương tự.

Theo tôi, IoC có những lợi thế hợp lý và sẽ giúp dễ dàng thay thế mô hình người dùng mặc định django, nhưng việc sử dụng rộng rãi các lớp giao diện và IoC trong Python có vẻ hơi kỳ lạ và không »pythonic«. Nhưng có lẽ ai đó có một lời giải thích tốt hơn, tại sao IoC không được sử dụng rộng rãi trong Python.


2
Tôi đoán, cùng một lý do rằng nó ít phổ biến hơn trong Ruby, các mixin tích hợp và các lớp mở
Sam Saffron 17/03/2016

3
Bạn đã bao giờ thử springpython? Nó thậm chí không hoạt động như quảng cáo. ít nhất là trong phần aop. mọi thứ khác trong đó không hữu ích lắm trừ khi bạn đến từ java và cần một số mức độ thoải mái trong quá trình chuyển đổi.
Tom Willis

6
Xin lưu ý để phân biệt giữa việc sử dụng DI và sử dụng khung IOC. Cái trước là một mẫu thiết kế, cái sau là một khung để hỗ trợ việc sử dụng tự động cái trước.
Doug

Doug, tôi tin rằng bạn muốn nói DI là tính năng sáng tạo có được bằng cách sử dụng mẫu Trang trí.
njappboy

4
Tôi rất muốn thấy một câu trả lời giải quyết các vấn đề trong thế giới thực mà DI giải quyết: Quản lý trọn đời, dễ kiểm tra, v.v. Nếu có cách nào khác của Pythonic để giải quyết những vấn đề này, tôi là tất cả.
Josh Noe

Câu trả lời:


198

Tôi không thực sự nghĩ rằng DI / IoC là rằng không phổ biến trong Python. Có gì phổ biến, tuy nhiên, DI / IoC khuôn khổ / container .

Hãy nghĩ về nó: một container DI làm gì? Nó cho phép bạn

  1. kết nối các thành phần độc lập với nhau thành một ứng dụng hoàn chỉnh ...
  2. ... trong thời gian chạy.

Chúng tôi có tên cho "nối với nhau" và "trong thời gian chạy":

  1. kịch bản
  2. năng động

Vì vậy, một container DI không là gì ngoài trình thông dịch cho ngôn ngữ kịch bản động. Trên thực tế, hãy để tôi nói lại rằng: một thùng chứa Java / .NET DI điển hình không là gì ngoài một trình thông dịch nhảm nhí cho một ngôn ngữ kịch bản động thực sự xấu với cú pháp xấu xí, đôi khi dựa trên XML.

Khi bạn lập trình bằng Python, tại sao bạn muốn sử dụng một ngôn ngữ kịch bản xấu, xấu khi bạn có một ngôn ngữ kịch bản đẹp, tuyệt vời theo ý của bạn? Trên thực tế, đó là một câu hỏi chung chung hơn: khi bạn lập trình bằng bất kỳ ngôn ngữ nào, tại sao bạn lại muốn sử dụng một ngôn ngữ kịch bản xấu, xấu khi bạn có Jython và IronPython theo ý của bạn?

Vì vậy, để tóm tắt lại: việc thực hành DI / IoC cũng quan trọng trong Python như trong Java, vì những lý do chính xác như vậy. Việc thực hiện của DI / IoC tuy nhiên, được xây dựng vào ngôn ngữ và thường rất nhẹ rằng nó hoàn toàn biến mất.

. sắp xếp cho nó bằng cách nào đó quay trở lại chương trình con của bạn khi nó kết thúc, đặt các đối số ở đâu đó mà callee có thể tìm thấy chúng, v.v. Fortran có các cuộc gọi chương trình con được xây dựng, mọi người đang xây dựng "khung chương trình con" của riêng họ. Bạn có nói rằng các cuộc gọi chương trình con là "không phổ biến" trong Python, chỉ vì bạn không sử dụng khung chương trình con?)

BTW: cho một ví dụ về những gì có vẻ như để tận DI để kết luận hợp lý của nó, hãy nhìn vào Gilad Bracha 's Newspeak Lập trình Ngôn ngữ và tác phẩm của ông về đề tài này:


58
Trong khi tôi đồng ý. Nhận xét XML là sai. Nhiều (ít nhất là các thùng chứa IOC hiện đại) sử dụng quy ước (mã) trên cấu hình (XML).
Fingerlas

20
Không có gì ngăn cản bạn viết hệ thống dây một cách rõ ràng bằng Java, nhưng khi bạn càng có nhiều dịch vụ, thì sự phụ thuộc trở nên phức tạp hơn. Một thùng chứa DI giống như Make: bạn khai báo các phụ thuộc và container khởi tạo chúng theo đúng thứ tự. Guice là một khung công tác Java DI nơi mọi thứ được viết bằng mã Java. Bằng cách viết khai báo, bộ chứa DI cũng bổ sung hỗ trợ xử lý bài đăng các bộ giải mã trước khi khởi tạo (ví dụ: thay thế chỗ dành cho tài sản bằng giá trị thực)
IttayD

133
"Tuy nhiên, việc triển khai DI / IoC được tích hợp vào ngôn ngữ và thường nhẹ đến mức nó hoàn toàn biến mất." Bỏ phiếu vì điều này là không đúng sự thật. DI là một mẫu trong đó một giao diện được truyền vào hàm tạo. Nó không được tích hợp trong python.
Doug

146
downvote, nối dây với nhau không liên quan gì đến kịch bản, DI là một mô hình và nó không tương đương với kịch bản
Luxspes 23/12/12

38
Tôi không đồng ý với điều này. DI không giải quyết được việc thiếu kịch bản động trong các ngôn ngữ tĩnh. Nó cung cấp một khung để cấu hình và soạn thảo các phần của ứng dụng của bạn. Tôi đã từng nghe một nhà phát triển Ruby nói rằng DI không cần thiết trong các ngôn ngữ động. Nhưng anh ta đã sử dụng Rails ... Rails chỉ là một thùng chứa DI loại lớn, sử dụng quy ước để tìm ra phần nào sẽ cấu hình khi nào. Anh ta không cần DI vì Rails đã giải quyết vấn đề tìm kiếm các bộ phận cho anh ta.
Brian Genisio

51

Một phần của nó là cách hệ thống mô-đun hoạt động trong Python. Bạn có thể nhận được một loại "singleton" miễn phí, chỉ bằng cách nhập nó từ một mô-đun. Xác định một thể hiện thực tế của một đối tượng trong một mô-đun, và sau đó bất kỳ mã máy khách nào cũng có thể nhập nó và thực sự có được một đối tượng hoạt động, được xây dựng / xây dựng đầy đủ.

Điều này trái ngược với Java, nơi bạn không nhập các thể hiện thực tế của các đối tượng. Điều này có nghĩa là bạn luôn phải tự khởi tạo chúng, (hoặc sử dụng một số cách tiếp cận kiểu IoC / DI). Bạn có thể giảm thiểu rắc rối khi phải tự khởi tạo mọi thứ bằng cách có các phương thức tĩnh của nhà máy (hoặc các lớp nhà máy thực tế), nhưng sau đó bạn vẫn phải chịu chi phí tài nguyên của việc thực sự tạo ra các phương thức mới mỗi lần.


2
Điều đó có ý nghĩa. Nếu tôi muốn thay đổi triển khai trong Python, tôi chỉ cần nhập từ một vị trí khác bằng cùng tên. Nhưng bây giờ tôi đang suy nghĩ nếu nó cũng có thể theo cách khác bằng cách định nghĩa một MyClassInstanceslớp cho mỗi lớp MyClasstrong Java, chỉ chứa các cá thể tĩnh, được khởi tạo đầy đủ. Đó sẽ là có dây: D
tux21b 17/03/2016

2
Và một ý tưởng khác: Cung cấp một cách thay đổi nhập khẩu như vậy trong python sẽ giúp nó có thể thay thế việc triển khai dễ dàng mà không cần chạm vào tất cả các tệp python. Thay vì from framework.auth.user import User có thể tốt hơn để viết User = lookup('UserImplentation', 'framework.auth.user.User')(tham số thứ 2 có thể là giá trị mặc định) bên trong khung. Sau đó, người dùng của khung sẽ có thể thay thế / chuyên môn hóa việc Userthực hiện mà không cần chạm vào khung.
tux21b

14
Đơn giản hóa, trả lời, trong cuộc sống thực, bạn hiếm khi chỉ cần "một người độc thân", bạn cần kiểm soát phạm vi (bạn có thể cần một chủ đề đơn lẻ cục bộ, hoặc một phiên đơn, v.v.), điều này khiến tôi nghĩ rằng đó là loại vấn đề giải quyết bằng Python không phải là loại vấn đề trong thế giới thực được giải quyết trong môi trường doanh nghiệp
Luxspes 23/12/12

3
Trên thực tế DI là về việc có thể kiểm tra và tách rời các phụ thuộc của mã. Ngoài ra, tính năng nhập tương tự như nhập tĩnh trong Java, cho phép tôi nhập một phiên bản duy nhất của một đối tượng.
Richard Warburton

1
"Bạn có thể nhận được một loại" singleton "miễn phí, chỉ bằng cách nhập nó từ một mô-đun." Có thể dễ dàng thực hiện trong Java bằng cách khai báo một trường đối tượng tĩnh và đặt nó thành một giá trị. Đây không phải là một sol
ggranum

45

IoC và DI là siêu phổ biến trong mã Python trưởng thành. Bạn không cần một khung để thực hiện DI nhờ gõ vịt.

Ví dụ tốt nhất là cách bạn thiết lập ứng dụng Django bằng cách sử dụng 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 Framework sử dụng DI rất nhiều:

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

Hãy để tôi nhắc ( nguồn ):

"Dependency Injection" là một thuật ngữ 25 đô la cho khái niệm 5 xu. [...] Tiêm phụ thuộc có nghĩa là đưa ra một đối tượng các biến đối tượng của nó. [...].


8
+1. Vâng đặt. Là một lập trình viên Python, tôi hoàn toàn bị bối rối bởi toàn bộ bài thuyết trình phỏng vấn về các khung DI trong C #. Mất một lúc tôi mới nhận ra tôi đã làm điều đó mọi lúc trong các ứng dụng Flask mà không hề nghĩ về nó bởi vì bạn không cần một khuôn khổ. Đối với một người không biết gì ngoài C # / Java, câu hỏi có ý nghĩa. Đối với các lập trình viên ngôn ngữ gõ vịt, đó chỉ là điều tự nhiên và như bạn nói, "thuật ngữ 25 đô la cho khái niệm 5 xu".
Samuel Harmer

5
err ... đây không phải là phép tiêm phụ thuộc vì các thể hiện ( IsAuthenticated, ScopedRateThrottle) được lớp khởi tạo. Chúng không được truyền vào hàm tạo.
dopatraman

5
IsAuthenticatedScopedRateThrottlekhông phải là trường hợp, đây là các lớp. Chúng được khởi tạo khi một FooView được xây dựng (thực tế, khi FooView xử lý một yêu cầu). Dù sao, đây chỉ là một chi tiết thực hiện. IsAuthenticatedScopedRateThrottlelà những phụ thuộc; họ được tiêm vào FooView. Nó không quan trọng khi nào hoặc làm thế nào điều này được thực hiện. Python không phải là Java, vì vậy có nhiều cách khác nhau để thực hiện điều này.
Max Malysh

3
@MaxMalysh Tôi đồng ý với dopatraman về điều này. Điều này thậm chí không phải là IoC vì bản thân lớp này có các phụ thuộc "được mã hóa cứng" cho một lớp cụ thể. Trong IoC, shoudl phụ thuộc được cung cấp thay vì mã hóa cứng. Trên hết, trong Dependency Injection, bạn sẽ có một thực thể chịu trách nhiệm quản lý vòng đời của mỗi dịch vụ và tiêm chúng khi đó là trường hợp. Các giải pháp được cung cấp trong không phải bất kỳ trong số đó.
Ricardo Alves

3
@alex Không, bạn không cần thay đổi mã của mình để sử dụng trình kết xuất khác. Bạn thậm chí có thể sử dụng nhiều trình kết xuất cùng một lúc : renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer). Mocking là đơn giản như @unittest.patch('myapp.views.FooView.permission_classes'). Một nhu cầu tuyệt vọng để "vượt qua một cái gì đó" là kết quả của "cách làm Java" do Java là một ngôn ngữ được biên dịch và gõ tĩnh thiếu khả năng lập trình siêu dữ liệu mạnh.
Max Malysh

35

Django sử dụng tuyệt vời của đảo ngược kiểm soát. Ví dụ, máy chủ cơ sở dữ liệu được chọn bởi tệp cấu hình, sau đó khung cung cấp các phiên bản trình bao bọc cơ sở dữ liệu thích hợp cho các máy khách cơ sở dữ liệu.

Sự khác biệt là Python có các loại hạng nhất. Các kiểu dữ liệu, bao gồm các lớp, chính chúng là các đối tượng. Nếu bạn muốn một cái gì đó để sử dụng một lớp cụ thể, chỉ cần đặt tên cho lớp. Ví dụ:

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

Mã sau này có thể tạo giao diện cơ sở dữ liệu bằng cách viết:

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

Thay vì các hàm nhà máy soạn sẵn mà Java và C ++ cần, Python thực hiện nó với một hoặc hai dòng mã thông thường. Đây là sức mạnh của lập trình chức năng so với mệnh lệnh.


4
Những gì bạn gọi mã thực sự là phần dây. Đó sẽ là XML của khung ioc của bạn. Nó thực sự có thể được viết đơn giản là import psycopg2 as database_interface. Đặt dòng đó trong một injections.pyet voilà.
quang phổ

29
Ơ Những gì bạn đang làm có khá nhiều sách giáo khoa bắt buộc Daniel.
Shayne

Đó chắc chắn là mã bắt buộc, nhưng đó là loại chức năng bởi vì nó sử dụng một giá trị gọi được như một giá trị.
Jeremy

5
Không phải đó chỉ là Chức năng hạng nhất sao? vi.wikipedia.org/wiki/First- class_feft Chỉ vì bạn có và sử dụng chúng không làm cho mã của bạn có chức năng. Có khá nhiều tác dụng phụ xảy ra ở đây (chẳng hạn như thay đổi self.database_interface), trong đó hét lên bắt buộc.
hjc1710

15

Nó cho thấy rằng mọi người thực sự không nhận được những gì Phụ thuộc tiêm và đảo ngược kiểm soát có nghĩa là nữa.

Việc sử dụng nghịch đảo điều khiển là có các lớp hoặc hàm phụ thuộc vào các lớp hoặc hàm khác, nhưng thay vì tạo các thể hiện với lớp mã chức năng, tốt hơn là nhận nó như một tham số, do đó có thể lưu trữ khớp nối lỏng lẻo. Điều đó có nhiều lợi ích như khả năng kiểm tra nhiều hơn và để lưu trữ nguyên tắc thay thế liskov.

Bạn thấy, bằng cách làm việc với các giao diện và phép tiêm, mã của bạn sẽ dễ bảo trì hơn, vì bạn có thể thay đổi hành vi một cách dễ dàng, vì bạn sẽ không phải viết lại một dòng mã (có thể là một hoặc hai dòng trên cấu hình DI) của bạn lớp để thay đổi hành vi của nó, vì các lớp thực hiện giao diện mà lớp bạn đang chờ có thể thay đổi độc lập miễn là chúng tuân theo giao diện. Một trong những chiến lược tốt nhất để giữ cho mã tách rời và dễ bảo trì là tuân theo ít nhất các nguyên tắc đảo ngược, thay thế và phụ thuộc duy nhất.

Thư viện DI có ích gì nếu bạn có thể tự khởi tạo một đối tượng trong một gói và nhập nó để tự tiêm? Câu trả lời được chọn là đúng, vì java không có các phần thủ tục (mã bên ngoài các lớp), tất cả đều đi vào cấu hình nhàm chán của xml, do đó cần một lớp để khởi tạo và tiêm các phụ thuộc vào thời trang tải lười biếng để bạn không thổi bay hiệu suất của bạn, trong khi trên python, bạn chỉ cần mã hóa các mũi tiêm trên các phần "thủ tục" (mã bên ngoài các lớp) trong mã của bạn


bạn vẫn nhớ rằng một IoC / DI tự động nối các đối tượng lại với nhau. Không có nhiều khả năng để làm điều đó trong thời gian chạy (dù sao Java cũng có thể thực hiện điều đó thông qua sự phản chiếu), đó là khung công tác chăm sóc nó và bạn không cần phải làm điều đó một cách rõ ràng. Việc có các phần thủ tục cũng không liên quan, không có gì ngăn người ta viết một ứng dụng hoàn toàn theo thủ tục trong Java, bằng cách sử dụng các lớp như là các thùng chứa các chương trình con và hàm tĩnh, hoàn toàn không sử dụng các tính năng OOP.
zakmck

@zakmck: phần "thủ tục" của Python ở đây không thực sự là về việc viết mã thủ tục. Điều làm cho phần "thủ tục" của Python khác với ngôn ngữ tĩnh là khả năng đặt mã thủ tục trong phần thân lớp, chạy trong thời gian định nghĩa lớp và đặt các câu lệnh nhập bên trong câu lệnh if và để tạo nhà máy lớp chỉ bằng cách định nghĩa các lớp bên trong một phương pháp nhà máy. Đây là những điều bạn không thể thực sự làm trong các ngôn ngữ tĩnh và giải quyết hầu hết các vấn đề mà IOC / DI đã cố gắng giải quyết. Siêu lập trình trong Python thường trông giống như mã Python thông thường.
Nói dối Ryan

@LieRyan, bạn có thể làm điều đó với sự phản chiếu hoặc, nếu bạn cần nó thường xuyên hoặc trong thời gian chạy, bạn có thể gọi ngôn ngữ tĩnh từ một ngôn ngữ khác như Groovy (được thiết kế để dễ dàng chơi với Java) hoặc thậm chí là chính Python. Tuy nhiên, điều đó ít liên quan đến các khung IoC / DI, vì mục đích của chúng là thực hiện hầu hết các hệ thống dây đối tượng thủ tục cho bạn, chỉ sử dụng các định nghĩa. Đáng buồn thay, hầu hết các câu trả lời ở đây bỏ lỡ điểm này.
zakmck

12

Không sử dụng Python trong vài năm, nhưng tôi sẽ nói rằng nó có liên quan nhiều hơn với nó là một ngôn ngữ được gõ động hơn bất kỳ thứ gì khác. Ví dụ đơn giản, trong Java, nếu tôi muốn kiểm tra một cái gì đó được viết theo tiêu chuẩn một cách phù hợp, tôi có thể sử dụng DI và chuyển vào bất kỳ PrintStream nào để chụp văn bản được viết và xác minh nó. Tuy nhiên, khi tôi làm việc trong Ruby, tôi có thể tự động thay thế phương thức 'đặt' trên STDOUT để xác minh, khiến DI hoàn toàn không có hình ảnh. Nếu lý do duy nhất tôi tạo ra một sự trừu tượng là để kiểm tra lớp đang sử dụng nó (nghĩ rằng hoạt động của hệ thống tệp hoặc đồng hồ trong Java) thì DI / IoC tạo ra sự phức tạp không cần thiết trong giải pháp.


3
Tôi không bao giờ hết ngạc nhiên rằng mọi người sẵn sàng thay đổi cách một hệ thống hoạt động để kiểm tra xem nó hoạt động như thế nào. Bây giờ bạn cần kiểm tra xem xét nghiệm của bạn không gây ra tác dụng phụ.
Cơ bản

2
Ông nói về việc thay đổi phương thức đặt chỉ trong phạm vi thử nghiệm, nó giống như phương pháp giả của đối tượng được tiêm.
dpa

2
@Basic điều đó khá bình thường trong các bài kiểm tra đơn vị , thực sự nên làm điều đó trong các bài kiểm tra này vì bạn không muốn làm ô nhiễm phạm vi kiểm tra trường hợp của bạn với nhiều hơn một khối mã (một bài kiểm tra). Sẽ là sai khi làm điều đó cho các bài kiểm tra tích hợp, có lẽ đó là những gì bạn đang đề cập đến bình luận của bạn?
samuelgrigolato

1
Đối với tôi, khả năng kiểm tra là một mối quan tâm hạng nhất. Nếu một thiết kế không thể kiểm tra được thì đó không phải là một thiết kế tốt và tôi không có vấn đề gì khi thay đổi thiết kế để làm cho nó dễ kiểm tra hơn. Tôi sẽ phải xác nhận lại rằng nó vẫn hoạt động, nhưng không sao. Khả năng kiểm tra là một lý do hoàn toàn hợp lệ để thay đổi mã IMO
Carlos Rodriguez

10

Trên thực tế, khá dễ dàng để viết mã đủ sạch và gọn với DI (tôi tự hỏi, nó sẽ / ở lại pythonic , nhưng dù sao :)), ví dụ tôi thực sự quan tâm đến cách mã hóa này:

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!

Vâng, điều này có thể được xem như chỉ là một hình thức đơn giản của các hàm / lớp tham số hóa, nhưng nó thực hiện công việc của nó. Vì vậy, có lẽ pin bao gồm mặc định của Python cũng đủ ở đây.

PS Tôi cũng đã đăng một ví dụ lớn hơn về cách tiếp cận ngây thơ này tại Đánh giá động về logic boolean đơn giản trong Python .


3
Đối với các trường hợp đơn giản có thể hoạt động, nhưng chỉ cần tưởng tượng một trình điều khiển blog web đơn giản, sử dụng các mô hình khác nhau (Đăng, Nhận xét, Người dùng). Nếu bạn muốn người dùng tiêm mô hình Bài đăng của riêng mình (với thuộc tính lượt xem bổ sung để theo dõi điều đó) và mô hình Người dùng của riêng anh ta với nhiều thông tin hồ sơ hơn, v.v., tất cả các tham số có thể trông khó hiểu. Ngoài ra, người dùng cũng có thể muốn thay đổi đối tượng Yêu cầu, để hỗ trợ phiên hệ thống tệp thay vì phiên dựa trên cookie đơn giản hoặc một cái gì đó tương tự ... Vì vậy, bạn sẽ sớm có rất nhiều tham số.
tux21b

1
@ tux21b Vâng, có "sự phức tạp thiết yếu" mà người dùng muốn ứng dụng thực hiện, có các giải pháp kiến ​​trúc cho nó (một số trong đó không tệ hơn phần còn lại về phát triển và có thể là thời gian bảo trì, tốc độ, v.v. ) và có khả năng con người hiểu được kiến ​​trúc API và phần mềm. Nếu không có giải pháp nào dễ hiểu cho con người (không chỉ trong số những người sử dụng (bất kỳ hình thức nào) DI) ... thì ai đã nói rằng tất cả các vấn đề đều có thể giải quyết được? Và việc có nhiều tham số được gán mặc định (nhưng có thể thay đổi theo lựa chọn của người dùng) thực sự có thể đủ thường xuyên.
mlvljr 18/03/2016

9

IoC / DI là một khái niệm thiết kế, nhưng thật không may, nó thường được coi là một khái niệm áp dụng cho các ngôn ngữ nhất định (hoặc hệ thống gõ). Tôi muốn thấy các thùng chứa phụ thuộc trở nên phổ biến hơn nhiều trong Python. Có Spring, nhưng đó là một siêu khung và dường như là một cổng trực tiếp của các khái niệm Java mà không cần quan tâm nhiều đến "The Python Way".

Đưa ra chú thích trong Python 3, tôi quyết định có một vết nứt tại một thùng chứa tiêm phụ thuộc đầy đủ tính năng, nhưng đơn giản: https://github.com/zsims/dic . Nó dựa trên một số khái niệm từ một thùng chứa phụ thuộc .NET (IMO thật tuyệt vời nếu bạn từng chơi trong không gian đó), nhưng bị biến đổi với các khái niệm Python.


6

Tôi nghĩ do tính chất năng động của con trăn, người ta thường không thấy sự cần thiết phải có một khung năng động khác. Khi một lớp kế thừa từ 'đối tượng' kiểu mới, bạn có thể tự động tạo một biến mới ( https://wiki.python.org/moin/NewClassVsClassicClass ).

tức là trong trăn đồng bằng:

#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()

Tuy nhiên, hãy xem https://github.com/noodleflower/pyioc đây có thể là những gì bạn đang tìm kiếm.

tức là trong pyioc

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()

2
Thực tế là cả hai phiên bản đều có cùng số lượng mã đi một chặng đường dài để giải thích lý do tại sao sử dụng khung không phổ biến lắm.
quang phổ

Trong other.pydòng 1, có một độ phân giải phụ thuộc tự động, nhưng sẽ không tính đó là tiêm phụ thuộc.
andho

Định vị dịch vụ thường là một mô hình chống, chỉ cần nói.
PmanAce

6

Tôi trả lời "Jorg W Mittag": "Việc triển khai DI / IoC của Python rất nhẹ đến nỗi nó hoàn toàn biến mất".

Để sao lưu tuyên bố này, hãy xem ví dụ nổi tiếng của Martin Fowler được chuyển từ Java sang Python: Python: Design_Potypes: Inversion_of_Control

Như bạn có thể thấy từ liên kết trên, một "Container" trong Python có thể được viết bằng 8 dòng mã:

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

42
Điều này thua xa các container DI yếu nhất. Quản lý trọn đời ở đâu, độ phân giải phụ thuộc đệ quy, khả năng giả định hoặc - thất bại tất cả - cấu hình? Điều này không gì khác hơn là một kiểu tra cứu và bộ đệm không giống với IoC.
Cơ bản

2
Cách đây nhiều năm, tôi đã viết một khung DI nhỏ sử dụng siêu dữ liệu như một bài tập. Toàn bộ điều này là một tập tin duy nhất với số lần nhập và tài liệu không làm cho nó tự giải thích. Điều đó cho thấy các tính năng cơ bản không khó thực hiện theo cách thậm chí là "pythonic", nhưng tôi thực sự nghĩ rằng thật đáng buồn khi không có giải pháp hoàn chỉnh nào có được lực kéo lớn như Spring có trong Java và mọi người đang thực hiện các kiến ​​trúc plugin tùy chỉnh.
Andrea Ratto

2

2cents của tôi là trong hầu hết các ứng dụng Python bạn không cần nó và thậm chí nếu bạn cần nó, rất có thể nhiều người ghét Java (và những người chơi trò chơi không đủ năng lực tin rằng họ là nhà phát triển) coi nó là một thứ gì đó tồi tệ, chỉ vì nó phổ biến trong Java .

Một hệ thống IoC thực sự hữu ích khi bạn có các mạng đối tượng phức tạp, trong đó mỗi đối tượng có thể là một phụ thuộc cho một số người khác và đến lượt mình, chính nó lại phụ thuộc vào các đối tượng khác. Trong trường hợp như vậy, bạn sẽ muốn xác định tất cả các đối tượng này một lần và có cơ chế tự động đặt chúng lại với nhau, dựa trên càng nhiều quy tắc ngầm càng tốt. Nếu người dùng / quản trị viên ứng dụng xác định cấu hình theo cách đơn giản, đó là một lý do bổ sung để mong muốn một hệ thống IoC có thể đọc các thành phần của nó từ một cái gì đó giống như một tệp XML đơn giản (sẽ là cấu hình).

Ứng dụng Python điển hình đơn giản hơn nhiều, chỉ là một loạt các tập lệnh, không có kiến ​​trúc phức tạp như vậy. Cá nhân tôi nhận thức được IoC thực sự là gì (trái với những người đã viết câu trả lời nhất định ở đây) và tôi chưa bao giờ cảm thấy cần nó trong trải nghiệm Python hạn chế của mình (tôi cũng không sử dụng Spring ở mọi nơi, không phải khi có lợi thế nó không cho phép chứng minh chi phí phát triển của nó).

Điều đó nói rằng, có những tình huống Python trong đó cách tiếp cận IoC thực sự hữu ích và trên thực tế, tôi đã đọc ở đây rằng Django sử dụng nó.

Lý do tương tự ở trên có thể được áp dụng cho Lập trình hướng theo khía cạnh trong thế giới Java, với sự khác biệt là số trường hợp AOP thực sự đáng giá thậm chí còn hạn chế hơn.


Có một URL tham chiếu đến nguồn thông tin mà django sử dụng IoC không?
Sajuuk

@Sajuuk, tôi đã biết rằng về Django về chủ đề của câu hỏi này, vì vậy tôi không biết, bạn nên hỏi các tác giả câu trả lời khác.
zakmck

Theo tôi, câu trả lời đầu tiên của câu trả lời này có thêm 0 giá trị theo quan điểm của tôi ... Tôi nghĩ rằng tôi có khả năng quyết định khi nào mã trăn của tôi sẽ được hưởng lợi từ IoC và tôi không quan tâm đến việc nhà phát triển nghĩ gì là xấu. Tôi coi trọng tính thực dụng hơn những ý kiến ​​không có căn cứ.
Mike de Klerk

@MikedeKlerk Đề nghị của tôi là một cái gì đó chưa được biết đến (như nhiều câu trả lời chứng minh) và nạn nhân của định kiến ​​không có khả năng trở nên phổ biến, bất kể khách quan và thông tin tốt như một số ít như bạn. Và tất nhiên tôi không chắc đây là lý do tại sao bạn không thấy nhiều cách sử dụng IoC trong Python, tôi nghĩ lý do chính là các ứng dụng tương đương thấp / trung bình không cần đến chúng.
zakmck

The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.- khá giả định
hyankov


-1

Tôi đồng ý với @Jorg ở điểm DI / IoC là có thể, dễ dàng hơn và thậm chí còn đẹp hơn trong Python. Điều còn thiếu là các khung hỗ trợ nó, nhưng có một vài ngoại lệ. Để chỉ ra một vài ví dụ xuất hiện trong đầu tôi:

  • Nhận xét Django cho phép bạn kết nối lớp Nhận xét của riêng bạn với logic và biểu mẫu tùy chỉnh của bạn. [Thêm thông tin]

  • Django cho phép bạn sử dụng một đối tượng Hồ sơ tùy chỉnh để đính kèm vào mô hình Người dùng của bạn. Đây không phải là hoàn toàn IoC nhưng là một cách tiếp cận tốt. Cá nhân tôi muốn thay thế mô hình Người dùng lỗ như khung nhận xét. [Thêm thông tin]


-3

Theo tôi, những thứ như tiêm phụ thuộc là triệu chứng của một khuôn khổ cứng nhắc và quá phức tạp. Khi phần chính của mã trở nên quá nặng để dễ dàng thay đổi, bạn thấy mình phải chọn các phần nhỏ của nó, xác định giao diện cho chúng và sau đó cho phép mọi người thay đổi hành vi thông qua các đối tượng cắm vào các giao diện đó. Đó là tất cả tốt và tốt, nhưng tốt hơn hết là tránh sự phức tạp đó ngay từ đầu.

Đó cũng là triệu chứng của một ngôn ngữ gõ tĩnh. Khi công cụ duy nhất bạn phải thể hiện sự trừu tượng là sự kế thừa, thì đó là những gì bạn sử dụng ở mọi nơi. Phải nói rằng, C ++ khá giống nhau nhưng không bao giờ thu hút được niềm đam mê với Nhà xây dựng và Giao diện ở mọi nơi mà các nhà phát triển Java đã làm. Thật dễ dàng để trở nên quá phấn khích với giấc mơ trở nên linh hoạt và có thể mở rộng với chi phí viết quá nhiều mã chung chung với rất ít lợi ích thực sự . Tôi nghĩ đó là một điều văn hóa.

Thông thường tôi nghĩ rằng người Python thường chọn công cụ phù hợp cho công việc, đó là một tổng thể mạch lạc và đơn giản, thay vì Công cụ One True (Với hàng ngàn plugin có thể làm được) nhưng cung cấp một loạt các hoán vị cấu hình có thể gây hoang mang . Vẫn có những phần có thể hoán đổi cho nhau khi cần thiết, nhưng không cần sự hình thức lớn trong việc xác định giao diện cố định, do tính linh hoạt của việc gõ vịt và sự đơn giản tương đối của ngôn ngữ.


4
Nó không phải là quá nhiều khung như ngôn ngữ chính nó. Để tạo ra loại linh hoạt mà các ngôn ngữ gõ vịt thích, các ngôn ngữ gõ tĩnh cần các khung và quy tắc rất tinh vi. DI là một trong những quy tắc đó. Người Python không nghĩ hai lần về. Người Java phải thực sự làm việc với nó.
S.Lott 17/03/2016

6
@ S.Lott - Tôi hoàn toàn đồng ý với bạn, ngoại trừ người C ++ dường như có được mà không có sự bùng nổ của các mẫu thiết kế và kiến ​​trúc, mặc dù làm việc với các hạn chế tương tự như của Java. Tôi nghĩ rằng điều đó hàm ý một sự khác biệt về văn hóa, khi phải đối mặt với 2 cách có thể để làm một cái gì đó, người Java thích trích xuất một giao diện khác để tạo điều kiện cho mẫu Chiến lược trong khi người C ++ nhúng ngay vào và thêm một bool và một câu lệnh if ...
Kylotan 17/03/2016

3
@Finglas vì vậy nếu tôi có cả tá lớp sử dụng EmailSendervà quyết định thay thế nó bằng a DesktopNotifier, tôi phải đi chỉnh sửa 12 lớp bằng tay. Và bạn nghĩ rằng đơn giản và gọn gàng hơn khi chỉ viết vào một INotifiergiao diện và để cho container hoạt động chi tiết?
Cơ bản

1
Thật không may, một mức độ phức tạp nhất định là một thực tế mà các nhà phát triển phần mềm chuyên nghiệp phải đối mặt. Tôi thấy những lời chỉ trích nhưng không có giải pháp trong câu trả lời này. Giải pháp "pythonic" cho vấn đề này là gì: Tôi đang viết thư viện và tôi muốn cung cấp một cái móc để ghi nhật ký (một cái gì đó giống như PSR-3 LoggerInterface của PHP). Tôi biết cách sử dụng các mức nhật ký, nhưng tôi không quan tâm làm thế nào chương trình thực sự báo cáo chúng. Cách sạch sẽ để cho phép ứng dụng khách tiêm chi tiết triển khai đó là gì. Lưu ý: các phần khác của ứng dụng có thể có các cài đặt khác nhau của giao diện này.
Cướp

2
Câu hỏi của tôi cho bạn không phải là làm thế nào để bạn sử dụng thư viện ghi nhật ký tiêu chuẩn, cũng không phải là về việc tạo các phiên bản khác nhau của một lớp logger. Câu hỏi của tôi là làm thế nào để bạn định cấu hình ứng dụng của mình để các phần khác nhau trong ứng dụng của bạn có thể sử dụng các triển khai khác nhau và không quan tâm đến các chi tiết đó (miễn là chúng biết cách sử dụng giao diện). Đây là một vấn đề rất thực tế mà DI đã giải quyết cho nhiều ứng dụng PHP mà tôi đã làm việc. Tôi đang tìm kiếm con trăn tương đương. Và đề xuất "đừng làm cho ứng dụng của bạn phức tạp" không phải là câu trả lời tôi đang tìm kiếm.
Cướp

-5

Không giống như bản chất gõ mạnh trong Java. Hành vi gõ vịt của Python giúp dễ dàng vượt qua các đối tượng xung quanh.

Các nhà phát triển Java đang tập trung vào việc xây dựng cấu trúc lớp và mối quan hệ giữa các đối tượng, trong khi giữ cho mọi thứ linh hoạt. IoC là cực kỳ quan trọng để đạt được điều này.

Các nhà phát triển Python đang tập trung vào việc hoàn thành công việc. Họ chỉ nối dây các lớp khi họ cần. Họ thậm chí không phải lo lắng về loại lớp học. Miễn là nó có thể quẫy, đó là một con vịt! Bản chất này không có chỗ cho IoC.


4
Bạn vẫn cần phải tìm một thứ mà lang băm.
andho
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.