1565411321
Let's examine three different techniques for doing this.
B
plugs into A
. A
provides a mechanism for B
to do this — but otherwise A
need know nothing about B
.
The diagram provides a high level view of the mechanism, but how is this actually implemented?
Getting a little closer to the code structure, we can use this powerful pattern:
This is the basic shape of inversion of control. Captured within the notation, which may or may not be familiar to you, are the concepts of abstraction, implementation and interface. These concepts are all important to understanding the techniques we’ll be employing. Let’s make sure we understand what they mean when applied to Python.
Consider three Python classes:
class Animal: def speak(self): raise NotImplementedError class Cat(Animal): def speak(self): print("Meow.") class Dog(Animal): def speak(self): print("Woof.")
In this example, Animal
is an abstraction: it declares its speak
method, but it’s not intended to be run (as is signalled by the NotImplementedError
).
Cat
and Dog
, however, are implementations: they both implement the speak
method, each in their own way.
The speak
method can be thought of as an interface: a common way in which other code may interact with these classes.
This relationship of classes is often drawn like this, with an open arrow indicating that Cat
and Dog
are concrete implementations of Animal
.
Because Cat
and Dog
implement a shared interface, we can interact with either class without knowing which one it is:
def make_animal_speak(animal):
animal.speak()
make_animal_speak(Cat())
make_animal_speak(Dog())
The make_animal_speak
function need not know anything about cats or dogs; all it has to know is how to interact with the abstract concept of an animal. Interacting with objects without knowing their specific type, only their interface, is known as ‘polymorphism’.
Of course, in Python we don’t actually need the base class:
class Cat:
def speak(self):
print(“Meow.”)class Dog:
def speak(self):
print(“Woof.”)
Even if Cat
and Dog
don’t inherit Animal
, they can still be passed to make_animal_speak
and things will work just fine. This informal ability to interact with an object without it explicitly declaring an interface is known as ‘duck typing’.
We aren’t limited to classes; functions may also be used in this way:
def notify_by_email(customer, event):
…def notify_by_text_message(customer, event):
…
for notify in (notify_by_email, notify_by_text_message):
notify(customer, event)
We may even use Python modules:
import email
import text_messagefor notification_method in (email, text_message):
notification_method.notify(customer, event)
Whether a shared interface is manifested in a formal, object oriented manner, or more implicitly, we can generalise the separation between the interface and the implementation like so:
This separation will give us a lot of power, as we’ll see now.
Let’s look again at the Inversion of Control pattern.
In order to invert control between A
and B
, we’ve added two things to our design.
The first is <<B>>
. We’ve separated out into its abstraction (which A
will continue to depend on and know about), from its implementation (of which A
is blissfully ignorant).
However, somehow the software will need to make sure that B
is used in place of its abstraction. We therefore need some orchestration code that knows about both A
and B
, and does the final linking of them together. I’ve called this main
.
It’s now time to look at the techniques we may use for doing this.
Dependency Injection is where a piece of code allows the calling code to control its dependencies.
Let’s begin with the following function, which doesn’t yet support dependency injection:
# hello_world.pydef hello_world():
print(“Hello, world.”)
This function is called from a top level function like so:
# main.pyfrom hello_world import hello_world
if name == “main”:
hello_world()
hello_world
has one dependency that is of interest to us: the built in function print
. We can draw a diagram of these dependencies like this:
The first step is to identify the abstraction that print
implements. We could think of this simply as a function that outputs a message it is supplied — let’s call it output_function
.
Now, we adjust hello_world
so it supports the injection of the implementation of output_function
. Drum roll please…
# hello_world.pydef hello_world(output_function):
output_function(“Hello, world.”)
All we do is allow it to receive the output function as an argument. The orchestration code then passes in the print
function via the argument:
# main.pyimport hello_world
if name == “main”:
hello_world.hello_world(output_function=print)
That’s it. It couldn’t get much simpler, could it? In this example, we’re injecting a callable, but other implementations could expect a class, an instance or even a module.
With very little code, we have moved the dependency out of hello_world
, into the top level function:
Notice that although there isn’t a formally declared abstract output_function
, that concept is implicitly there, so I’ve included it in the diagram.
A Registry is a store that one piece of code reads from to decide how to behave, which may be written to by other parts of the system. Registries require a bit more machinery that dependency injection.
They take two forms: Configuration and Subscriber:
A configuration registry gets populated once, and only once. A piece of code uses one to allow its behaviour to be configured from outside.
Although this needs more machinery than dependency injection, it doesn’t need much:
# hello_world.pyconfig = {}
def hello_world():
output_function = config[“OUTPUT_FUNCTION”]
output_function(“Hello, world.”)
To complete the picture, here’s how it could be configured externally:
# main.pyimport hello_world
hello_world.config[“OUTPUT_FUNCTION”] = print
if name == “main”:
hello_world.hello_world()
The machinery in this case is simply a dictionary that is written to from outside the module. In a real world system, we might want a slightly more sophisticated config system (making it immutable for example, is a good idea). But at heart, any key-value store will do.
As with dependency injection, the output function’s implementation has been lifted out, so hello_world
no longer depends on it.
In contrast to a configuration registry, which should only be populated once, a subscriber registry may be populated an arbitrary number of times by different parts of the system.
Let’s develop our ultra-trivial example to use this pattern. Instead of saying “Hello, world”, we want to greet an arbitrary number of people: “Hello, John.”, “Hello, Martha.”, etc. Other parts of the system should be able to add people to the list of those we should greet.
# hello_people.pypeople = []
def hello_people():
for person in people:
print(f"Hello, {person}.")john.py
import hello_people
hello_people.people.append(“John”)
martha.py
import hello_people
hello_people.people.append(“Martha”)
As with the configuration registry, there is a store that can be written to from outside. But instead of being a dictionary, it’s a list. This list is populated, typically at startup, by other components scattered throughout the system. When the time is right, the code works through each item one by one.
A diagram of this system would be:
Notice that in this case, main
doesn’t need to know about the registry — instead, it’s the subscribers elsewhere in the system that write to it.
A common reason for using a subscriber registry is to allow other parts of a system to react to events that happen one place, without that place directly calling them. This is often solved by the Observer Pattern, a.k.a. pub/sub.
We may implement this in much the same way as above, except instead of adding strings to a list, we add callables:
# hello_world.pysubscribers = []
def hello_world():
print(“Hello, world.”)
for subscriber in subscribers:
subscriber()log.py
import hello_world
def write_to_log():
…
hello_world.subscribers.append(write_to_log)
Our final technique, Monkey Patching, is very different to the others, as it doesn’t use the Inversion of Control pattern described above.
If our hello_world
function doesn’t implement any hooks for injecting its output function, we could monkey patch the built in print
function with something different:
# main.pyimport hello_world
from print_twice import print_twicehello_world.print = print_twice
if name == “main”:
hello_world.hello_world()
Monkey patching takes other forms. You could manipulate to your heart’s content some hapless class defined elsewhere — changing attributes, swapping in other methods, and generally doing whatever you like to it.
Given these three techniques, which should you choose, and when?
Code that abuses the Python’s dynamic power can be extremely difficult to understand or maintain. The problem is that if you are reading monkey patched code, you have no clue to tell you that it is being manipulated elsewhere.
Monkey patching should be reserved for desperate times, where you don’t have the ability to change the code you’re patching, and it’s really, truly impractical to do anything else.
Instead of monkey patching, it’s much better to use one of the other inversion of control techniques. These expose an API that formally provides the hooks that other code can use to change behaviour, which is easier to reason about and predict.
A legitimate exception is testing, where you can make use of unittest.mock.patch
. This is monkey patching, but it’s a pragmatic way to manipulate dependencies when testing code. Even then, some people view testing like this as a code smell.
If your dependencies change at runtime, you’ll need dependency injection. Its alternative, the registry, is best kept immutable. You don’t want to be changing what’s in a registry, except at application start up.
json.dumps
is a good example from the standard library which uses dependency injection. It serializes a Python object to a JSON string, but if the default encoding doesn’t support what you’re trying to serialize, it allows you to pass in a custom encoder class.
Even if you don’t need dependencies to change, dependency injection is a good technique if you want a really simple way of overriding dependencies, and don’t want the extra machinery of configuration.
However, if you are having to inject the same dependency a lot, you might find your code becomes rather unwieldy and repetitive. This can also happen if you only need the dependency quite deep in the call stack, and are having to pass it around a lot of functions.
Registries are a good choice if the dependency can be fixed at start up time. While you could use dependency injection instead, the registry is a good way to keep configuration separate from the control flow code.
Use a configuration registry when you need something configured to a single value. If there is already a configuration system in place (e.g. if you’re using a framework that has a way of providing global configuration) then there’s even less extra machinery to set up. A good example of this is Django’s ORM, which provides a Python API around different database engines. The ORM does not depend on any one database engine; instead, you configure your project to use a particular database engine via Django’s configuration system.
Use a subscriber registry for pub/sub, or when you depend on an arbitrary number of values. Django signals, which are a pub/sub mechanism, use this pattern. A rather different use case, also from Django, is its admin site. This uses a subscriber registry to allow different database tables to be registered with it, exposing a CRUD interface in the UI.
Configuration registries may be used in place of subscriber registries for configuring, say, a list — if you prefer doing your linking up in single place, rather than scattering it throughout the application.
I hope these examples, which were as simple as I could think of, have shown how easy it is to invert control in Python. While it’s not always the most obvious way to structure things, it can be achieved with very little extra code.
In the real world, you may prefer to employ these techniques with a bit more structure. I often choose classes rather than functions as the swappable dependencies, as they allow you to declare the interface in a more formal way. Dependency injection, too, has more sophisticated implementations, and there are even some third party frameworks available.
There are costs as well as benefits. Locally, code that employs IoC may be harder to understand and debug, so be sure that it is reducing complication overall.
Whichever approaches you take, the important thing to remember is that the relationship of dependencies in a software package is crucial to how easy it will be to understand and change. Following the path of least resistance can result in dependencies being structured in ways that are, in fact, unnecessarily difficult to work with. These techniques give you the power to invert dependencies where appropriate, allowing you to create more maintainable, modular code. Use them wisely!
Thanks for reading ❤
If you liked this post, share it with all of your programming buddies!
Follow us on Facebook | Twitter
☞ Complete Python Bootcamp: Go from zero to hero in Python 3
☞ Machine Learning A-Z™: Hands-On Python & R In Data Science
☞ Python and Django Full Stack Web Developer Bootcamp
☞ Python Tutorial - Python GUI Programming - Python GUI Examples (Tkinter Tutorial)
☞ Computer Vision Using OpenCV
☞ OpenCV Python Tutorial - Computer Vision With OpenCV In Python
☞ Python Tutorial: Image processing with Python (Using OpenCV)
☞ A guide to Face Detection in Python
☞ Machine Learning Tutorial - Image Processing using Python, OpenCV, Keras and TensorFlow
#python
1619518440
Welcome to my Blog , In this article, you are going to learn the top 10 python tips and tricks.
…
#python #python hacks tricks #python learning tips #python programming tricks #python tips #python tips and tricks #python tips and tricks advanced #python tips and tricks for beginners #python tips tricks and techniques #python tutorial #tips and tricks in python #tips to learn python #top 30 python tips and tricks for beginners
1619510796
Welcome to my Blog, In this article, we will learn python lambda function, Map function, and filter function.
Lambda function in python: Lambda is a one line anonymous function and lambda takes any number of arguments but can only have one expression and python lambda syntax is
Syntax: x = lambda arguments : expression
Now i will show you some python lambda function examples:
#python #anonymous function python #filter function in python #lambda #lambda python 3 #map python #python filter #python filter lambda #python lambda #python lambda examples #python map
1602968400
Python is awesome, it’s one of the easiest languages with simple and intuitive syntax but wait, have you ever thought that there might ways to write your python code simpler?
In this tutorial, you’re going to learn a variety of Python tricks that you can use to write your Python code in a more readable and efficient way like a pro.
Swapping value in Python
Instead of creating a temporary variable to hold the value of the one while swapping, you can do this instead
>>> FirstName = "kalebu"
>>> LastName = "Jordan"
>>> FirstName, LastName = LastName, FirstName
>>> print(FirstName, LastName)
('Jordan', 'kalebu')
#python #python-programming #python3 #python-tutorials #learn-python #python-tips #python-skills #python-development
1602666000
Today you’re going to learn how to use Python programming in a way that can ultimately save a lot of space on your drive by removing all the duplicates.
In many situations you may find yourself having duplicates files on your disk and but when it comes to tracking and checking them manually it can tedious.
Heres a solution
Instead of tracking throughout your disk to see if there is a duplicate, you can automate the process using coding, by writing a program to recursively track through the disk and remove all the found duplicates and that’s what this article is about.
But How do we do it?
If we were to read the whole file and then compare it to the rest of the files recursively through the given directory it will take a very long time, then how do we do it?
The answer is hashing, with hashing can generate a given string of letters and numbers which act as the identity of a given file and if we find any other file with the same identity we gonna delete it.
There’s a variety of hashing algorithms out there such as
#python-programming #python-tutorials #learn-python #python-project #python3 #python #python-skills #python-tips
1597751700
Magic Methods are the special methods which gives us the ability to access built in syntactical features such as ‘<’, ‘>’, ‘==’, ‘+’ etc…
You must have worked with such methods without knowing them to be as magic methods. Magic methods can be identified with their names which start with __ and ends with __ like init, call, str etc. These methods are also called Dunder Methods, because of their name starting and ending with Double Underscore (Dunder).
Now there are a number of such special methods, which you might have come across too, in Python. We will just be taking an example of a few of them to understand how they work and how we can use them.
class AnyClass:
def __init__():
print("Init called on its own")
obj = AnyClass()
The first example is _init, _and as the name suggests, it is used for initializing objects. Init method is called on its own, ie. whenever an object is created for the class, the init method is called on its own.
The output of the above code will be given below. Note how we did not call the init method and it got invoked as we created an object for class AnyClass.
Init called on its own
Let’s move to some other example, add gives us the ability to access the built in syntax feature of the character +. Let’s see how,
class AnyClass:
def __init__(self, var):
self.some_var = var
def __add__(self, other_obj):
print("Calling the add method")
return self.some_var + other_obj.some_var
obj1 = AnyClass(5)
obj2 = AnyClass(6)
obj1 + obj2
#python3 #python #python-programming #python-web-development #python-tutorials #python-top-story #python-tips #learn-python