We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Dependency Injection: Stealing Cool stuff from the Weird Kids

00:00

Formal Metadata

Title
Dependency Injection: Stealing Cool stuff from the Weird Kids
Title of Series
Number of Parts
115
Author
Contributors
License
CC Attribution - NonCommercial - ShareAlike 4.0 International:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Dependency Injection: Stealing Cool stuff from the Weird Kids [EuroPython 2021 - Talk - 2021-07-29 - Brian] [Online] By Bojan Miletic While dependency injections is widely used in other programming languages, it's a design pattern that is nowhere near as popular in Python. During this talk I would like to introduce you to this concept and show you how it can improve your engineering skills. I would also like to share couple of examples and ways you can implement them in python. License: This video is licensed under the CC BY-NC-SA 4.0 license: https://creativecommons.org/licenses/by-nc-sa/4.0/ Please see our speaker release agreement for details: https://ep2021.europython.eu/events/speaker-release-agreement/
GoogolQuarkBlogPoint cloudProjective planeAdaptive behaviorSocial classSoftware testingVirtual machineType theoryError messageComputer fileConstructor (object-oriented programming)Configuration spaceSet (mathematics)Line (geometry)Functional (mathematics)Domain nameCore dumpInterface (computing)Multiplication signControl flowINTEGRALClient (computing)Software bugBitWordString (computer science)Cache (computing)Attribute grammarException handlingSoftware developerIntegrated development environmentPiInternet service providerMusical ensembleRun time (program lifecycle phase)Lambda calculusGateway (telecommunications)Moment (mathematics)Cartesian coordinate systemMathematicsDot productPersonal digital assistantWebsiteProduct (business)Electric generatorProcess (computing)2 (number)Loop (music)Instance (computer science)Pairwise comparisonCodeObject (grammar)Dimensional analysisInjektivitätPatch (Unix)Open setGoodness of fitAxiom of choiceFlagFunction (mathematics)MereologyMobile appJava appletDenotational semanticsMessage passingDatabaseCASE <Informatik>CompilerDifferent (Kate Ryan album)Stress (mechanics)Physical systemWrapper (data mining)Right angleMixed realityExclusive orCrash (computing)AbstractionSystem administratorAbsolute valueServer (computing)Inheritance (object-oriented programming)Figurate numberWeightProgrammer (hardware)Local ringAddressing modeTerm (mathematics)Sheaf (mathematics)Uniform resource locatorOnline helpFormal languageGodStandard deviationCloud computingRadiusMatrix (mathematics)LogicExecution unitOrder (biology)Connected spaceInteractive televisionData storage deviceFehlererkennungPie chartFile systemExterior algebraElectronic mailing listSuite (music)Video gameMeeting/Interview
Computer virusPersonal digital assistantData miningPresentation of a groupMeeting/Interview
Software developerProduct (business)Java appletIndependence (probability theory)Maxima and minimaCodeWordJava appletAttribute grammarCodeSocial classMereologyVirtual machineMultiplication signSoftware developerBitInjektivitätFunctional (mathematics)Object (grammar)Order (biology)2 (number)Software testingMathematicsAxiom of choiceConstructor (object-oriented programming)Line (geometry)WordComputer fileDatabasePairwise comparisonOpen setInterface (computing)Goodness of fitComputer animation
CodeJava appletWordIndependence (probability theory)CompilerMultiplication signSoftware testingType theoryCloud computingCompilerJava appletComputer filePoint cloudFunctional (mathematics)CodeConfiguration spaceInjektivitätError messageClient (computing)Patch (Unix)Object (grammar)Data storage deviceSocial classInterface (computing)Dot productString (computer science)AbstractionElectronic mailing listCartesian coordinate systemSet (mathematics)Virtual machineDatabaseMereologyDomain nameAdaptive behaviorLogicPhysical systemCrash (computing)Connected spaceProjective planeExterior algebraComputer animation
CodeComputer fileInheritance (object-oriented programming)CodeConfiguration spaceDifferent (Kate Ryan album)Software testingMultiplication signCore dumpInjektivitätInternet service providerFlagInterface (computing)Stress (mechanics)FehlererkennungCloud computingMathematicsINTEGRALSoftware bugRun time (program lifecycle phase)Type theoryClient (computing)Java appletRight angleProduct (business)WebsitePhysical systemElectric generatorAbstractionSocial classWritingCompilerComputer animationDiagram
Computer fileMobile appQueue (abstract data type)Message passingSocial classGoodness of fitMatrix (mathematics)CodeInjektivitätInterface (computing)Moment (mathematics)Lambda calculusSoftware testingDomain nameControl flowBitVideo gameServer (computing)Suite (music)System administratorSoftware developerOrder (biology)Product (business)Unit testingCore dumpMixed realitySheaf (mathematics)Denotational semanticsMultiplication signProgrammer (hardware)Formal languageCloud computingVirtual machineStandard deviationClient (computing)Integrated development environmentProjective planeAbsolute valueException handlingLecture/ConferenceComputer animationMeeting/Interview
Transcript: English(auto-generated)
Hello there, and we are back. So I hope you are enjoying the conference so far. And Dan, and also the keynote as well, is amazing keynote. So yeah, now we are having our talk again.
And our first speaker for this blog will be Bo Yan. And Bo Yan is a good friend of mine. Hello. Hello. Where are you calling from, for people who don't know? From Berlin. Oh, Berlin. Yeah. Cool. Cool stuff.
And today, I have a very, very special assistant. He is running all around, and his name is Frederic von Edelmann. It's a puppy with a very huge name, and a royalty. Cool stuff. Cool stuff. And then I hope the puppy doesn't make a debut while you're presenting.
I hope he does. Oh, you want to. OK. We'll see. We'll see. So you tell us about dependency injection, which is a very, very interesting, interesting topic. So I'll let you take us away. OK. OK.
So welcome, everybody. And first thing I want to tell you is I love each and every one of you. And I love you so much. I actually went undercover into Java community, and I managed to steal some of their secrets. And I'm going to share it today. So for you, I should suffer in Java.
Now, one second. Dum, dum, dum, dum. So about me, I've been doing Python development for almost a decade.
I'm a very, very person since I love unicorns and baking cakes. I work with AWS. I tried designing. Also, I didn't put here, but I did some Java development and even PHP did not like it. But I tried Python one day, and ever since, I've
been only doing Python. And that makes me super happy. And somehow, I also ended up as a CEO of my company. Not sure how that happened, but I'll keep you in the loop. Now, we're going to talk about Java. I know everybody has fears of Java, myself included,
because there's so much code. My first experience with Java was with Java and Python comparison was when I saw how you can easily open the file in Python. Like, you just type open. And in Java, there's code, and code, and code, and code.
A bunch of things are happening there that you should not have. There are some good stuff in Java that we are not using that much in Python, mostly because we don't need them and we have the choice. They do not have the choice. So I'm going to tell you about things
that we could use that's going to help us become better, even better than all Java community. So I'm going to talk about dependency injection, but let's establish some basics first. Let's have unicorn that bakes cakes.
So one class uses the another class. So when unicorn makes cupcakes, he uses the oven. In this case, we can say that unicorn class depends on the oven. Fairly simple. You're always going to have some class using another class and depending on it.
Now, in some cases, unicorn can use some fancy oven, and that's going to be another dependency. Nothing too scary. Now, we're going to talk about dependency injection. The injection part.
Basically, you can make a unicorn class. So when you call a constructor, unicorn class goes and sets up its own oven, and then it works with it. Fairly simple stuff. We do it all the time. But the injection part is what's interesting.
You can create the object of the oven class, then just pass it to unicorn, and unicorn doesn't have to set up a whole oven in order to bake some cakes. We can even preheat the oven, and unicorn can do that without any troubles. So as I mentioned here, very nice thing to do.
And it can sell you a bunch of stuff, especially with something that you want to have a singleton, for example. I don't know why, but let's say we want to. Dependency injection to drive skill. Now, in Python, we have many ways of doing this.
The simplest way to do it is to just extend the constructor. Say, okay, instead of initializing a cooking machine to be an oven in the constructor, we're just going to pass it. And that's it. Whole thingy, few lines of code.
As everything beautiful in Python. Now, here we introduce some difficulties. Right now, we have to first create an oven, and then the unicorn class. A bit of extra work, but I'll show you very, very quickly why this is going to pay off.
Now, here's the stuff that Java people can do. We can create a unicorn class, and then during the run time, we can just assign it an attribute cooking oven. Now, words of advice.
If you are going to do this approach, never, ever, ever just assign stuff. Always declare the attribute in the initialization. Like it here. See? Self-cooking machine. Because if you don't do it like this,
when you start working with the class, you're never going to know what are the attributes of my class. And that requires chasing down all the usages in the code and figuring out, okay, here I get this attribute, here I get this other attribute. And you do not want to do that.
In Java, they try to simulate this with getter and setters, but because we're the cool kids, we can just take instance, slap one nice attribute, object, whatever you want to do with it. We get freedom.
Now, in Java world, things get super scary in this. Like they don't do testing like we do. We can basically just grab by test, monkey patch, change stuff without any problem.
For them, that's a bit difficult because everything's statically typed. You cannot easily overwrite functions in code. So what they do is they take stuff and make it depend on interfaces. And then you have, for example, database interface.
One of the classes is for testing that is going to be a basically mock class and other one is actual class that's connecting to the database. In Python, you don't have to do that. You can just intest or write the function that connects database and say, tell it, oh, use this mock object that I created.
But there is logic to what they are doing. Since they're telling compiler in advance what type of dependency it is, they're going to notice errors much sooner than we do.
I know from my experience, when I'm monkey patching stuff and mocking stuff, usually I need to run test a couple of times before I make sure that everything is done particularly nice. So type hints are going to help us to get better in Python without using Java.
MyPy is absolutely amazing tool to use. And also beside the MyPy, I'm the PyCharm user, I love it. As soon as you add type hints, you're going to get notifications and warnings about all the stuff where you forgot to use.
And since you're working with interfaces in Python, we're also going to notice if we did something wrong. Now, everybody knows two dots and the type of the object.
Very simple. And it works beautifully. If we try to give Unicorn some strings, say this is instead of cooking machine, we give it a string that says this is a cooking machine. MyPy is going to complain, PyCharm is going to complain.
So type hints, super awesome. Now, one reason I want to mention here while we're using abstract classes in Python to accomplish this, it's basically just to create an interface.
It's not, we're going to define this class then it's going to get inherited and stuff like that. No, we're just telling, this is a lightweight abstract class and it has this and this methods. For example, when I'm working with AWS, I might have a adapter class that has upload file.
Get file URL, that's all there is to it. Initialization and stuff like that, somebody else is going to handle it, some other part of the code. But when I'm creating mocks and other stuff, I know how this class is going to be used by the rest of the system. So abstract classes are super nice in Python.
We just don't use them that much. Now, dependency injection is super cool because we can create a configuration file. In that configuration file, you can say, okay, I'm going to use this class for this
and this and this and this. Basically, make a list of classes that are used by your project. And when you're testing, you just create another configuration file. Django does this quite nicely with his settings file. Just write strings if you want to use,
for example, redis for caching, you just say that. If you want to use something else for caching, for example, memcache, you just say that. And once the application starts up, it looks at the configuration, these are the things I need to do and just passes it to the main class.
Everything works perfectly and nicely. And because they have the common interface, which we already declared, there is absolutely no problem. And we can easily add our own code and functions and objects because we just have to inherit that abstract path.
And in my case, PyCharm, it's going to complain, okay, you need to declare these methods. I can declare them, they can do some nonsense, but the code is not going to crash because they know what they need to add. Now, writing mocks is very, very easy
when you're working with interfaces, because you know exactly what you need to mock. Without mocks, as I mentioned, it tends to get more, let me make a change, let me try stuff as it happens, and afterwards, I'm going to figure this out.
With dependency injection and interfaces, it's super, super, super easy. It's very hard to mess up, even for me, and I mess up a lot. So, mocks help you write better tests. Of course, if you can just rewrite some parts of the object,
then you don't need the interface. This is just to tell you, okay, there is alternative to just monkey patching connections and stuff like that. Bigger decoupling. Now, this is the good stuff. Basically, where I use dependency injection,
that's on the domain borders. If I have some piece of code communicating with another independent part of the code, I'm just going to put dependency injection in there. Basically, an interface. For me, using them as adapters is extremely powerful,
especially when working with clouds. For example, as I mentioned, you might use one cloud provider to upload file. You write a function for it. Now, the client might demand the multi-cloud.
It happens. They do. And then you have to rewrite the code. Okay, so if I pass through to this function, to this object, it's going to initialize AWS data storage path. However, if I pass this false,
then it will initiate Azure. Which is pretty acceptable. But once you start to keep adding flags and flags and flags, it gets very, very messy.
So because I don't like messy code, and because I'm very, very forgetful, I like to keep my code base as simple as possible. So in your code, you can just declare the interface. And then based on the configuration, your worker can just read that configuration file
and then work. It is also super nice when you're doing some testing. For example, I want to do some stress testing to see maybe I can use RabbitMQ and see how things work out for my worker and microservices. Or I can just try AWS SQS or maybe some third solution.
I could probably use maybe Redis and stuff like that. And the good thing here is that I just need to implement that interface and change configuration file. The rest of the code is already tested.
And it will not be broken. So if there are some bugs in there, it's probably in the new code I added. So that way I can be nice to two fences about all the integrations and craziness that I'm going to do and protect the rest of the code.
It saves me a lot to debug. As I mentioned, easier error detection. First of all, as mentioned here, before running the code, my PyCharm, probably VS code. I did not work that much in that.
They are very good at dealing with type hints. So they are going to complain and you're going to notice right away. This is good. Because without them, I usually notice it in runtime. And on days when client is very, very busy
and some change needs to be deployed extremely urgently, right at moment, for example, for Christmas, website needs to have this very second snowflakes falling and it's mission critical. If you don't even want to test the code,
you just deploy because that's snowflakes. Here, your compiler is going to protect you from yourself. You don't have to deploy to production to find out what went wrong. If it's a disclaimer, do not generate snowflakes in Python.
That's usually JavaScript's job. Now, the downside. There is more code, as you have seen earlier, when we use a constructor-based approach. Suddenly, things that were very super easy,
basically calling a unicorn constructor, become much, much harder. Since now, okay, before I call the unicorn constructor, I need to call this class, and then this class, and then this class, and then pass them into constructor. Things tend to get pretty heavy.
But benefits, on the other hand, are all things that I mentioned. However, you can very easily create a wrapper around it. A system is going to read the configuration, see what classes go there,
and then just initialize the class for you. So it's more code, but you get cool stuff. Abstract classes. Best thing ever. Now, I say this with pass because I was coming from Java,
and it was always super, super difficult to get my hands around Liberty in Python. My first two months, I spent basically trying to write Java in Python. So once I stopped doing that, I had a mild aversion to the Java way of doing things.
So I didn't use abstract classes for a long time. But then when I started using them, I started using them probably way too much. Now, for those of you who are still learning Python and all its charms,
abstract class is class that you cannot initialize. It's there just for inheritance. And you don't have to declare a bunch of methods. You can just choose one method. For example, upload file. And that tells you that any class that inherits that class
needs to have an upload file method. Easy peasy. Now, I'm super efficient. I get to talk now about all the cool stuff I use this. Now, as I mentioned, I use this all the time
when I'm working with different code providers. And for example, working with China, going to use Tencent, AliCloud, and stuff like that. When you're working with AWS, AWS stuff. And if you're working in France, they have their own cloud provider.
And I just let people write the code for them. And that's it. Main logic, the core of your business. The most mission critical stuff is completely isolated from the other stuff.
You can even outsource that stuff thanks to your interfaces. And they're going to do that stuff for you. Meanwhile, you're focusing on the core of your business. Which is pretty good because we do not want to write the code we don't have to.
So dependency injection must again help us be lazy. And that's pretty much everything. Now, question. Yeah, I think we just want to uproar. Do you set that like now is the Q&A time?
Yes. Yes, of course. So people don't wish I'm, please ask in the chat in the matrix. And hello there. There isn't a lot of question at the moment. Someone's typing. So that's really a good talk. And, oh no, someone posted a picture of the puppy.
So yeah, like, may I ask like what is the, what advice would you give to people who may be like doing the same thing? Like, could you sum up like maybe three advice you can give people? Yes.
First advice, interfaces. Interfaces are very good. And they're a bit hard to wrap our hands around. Because in Python, they don't come naturally. In statically-typed languages, there's something that you can't work without them, especially unit testing. I think we underestimate how pytest and the whole testing suit
that the disease in Python is making your life easier. When you see all the things that you need to do around them in order to make your code testable, you can't do it without dependency injection. So be very happy because you have Pytest
and give those developers some money. Don't need stuff to them because they did magic for us. That's the first one. Third one is try to know your domains in the business. What's your core domain? What the third-party stuff?
Something that's not so important, but you can outsource it. And basically on the borders, that's where you're most likely to encounter dependency injections. That will allow you to integrate one thing with another. Now the third advice is break things.
I use this all the time to do some weird stuff. Because AWS does not crash when I want it to crash. Only when I don't. So I basically create a bunch of classes that are just drawing exceptions and doing crazy stuff.
And I use dependency injection in testing to see how breaking AWS is going to break my code. And the answer is a lot. But thanks to this, I can detect it quite early. It's much nicer when your development
IDE tells you something is broken than when your client calls you in the middle of the night. Yeah, absolutely. So there's a question actually now. Do you use this kind of DI in small projects too?
Yes. Basically, since I work a lot with cloud providers and the code for small projects is, okay, I need some API gateway, write me a lambda here. I love using dependency injection in there because there are a bunch of stuff that's interquiring.
Even though it's a small piece of the code, it's very easy to make mistakes. And testing lambdas is not easy. Also testing lambdas without creating huge build is not easy. So I use this all the time, but it depends on the domain.
So if you're writing some simple app that doesn't use any third-party dependency, does not work with file system, does not communicate with message queue and stuff like that, you don't need dependency injection.
Yeah, cool. That's good. And then there's another question. So we have time. I love questions. Yes. Please ask more questions, people. Yes. So what about typing packages? Actually, there's two questions. So are there advantages to use this over abstract classes?
You can try and do them without any problems. I just prefer using abstract classes. My cup of tea. Yeah. So what would be the difference?
Like, why can't I use, like, you know, I can use it, I can still use it for abstract classes, right? Or is it like, you know, or I should not do it, or what's your thought? Yeah, my thought is basically, Python gives us a bunch of liberties.
So write like this, and then write abstract classes. Maybe you can use the mix of them. But for me, I just go full abstract classes and work with that. That's the stuff I understand. However, this thing with typing packages can also work. So it's different approaches,
but they are not mutually exclusive. Personal preference. It's the important stuff is to actually just isolate stuff. Decouple them. The way you do that, it's up to you. Yeah, so, oh, someone already commented.
Extra class will carry more intention about the usage. Oh, if only from the name of the code I gave to it, then bare stuff from the typing packages. Any thoughts on that? Do you agree? Or no?
Well, I agree. The abstract classes do carry more weight. Yeah, yeah. I think it's really up to you.
Then it's really taking a risk, I would guess. Yeah, I mean, that's what I always tell people. Break things. Try them, break them. That's how I learned Linux administration. I deleted the server in production. And then I have to bring it up in 10 minutes.
I learned so many things. So break them, try them, see what works for you. Don't be afraid of the breaking stuff. That's the thing as programmers. We're afraid to break things. You have your local machine. Try everything and see what makes you feel happy.
Python is one of those languages where you know how code is bad because it's not beautiful. First thing, if code is beautiful, then it's good. If it makes you happy, then it's even better code. That's the standard for Python. Try things, see what makes you happy, go with it. You can't go wrong.
Yeah, yeah, that's true. That's true. I think it's a really good advice, actually. Break things and don't be afraid of breaking things. Someone already said that, like, yeah, third advice, break things. Which is very true, I guess. So yeah, I think it's towards the end of this section.
And then we are ready to welcome the next speaker. So thank you so much, Bo-Ya.