Warning, at 4:26: never use a mutable variable as an argument! This is because the same object will be reused if you call the function/method several times! This is also a common mistake when you want an empty list. A solution is to not provide a default value, but a default factory. Apart from this, thanks Arjan for the video :) Very good, as always! The new example is refreshing and is well appropriate
@@dynamicgrad3820 If you pass an empty list as default argument in a function, that default empty list is created once, and then reused every time that function is called. Which means that most likely, after that first function call, that list no longer is empty. And if the list passed to the function is stored somewhere in your code, there are multiple references to that list spread around your data structure waiting to cause nasty and hard-to-debug side-effects. Your Python IDE will warn about this. A better solution is to use an `typing.Optional[List[xxx]]` argument, with a `None` as default argument. `None` is immutable, so it can be safely reused and shared between multiple calls to the function.
Seconded. I spotted that as well, you beat me to it. Besides using the factory argument, Python has the Optional typing hint, which expects a None default value.
@@dynamicgrad3820 if you specify a default value to a function argument, Python constructs that object only once (together with the function), not again for every call that needs the default value. A common situation would be that you make an argument default to an empty list []. Let's say that the function's code appends something to this list. You may be surprised to notice that if the function is called a second time, the default value is no longer the empty list but the result of manipulations to the list in the previous function call! I.e. it's reusing the very same instance of the default value, not constructing a new value. A way to solve this is to use None as default value then in the beginning of the function replace None with a new empty list if that's what you wanted.
So in this case, how the email service specific arguments are going to be handled? For example the attachment argument? First we are going to create an instance of email service, pass the arguments to it and then use it to create an instance of EmailSender to actually send the email?
One of the surprising things I encountered when I moved from Java to Python was the heavy usage of the service locator pattern. A typical example is Flask's "request" and "d". It is still widespread in Python to put a dependency or an accessor to a dependency in the global context and reference it inside a component. Of course, Python has enough flexibility to manage tests of such less decoupled codes. However, I have always believed that clear decoupling using DI is cleaner and more straightforward. FastAPI's DI is handly but covers only part of the DI commonly practised in Java. FastAPI supports only DI in the "request" scope. But DI frameworks in Java, like Spring, support other scopes, including the "application" scope. The lack of DI containers supporting the application scope is why people must repeat "app.include_router(...)" etc., to construct big applications. DI realises IoC (inversion of control) at the component level. However, you still need to control the construction of an application (injection of dependencies), as demonstrated in this video. The automated construction of applications using DI and CoC (convention over configuration), which is widely used in Java, is something we should consider in Python as well.
Why do you use Protocols if you're going to subclass that anyway? Doesn't it defeat the purpose of Protocols (structural subtyping) in the first place?
Thanks really helpful. In this example though I couldn't see how variations in the email service protocols were catered for. The send_email method didn't show an indication of the different logic needed for different services.
In my practice, I've created a class that operates on websocket stream. In order to allow developer to write their own handlers for every possible event thst they could receive from the stream snd avoid unnecessary inheritance of the ehole class, I left to the general websicket class only functions of build up websocket server and parsing messages from stream and passing them to corresponding hsndlers. For handlers i wrote different interface class, that contains all the methods with some basic functionality inside. Developer needs only creates his own child class from interface with his custom logic in handlers and pass this class as a argument to a main controlling class. Inside the methods of this Interface class are reassigned as the methods to the parent class instance. I don't know if it's dependency injection but I hope it is.
Thanks for the great video. I understand that this is a bit of a contrived example but can you explain why you use an EmailSender class at all when it only wraps a method of the service object that is passed to the initialiser? In other words, why not just call the send_email method directly?
Hello Arjan, great video as usual! I have a question why do you inherit from the protocol in a class which implements one? It's the second video in which I can see this, whereas in ABC vs Protocol you pointed out that it is not needed (duck typing) and also decouples the one who implements from the one who requires. Is there any reason for that? Thank you in advance for the response.
I am bit confused, since when we have to inherit explicitly from Protocols? Your Smtp, SendGrid and MailChimp classes all inherit from EmailService. Or is this just to show that those classes implements the protocol?
Inheriting from 'Protocol' is necessary to create EmailService Protocol. Whats not necessary and where i believe the confusion stems from is inheriting from 'EmailService'. A protocol in python is a typing related construct and should be used as such by using it to declare the type as done in the 'EmailSender' init method. In this example an ABC would be better suited as we have full control over the different implementations. If a third party implementation of an EmailService was imported that you could not control in addition to some of your own code alongside it or as alternative implementations you could use a protocol.
Python has, just like many other languages, very nice libraries for dealing with DI. Very valuable in big projects with loads of modules, units, tests, deployment environments, etc. etc.
We heavily use DI in one of our projects, and it is getting really difficult to manage. We looked and didn't find anything standalone (FastAPI has DI but not standalone, and the project isn't an http server.. ). So if someone knows a good package - lets us know!!
Hey Arjan, thank you for your work from Spain. Would be great if you create a video with your top python books "to improve your python skills to another level", thank you!
@@ArjanCodesi was looking in the comments, if this framework discussion has been requested yet^^. I recently used Dependency Injector, but never got really fond of it. I am looking forward to this upcoming topic.
Dumb question: In this example, the EmailService ends up hard coded (at 5:17 see Line 6-8, for example). In a more real-world example where you don't know which email service a user will need, won't there still be some big long if/elif/else block that needs to be updated when you add a new service?
5:20 Hi. This is not dependency injection. You demonstrated a pattern called dependency INVERSION. Injection is on top of that. Injection means you outsource the instantiation of a class to a Container that will automatically inject dependencies to the class based on configuration.
Yes, it is. Dependency injection doesn’t require a container. DI means you provide an object or function to another object or function, thus separating creation and use, which leads to better decoupling. Dependency inversion means you rely on abstractions instead of concrete classes, which I didn’t cover in this video.
"Dependency injection", as explained here, is probably the most simple OO design pattern. In OO, it is called the Strategy Pattern. You hide the details of an algorithm behind an (abstract) interface. The user of the strategy only knows the abstract interface, each implementation of the Strategy interface knows only the details it needs. I don't understand why people wanted to change the name. The name "Strategy Pattern" has been in wide-spread use for 30 years. I guess people like re-inventing the wheel. That is no critique on Arjan, he didn't coin "dependency injection". I thank him for this video, I never understood what people meant with DI. I thought it was some complex trick like the "Curiously Recurring Template" pattern. Which is similar to DI but achieves lightning fast execution & minimal memory footprint, at the cost of long compile times.
I would say, dependency injection is (usually) built upon the Strategy pattern, but goes beyond that. Your dependencies to inject will usually use the Strategy pattern, but in addition you have some concept of instantiating the dependencies, a repository to keep them, and a way to inject them as needed. This was of course the most basic example with just one dependency which is created in the main() and injected via the constructor. Really a textbook Strategy pattern. This works fine for this simple case with only one level of dependencies. However in an even moderately complex application, this simple approach soon falls apart in that it either explodes the number of dependencies you have to pass along, or forces you to create all strategies in a central place which prevents modularization. There Dependency Injection frameworks come to help...
TL;DR : The Strategy Pattern uses the Dependency Injection principle but the Strategy Pattern is not the Dependency Injection principle, and the Dependency Injection principle is not the Strategy Pattern neither
Strategy Pattern is specifically at runtime choosing different options. Dependency Injection is focused on a caller creating an object that the callee acts upon. Strategy Pattern doesn't require generic interfaces. The Before code that Arjan showed was an example of using the Strategy Pattern.
All applications of the strategy pattern need dependency injection, not all cases of dependency injection are applications of the strategy pattern. (All swallows are birds, not all birds are swallows)
Why does this concept look so complicated in libraries like python-dependency-injector? There they use containers and so on and it becomes really difficult to understand.
The emailSender class here is not needed imo. A better approach (imo) would be to use a common interface on the sender classes, and use DI to pass the relevant sending class to the interface object. Then just call send on the interface.
I don't get the real advantage of it .The lines of code you reduce in the first class you have to put them in an other file, so you end with the same (probably more) lines, more files and dependencies. I would really like to understand the advantages of it to apply it more naturally. Thanks in advance
Advantage is for testing, separating construction of a service from where it's used, organizing your services better, and keeping code clean. If you can swap out one service for another without a fuss, then your code is properly decoupled!
Unfortunately in Python there’s no way to indicate that an argument is a const. When you pass a mutable data structure like a list as an argument, you probably should avoid modifying the list in the function or method that you pass it to. In case it’s an object with methods that is passed as a dependency, it’s a good practice that reduces coupling.
The power of Python's duck-typing leads really well into dependency injection. Just pass an object that has the required properties/methods, and done. No need to subclass from an esoteric BaseSomethingClass first.
Convenient but a bad idea Write a Protocol to be explicit, and it’s easier to create a new implementation as you directly know what methods to write with what arguments
How I hate __init__, self, classes and of course dependency inversion/injection in the code where they don't need at all. They must be used in sending emais where 10+ types of sending and 10+ different services and users that uses that sendings. In all another cases please people uses just def and modules in Python, matherfucka without classes and that oop and solid bullshit
Simply passing an argument into a method is not dependency injection. It's passing arguments to methods, something every programmer has done since the start of time. Similarly, using an interface or abstract protocol is not dependency injection. That's simply coding to an interface and has nothing to do with dependency injection. It's no wonder so many people are confused about the term when so many tutorials about it get it wrong.
Disagree. His injecting the dependent object in the constructor. Also emailaservice is an interface thus these this satisfies the "O" in Solid. So I'm not sure what you're taking about.
@@giantbush4258 He most definitely is *not* injecting it. He is passing a reference. That's *not* dependency injection. The term DI wasn't invented in 2001 to describe 'passing a reference to a method', something every language could do since 1972. If passing an argument to a method is DI, then doesn't that make pretty much every line of code DI? And thus every class its own IoC container? That's utter nonsense. Trust me, the term wasn't invented in 2001 to describe a basic programming concept kids learn after their first Hello World. DI is not passing a argument to a constructor.
👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis
Warning, at 4:26: never use a mutable variable as an argument! This is because the same object will be reused if you call the function/method several times!
This is also a common mistake when you want an empty list.
A solution is to not provide a default value, but a default factory.
Apart from this, thanks Arjan for the video :) Very good, as always! The new example is refreshing and is well appropriate
What do you mean "when you want na empty list"?
@@dynamicgrad3820 If you pass an empty list as default argument in a function, that default empty list is created once, and then reused every time that function is called. Which means that most likely, after that first function call, that list no longer is empty. And if the list passed to the function is stored somewhere in your code, there are multiple references to that list spread around your data structure waiting to cause nasty and hard-to-debug side-effects.
Your Python IDE will warn about this. A better solution is to use an `typing.Optional[List[xxx]]` argument, with a `None` as default argument. `None` is immutable, so it can be safely reused and shared between multiple calls to the function.
Seconded. I spotted that as well, you beat me to it.
Besides using the factory argument, Python has the Optional typing hint, which expects a None default value.
Can someone explain why he added this warning?
@@dynamicgrad3820 if you specify a default value to a function argument, Python constructs that object only once (together with the function), not again for every call that needs the default value. A common situation would be that you make an argument default to an empty list []. Let's say that the function's code appends something to this list. You may be surprised to notice that if the function is called a second time, the default value is no longer the empty list but the result of manipulations to the list in the previous function call! I.e. it's reusing the very same instance of the default value, not constructing a new value. A way to solve this is to use None as default value then in the beginning of the function replace None with a new empty list if that's what you wanted.
Beautifuly explained, clear as water! keep it up!! I think dependendy injection is one of the most important concepts in programming!
I'm happy you enjoyed the video!
Don't get confused when talking to Java programmers about DI. Over there, it generally means "magically inferring which service to use".
So what you mean is "don't worry about the actual definition of the term, but learn something incorrect instead?" Strange take.🤦♂
So in this case, how the email service specific arguments are going to be handled? For example the attachment argument? First we are going to create an instance of email service, pass the arguments to it and then use it to create an instance of EmailSender to actually send the email?
I was just about to ask the same questions. I thought these would be answered in the video but they are not.
yea that made no sense to me
Thank you Arjan! Clear, concise, and to the point. I now know Dependency Injection!
Glad it was helpful!
One of the surprising things I encountered when I moved from Java to Python was the heavy usage of the service locator pattern.
A typical example is Flask's "request" and "d". It is still widespread in Python to put a dependency or an accessor to a dependency in the global context and reference it inside a component. Of course, Python has enough flexibility to manage tests of such less decoupled codes. However, I have always believed that clear decoupling using DI is cleaner and more straightforward.
FastAPI's DI is handly but covers only part of the DI commonly practised in Java. FastAPI supports only DI in the "request" scope. But DI frameworks in Java, like Spring, support other scopes, including the "application" scope.
The lack of DI containers supporting the application scope is why people must repeat "app.include_router(...)" etc., to construct big applications. DI realises IoC (inversion of control) at the component level. However, you still need to control the construction of an application (injection of dependencies), as demonstrated in this video.
The automated construction of applications using DI and CoC (convention over configuration), which is widely used in Java, is something we should consider in Python as well.
Why do you use Protocols if you're going to subclass that anyway? Doesn't it defeat the purpose of Protocols (structural subtyping) in the first place?
Not really. Protocol are better abstract classes
Thanks really helpful. In this example though I couldn't see how variations in the email service protocols were catered for. The send_email method didn't show an indication of the different logic needed for different services.
Yes it's a great pattern. To actually manage and reduce dependencies you need also consider where to place your abstract classes and interfaces.
In my practice, I've created a class that operates on websocket stream. In order to allow developer to write their own handlers for every possible event thst they could receive from the stream snd avoid unnecessary inheritance of the ehole class, I left to the general websicket class only functions of build up websocket server and parsing messages from stream and passing them to corresponding hsndlers. For handlers i wrote different interface class, that contains all the methods with some basic functionality inside. Developer needs only creates his own child class from interface with his custom logic in handlers and pass this class as a argument to a main controlling class. Inside the methods of this Interface class are reassigned as the methods to the parent class instance. I don't know if it's dependency injection but I hope it is.
Thanks for the great video. I understand that this is a bit of a contrived example but can you explain why you use an EmailSender class at all when it only wraps a method of the service object that is passed to the initialiser? In other words, why not just call the send_email method directly?
Hello Arjan, great video as usual! I have a question why do you inherit from the protocol in a class which implements one? It's the second video in which I can see this, whereas in ABC vs Protocol you pointed out that it is not needed (duck typing) and also decouples the one who implements from the one who requires. Is there any reason for that? Thank you in advance for the response.
I am bit confused, since when we have to inherit explicitly from Protocols? Your Smtp, SendGrid and MailChimp classes all inherit from EmailService. Or is this just to show that those classes implements the protocol?
They must inherit so that his EmailSender class can call any of them. See code at 4:00
Inheriting from 'Protocol' is necessary to create EmailService Protocol. Whats not necessary and where i believe the confusion stems from is inheriting from 'EmailService'. A protocol in python is a typing related construct and should be used as such by using it to declare the type as done in the 'EmailSender' init method.
In this example an ABC would be better suited as we have full control over the different implementations. If a third party implementation of an EmailService was imported that you could not control in addition to some of your own code alongside it or as alternative implementations you could use a protocol.
Python has, just like many other languages, very nice libraries for dealing with DI. Very valuable in big projects with loads of modules, units, tests, deployment environments, etc. etc.
Could you share some options here please?
Agree your codebase gets such an improvement when you start using dependency injection.
This time I felt like I've already seen this video. You should talk about an auto wiring DI library like kink
informative, thank you!
Have to ask though, is there a Python package that can do DI the way Spring Boot does it?
We heavily use DI in one of our projects, and it is getting really difficult to manage.
We looked and didn't find anything standalone (FastAPI has DI but not standalone, and the project isn't an http server.. ).
So if someone knows a good package - lets us know!!
We have better, something like Lagom for example.
FastAPI or FastDepends have enough DI for most people. But do you mean the Beans? I've not encountered something like that in Python.
Great video. Thanks Arjan.
Glad you liked it!
Recently did something like this for work. But used a general email sending function that takes in smtp type, port etc for different email providers.
Hey Arjan, thank you for your work from Spain. Would be great if you create a video with your top python books "to improve your python skills to another level", thank you!
These are the best videos!!
Thank you! Glad you like this content!
Can we see the files email_service and services? You put code there and never showed them
Great example.
I prefer SMTP as my email sender. Works every time, regardless of how a customer has configured his email server.
There's an awesome third party library called Dependency Injection, it's so good to apply this principle to any python project
Deja vu, You have recorded almost the same video again. Arjan it's time to go deeper into more advanced examples.
This video was a lead up to another one that’s coming very soon, covering DI frameworks 😊
@@ArjanCodesi was looking in the comments, if this framework discussion has been requested yet^^. I recently used Dependency Injector, but never got really fond of it. I am looking forward to this upcoming topic.
I’m confused between the factory pattern and dependency injection. I watched both your videos. Is it the same?
Your confusion is understandable, as what is described in this video is absolutely *not* dependency injection.
Thanks Arjan
Glad you enjoyed it!
Dumb question: In this example, the EmailService ends up hard coded (at 5:17 see Line 6-8, for example). In a more real-world example where you don't know which email service a user will need, won't there still be some big long if/elif/else block that needs to be updated when you add a new service?
5:20 Hi. This is not dependency injection. You demonstrated a pattern called dependency INVERSION. Injection is on top of that. Injection means you outsource the instantiation of a class to a Container that will automatically inject dependencies to the class based on configuration.
Yes, it is. Dependency injection doesn’t require a container. DI means you provide an object or function to another object or function, thus separating creation and use, which leads to better decoupling. Dependency inversion means you rely on abstractions instead of concrete classes, which I didn’t cover in this video.
How will it work if I need to pass an attachment? I still need to change send_email for all of my classes
To answer your question about which email service, I'm using mailjet
"Dependency injection", as explained here, is probably the most simple OO design pattern. In OO, it is called the Strategy Pattern. You hide the details of an algorithm behind an (abstract) interface. The user of the strategy only knows the abstract interface, each implementation of the Strategy interface knows only the details it needs.
I don't understand why people wanted to change the name. The name "Strategy Pattern" has been in wide-spread use for 30 years. I guess people like re-inventing the wheel.
That is no critique on Arjan, he didn't coin "dependency injection". I thank him for this video, I never understood what people meant with DI. I thought it was some complex trick like the "Curiously Recurring Template" pattern. Which is similar to DI but achieves lightning fast execution & minimal memory footprint, at the cost of long compile times.
I would say, dependency injection is (usually) built upon the Strategy pattern, but goes beyond that. Your dependencies to inject will usually use the Strategy pattern, but in addition you have some concept of instantiating the dependencies, a repository to keep them, and a way to inject them as needed.
This was of course the most basic example with just one dependency which is created in the main() and injected via the constructor. Really a textbook Strategy pattern. This works fine for this simple case with only one level of dependencies. However in an even moderately complex application, this simple approach soon falls apart in that it either explodes the number of dependencies you have to pass along, or forces you to create all strategies in a central place which prevents modularization. There Dependency Injection frameworks come to help...
TL;DR : The Strategy Pattern uses the Dependency Injection principle but the Strategy Pattern is not the Dependency Injection principle, and the Dependency Injection principle is not the Strategy Pattern neither
Strategy Pattern is specifically at runtime choosing different options. Dependency Injection is focused on a caller creating an object that the callee acts upon. Strategy Pattern doesn't require generic interfaces. The Before code that Arjan showed was an example of using the Strategy Pattern.
All applications of the strategy pattern need dependency injection, not all cases of dependency injection are applications of the strategy pattern. (All swallows are birds, not all birds are swallows)
is decency injection only applicable on Classes?
No, you can inject abstract functions into other functions.
I have been happy with Mailtrap
Sheesh! Fresh off the presses!
Why does this concept look so complicated in libraries like python-dependency-injector? There they use containers and so on and it becomes really difficult to understand.
The emailSender class here is not needed imo. A better approach (imo) would be to use a common interface on the sender classes, and use DI to pass the relevant sending class to the interface object. Then just call send on the interface.
I'd really like a few more videos on this topic -- it seems really clever and useful but I don't totally understand it
I have a couple of older videos talking about it already! :)
I don't get the real advantage of it .The lines of code you reduce in the first class you have to put them in an other file, so you end with the same (probably more) lines, more files and dependencies. I would really like to understand the advantages of it to apply it more naturally.
Thanks in advance
Advantage is for testing, separating construction of a service from where it's used, organizing your services better, and keeping code clean. If you can swap out one service for another without a fuss, then your code is properly decoupled!
Isn't using a mutable argument in Python a ROOKIE MISTAKE?
Unfortunately in Python there’s no way to indicate that an argument is a const. When you pass a mutable data structure like a list as an argument, you probably should avoid modifying the list in the function or method that you pass it to. In case it’s an object with methods that is passed as a dependency, it’s a good practice that reduces coupling.
I've been doing this but had no idea it was a design pattern.
If you've been doing something (in multiple projects, I assume) then it is by definition a design pattern ;)
The power of Python's duck-typing leads really well into dependency injection.
Just pass an object that has the required properties/methods, and done. No need to subclass from an esoteric BaseSomethingClass first.
Base class is esoteric for You ? 🤣
Convenient but a bad idea
Write a Protocol to be explicit, and it’s easier to create a new implementation as you directly know what methods to write with what arguments
Where do the ducks come into play?
You lose the benefits of typing and autocomplete if you don't at least use a Protocol.
Reupload? I swear I've watched this before.
Postmark
hmmmm, is this actually just Strategy Pattern??
How I hate __init__, self, classes and of course dependency inversion/injection in the code where they don't need at all. They must be used in sending emais where 10+ types of sending and 10+ different services and users that uses that sendings. In all another cases please people uses just def and modules in Python, matherfucka without classes and that oop and solid bullshit
Simply passing an argument into a method is not dependency injection. It's passing arguments to methods, something every programmer has done since the start of time. Similarly, using an interface or abstract protocol is not dependency injection. That's simply coding to an interface and has nothing to do with dependency injection. It's no wonder so many people are confused about the term when so many tutorials about it get it wrong.
Disagree. His injecting the dependent object in the constructor. Also emailaservice is an interface thus these this satisfies the "O" in Solid. So I'm not sure what you're taking about.
@@giantbush4258 He most definitely is *not* injecting it. He is passing a reference. That's *not* dependency injection. The term DI wasn't invented in 2001 to describe 'passing a reference to a method', something every language could do since 1972. If passing an argument to a method is DI, then doesn't that make pretty much every line of code DI? And thus every class its own IoC container? That's utter nonsense. Trust me, the term wasn't invented in 2001 to describe a basic programming concept kids learn after their first Hello World. DI is not passing a argument to a constructor.
You're saying a lot about what isn't a dependency injection, so could you please explain what IS a dependency injection then...?