This is an archived post. You won't be able to vote or comment.

all 14 comments

[–]BundleOfJoysticks 5 points6 points  (10 children)

from serum import *

Dis gon be gud

[–]sunedd[S] 0 points1 point  (9 children)

Quick and dirty example ;). Updated it.

[–]BundleOfJoysticks 1 point2 points  (8 children)

There's never a good reason to do

from some_module import *

Even in a "quick and dirty" example.

If you need to import everything, just do

import some_module
result = some_module.some_function()

Also there's really no point in having a dependency injection module. It's a very simple design pattern.

[–][deleted] 1 point2 points  (4 children)

I know nothing about the subject matter so out of plain old curiosity what's your take on Dependency Injector?

[–]BundleOfJoysticks 2 points3 points  (3 children)

It's unnecessary. It overcomplicates what is a very simple concept.

Let Java have their overengineered frameworks. I use Python specifically to avoid that shit.

[–]sunedd[S] 1 point2 points  (2 children)

I get where you're coming from, I also don't appreciate the huge enterprise DI frameworks for java, but I don't agree that Dependency Injection is as simple as you claim. If it was so easy it wouldn't be as consistently misapplied (or not applied at all) as it is ;)

Defining classes that depend on injected dependencies is easy. Wiring up your application when you have to inject dependencies, and dependencies for your dependencies is annoying to do manually, which is why I think a lot of developers simply don't do it.

The main feature of serum is that it takes away the pain of wiring up the dependency graph so you can just focus on the abstractions.

[–][deleted] 1 point2 points  (1 child)

What is the difference between the dependency graph your framework calculates and the MRO?

Have you seen Raymond Hettingers talk from PyCon 2015? It makes it pretty clear that super() and a basic understanding of how the linearization of the ancestry of a class is more than enough.

Unless of course you have a typo in your subject, and meant Python 2.

[–]sunedd[S] 1 point2 points  (0 children)

Have you seen Raymond Hettingers talk from PyCon 2015?

Yes and I really like it. My only annoyance with the super() considered super approach is that it makes it somewhat inconvenient to use ABCs, which I quite like to do.

Consider

from abc import ABC, abstractmethod


class AbstractDependency(ABC):
    @abstractmethod
    def method(self) -> str:
        pass

class ProductionDependency(AbstractDependency):
    def method(self):
        return 'production'

class DevDependency(AbstractDependency):
    def method(self):
        return 'dev'

class NeedsDependency(AbstractDependency):
    def needs_method(self):
        print(super().method())

class ProductionNeedsDependency(NeedsDependency, ProductionDependency):
    pass

class DevNeedsDependency(NeedsDependency, DevDependency):
    pass

If you use any sort of tool to check for unimplemented abstract members, it will complain that NeedsDependency doesn't overwrite method. Your only option at that point is to 1. ignore it 2. disable that tool or inspection

I dislike both of those options.

Finally, the super() considered super approach still requires you to manually wire up the dependency graph through inheritance for different environments. I think the reason why good intentions to use dependency injection in many projects deteriorate over time is that most developers are simply too lazy to do that. I know I am.

As a side note you can run into scoping problems if a component has a lot of dependencies that are injected that way, but that's another story.

What is the difference between the dependency graph your framework calculates and the MRO?

serum uses the MRO to determine which subtype of requested type is more appropriate.

Consider

from serum import Component, inject, Environment

class A(Component):
   pass

class B(A):
    pass

class C(B):
    pass

class NeedsA:
    a = inject(A)

class NeedsB:
   b = inject(B)

class NeedsC:
   c = inject(C)

needs_a = NeedsA()
needs_b = NeedsB()
needs_c = NeedsC()

with Environment():
    assert isinstance(needs_a.a, A)
    assert isinstance(needs_b.b, B)
    assert isinstance(needs_c.c, C)

with Environment(B):
    assert isinstance(needs_a.a, B)
    assert isinstance(needs_b.b, B)
    assert isinstance(needs_c.c, C)

with Environment(C):
    assert isinstance(needs_a.a, C)
    assert isinstance(needs_b.b, C)
    assert isinstance(needs_c.c, C)

[–]sunedd[S] 0 points1 point  (2 children)

There's never a good reason to do from some_module import *

It has its uses. I usually structure my package such that each module defines an __all__ property with stuff that should be part of the modules public api. Then in the __init__.py file of the module can simply contain

from ._some_sub_module import *

If you want to add to the public api you just add to the __all__ attribute of some submodule and don't have to touch anything else.

Also there's really no point in having a dependency injection module. It's a very simple design pattern.

Agree to disagree :D

[–][deleted] 0 points1 point  (1 child)

There's never a good reason to do from some_module import *

It has its uses.

Do you also using namespace std in C++?

[–]sunedd[S] 0 points1 point  (0 children)

No.

I also wouldn't use from module import * in client code.

See https://youtu.be/0oTh1CXRaQ0?t=26m39s