The True Power of "ChainMap" in Python

Поділитися
Вставка
  • Опубліковано 29 гру 2024

КОМЕНТАРІ •

  • @samoylov1973
    @samoylov1973 9 днів тому +72

    ChainMap - very interesting. Have to think of use cases for it, though. As in the example in this video it might be done with simple dictionaries. Look:
    >>> default: dict[str, str | bool] = {
    ... 'theme': 'Light',
    ... 'language': 'English',
    ... 'notify': True,
    ... }
    >>> user: dict[str, str | bool] = {
    ... 'theme': 'Dark',
    ... 'notify': False,
    ... }
    >>> choice = default | user
    >>> choice
    {'theme': 'Dark', 'language': 'English', 'notify': False}

    • @Naigo1
      @Naigo1 9 днів тому +12

      I had the same thought

    • @landsgevaer
      @landsgevaer 9 днів тому +27

      For example, if you change the original dicts after the ChainMap is created, then the ChainMap will dynamically use the changed values.
      Creating a merged dict will create a snapshot copy but will not follow the original dicts.
      The video mentions other use cases, like changing the ChainMap's first (empty) dict but shielding the others.

    • @samoylov1973
      @samoylov1973 9 днів тому +6

      @@landsgevaer Thank you, never had such use cases, but will keep in mind an option for dynamically updated dicts in ChainMap.

    • @JensRoland
      @JensRoland 9 днів тому +7

      I had the same thought initially; then I thought of how to represent variable scope when writing a compiler/interpreter, and it clicked. This is exactly what you need for representing a stack of closures where your current scope can access variables from the parent closure and the global scope; though in those scenarios you would need the ability to update those variables, so it’s not exactly right, hmm..

    • @Michallote
      @Michallote 9 днів тому +6

      I have needed it before, it is very useful for configurations and settings. Because it lets you save all the history of the settings. Imagine mid way through a program you want to override some settings for a particular task but don't need or want it to remain that way through so you gotta revert back to the original settings.

  • @PauxloE
    @PauxloE 9 днів тому +34

    As a only occasional python user who never heard of this, from the name I was expecting something which chains the mapping functions - i.e. given a key, it looks it up in the first dictionary, and then uses the value as the key for the second one.

    • @denys-p
      @denys-p День тому

      Yeah, this one probably should be named as ChainDictionary or something like that

  • @marcusg2000
    @marcusg2000 8 днів тому +16

    A case study in over-engineering in standard library. Somone was high on kool aid one afternoon.

  • @aaronm6675
    @aaronm6675 9 днів тому +12

    Love hearing about all the esoteric components of the collections, itertools, functools, etc. libraries!! Thx for your reliable quality and effective communication🎉

  • @astrobullivant5908
    @astrobullivant5908 9 днів тому +33

    I have never used ChainMap. That needs to change.

  • @DanielCordey
    @DanielCordey 8 днів тому +9

    Reading a code using Chainmap, and being written by someone else, is going to be very challenging... and probably very painful to debug.

    • @akin0m
      @akin0m 5 днів тому +2

      Any code using a tool you are unfamiliar with will be difficult to read. The tradeoff is that once you understand the reason the ChainMap is used, the code will become straightforward (assuming it is used appropriately; obviously, in many cases a dictionary will suffice). A huge advantage of ChainMap compared to merging dictionaries is that it effectively provides a way to continuously revise the merger of 2 or more dicts. There is no equivalent method that is not as hard or more hard to read.

  • @Chaplainelmo2001
    @Chaplainelmo2001 9 днів тому +6

    Excellent demo of theory, followed by the default/preferences example. Thanks from an old 'hobbyist'learner!

  • @JustAnotherLight
    @JustAnotherLight 2 дні тому +1

    one application of this would probably be in writing (simple) interpreters. this could store the variables and it's values; on every block-scope start, the chain map could have a new child, and update the keys on encountering variable declaration/assignment.

  • @しめい-l4m
    @しめい-l4m 9 днів тому +16

    When will this be used? I can't think of any case that you want to merge dictionaries while keeping the reference to the original dictionary, just to accidentally mutate it / try to pop keys that don't exist in the first dictionary

    • @AWriterWandering
      @AWriterWandering 7 днів тому +5

      According to the documentation, the advantage is speed. ChainMap uses a lazy approach to combining dictionaries, that doesn’t require running updates.

    • @しめい-l4m
      @しめい-l4m 7 днів тому +8

      ​@@AWriterWandering I naturally assumed that performance was the last thing python devs would ever concern about.

    • @ДаниилРабинович-б9п
      @ДаниилРабинович-б9п 7 днів тому +4

      ​@@しめい-l4mthat's actually not true at all. They just have a different approach to performance. The language itself is pretty slow, but there are a bunch of powerful libraries that are written in C and thus fast, so you get performance by offloading the majority of computation to those libraries.

    • @akin0m
      @akin0m 5 днів тому +3

      Any case in which you want to keep a history of revisions. Imagine you are a user and you temporarily switch your theme to light mode, then switch back to dark mode. Then the application updates to default to light mode.
      Case A where the user preferences are stored in a merged dictionary results in the user being defaulted to light mode. The screen brightness overpowers the users frail eyes, blinding him permanently.
      Case B where the user preferences are managed by a ChainMap recognizes that the user intentionally set the value of the application to dark mode, and he preserves his eyesight. Lawsuit avoided.

  • @drdca8263
    @drdca8263 8 днів тому +3

    2:30 : wait, so if you try to remove the value at 'c' it will error?
    Edit: 3:51 shows that yes.

  • @kirillsukhomlin3036
    @kirillsukhomlin3036 9 днів тому +6

    Looks like exactly what is possible in JavaScript with prototypical chains.
    I know, Python has much more (than JS) powerful standard library, but this particular thing is exactly like prototypical inheritance.
    Also it's even simpler in JS with { ...default_settings, ...settings } for one-time computation, if one doesn't need dynamic combination.
    Also in generic CS terms, this is the chain of responsibility pattern.

    • @maximkovalkov1334
      @maximkovalkov1334 9 днів тому +3

      Python has something quite similar with its inheritance model (see MRO), but it makes a distinction between index/subscript notation, like obj[x], and attribute access, like obj.x (even though objects are still normally backed by a dictionary!)
      So you could probably emulate most of what JS does using python’s language primitives but it would be quite a lot more cumbersome; i think this is one instance where I prefer JS’s “less is more” approach

  • @ultru3525
    @ultru3525 9 днів тому +5

    4:42 I've watched over a dozen of your vids, but never noticed you were Scandinavian until you said "Sandra" in a way only a Norseman would

    • @JensRoland
      @JensRoland 9 днів тому +3

      Wow, I never would have guessed based on the accent… but then I went to 4:40 and heard “Sandra” and there it was, the unmistakable trill of Swedish.

    • @Indently
      @Indently  8 днів тому +2

      I'm not Swedish, but I read in Swedish and go to Sweden often because I love Sweden.

  • @averagesoul8256
    @averagesoul8256 9 днів тому +4

    im curious as to why even for your example with the settings you would use this when you can do preferences = {**default_settings, **user_preferences}

    • @JOK120
      @JOK120 9 днів тому +1

      Hi there!
      ChainMap saves memory when compared with the unpacking method at the expense of slighty slower lookup. Also, is also looks cleaner in the code to process missing keys hierarchically, for example, if a key is missing on user_preferences, go with the default.

    • @maximkovalkov1334
      @maximkovalkov1334 9 днів тому +1

      ChainMap would not build a new dict but keep references to the two original ones, so if you modified them afterwards it would still stay in sync.
      Whether that is something you would want is up to you :)

  • @haidersultan5359
    @haidersultan5359 4 дні тому +1

    Python has the most random things built into it's standard library.

  • @vandelayindustries2971
    @vandelayindustries2971 2 дні тому

    Kind of going overboard with the type annotations don't you think? Like yeah ["abc"] is a list[str], does that really need an annotation? I find them most useful for function signatures/class fields and occasionally a local variable in a complex function. But using it literally everywhere just makes things unnecessarily complicated imo

  • @winandfx
    @winandfx 9 днів тому +1

    Recently i've tried to find a solution for merging default settings and user settings. The problem is nested dicts. I want to override a setting which is inside a dict inside a dict inside a dict. So I've written a recursive function which iterates over each item of the tree and updates them if needed.

  • @Chalisque
    @Chalisque 9 днів тому +1

    I never knew this existed. In the past, I implemented something like this.

  • @LifeEroz
    @LifeEroz 3 дні тому

    Fantastic video 👌

  • @davidl3383
    @davidl3383 8 днів тому

    Very interesring. Thanks a lot

  • @BuldogueTutoriais
    @BuldogueTutoriais 9 днів тому +1

    Thank you for your videos, they have titles in Portuguese, the only thing missing is AI dubbing, when will UA-cam make it available globally? It would be amazing.

  • @klmcwhirter
    @klmcwhirter 8 днів тому +2

    This smells a lot like the Java Properties class. But Properties has capability for only a single parent. I struggle to find a real use case for multiple parents. I doubt that it would be reasonable to model a tree structure using ChainMap. hmmmm
    Insightful video - got me thinking... Thanks

  • @junioroliveira2064
    @junioroliveira2064 8 днів тому

    I can't imagine that the ChainMap would be so useful. It might performs a mapping for keys and values inside dict, taking the key and iterating over each dict for values associated with this key and also by the reverse, returning one Key and all other values associated which is in the first and in any other. Instead, ChainMap stores each dict and if you want access any key this only returns the first

  • @denys-p
    @denys-p День тому

    I might have bad imagination, but this structure looks pretty specialized to me. I can’t imagine use-cases besides before-mentioned settings.
    Does anyone have good ideas how to use it?

  • @LxAU
    @LxAU 5 днів тому +4

    I’m sorry, but why would I ever want to use a ChainMap in real life? None of the examples felt in any way relatable.

  • @roysinjan9817
    @roysinjan9817 8 днів тому

    Pls tell waht ide you use

  • @isaacingleby8771
    @isaacingleby8771 8 днів тому

    But why would you need this, the merge syntax of two dicts would solve this easily? In your example around minute 8 if you just put default before custom and anything where the default conflicts with the custom will be overridden

  • @j5s7
    @j5s7 9 днів тому +4

    UA-cam presents your videos with automatic translation. As a viewer I have no chance to generally choose the original language. I have to switch from translation to original by hand. This is bullshit. Please consider changing your configuration.

    • @n0150
      @n0150 9 днів тому +3

      yeah the new auto translate features aren't thought through properly
      I ended up changing my app language to english

    • @PeterZaitcev
      @PeterZaitcev 4 дні тому

      ​@n0150 I ended up switching app locale to EN a couple of years before because of localized titles on 3b1b and other channels.

  • @contentbeyondinfinity8848
    @contentbeyondinfinity8848 7 днів тому +2

    this is lowkey funny

    • @sk2279
      @sk2279 7 днів тому

      Huh? Funny how?

  • @cparks1000000
    @cparks1000000 5 днів тому +1

    Sounds like bugs waiting to happen.

  • @halcyonramirez6469
    @halcyonramirez6469 4 дні тому

    Dictionaries have a union operator? Is this new or what?

  • @dipeshsamrawat7957
    @dipeshsamrawat7957 9 днів тому

    Thank you 😊

  • @rydmerlin
    @rydmerlin 6 днів тому

    Why did you choose the names Bob and Sandra?

    • @Indently
      @Indently  6 днів тому +1

      Only my therapist could tell you that

  • @nombable
    @nombable 9 днів тому

    Can chainmaps return one collapsed dictionary?

  • @Nacho_Meter_Stick
    @Nacho_Meter_Stick 8 днів тому

    That sounds like inheritance

  • @TheIpicon
    @TheIpicon 3 дні тому +2

    Your example isn’t practical, because you could achieve the same by just merging the user preference inside the defaults and not doing the opposite…

  • @GuyMichaely
    @GuyMichaely 9 днів тому

    I'm struggling to see the purpose of this feature. Why would I use it over `|`?

    • @Comonad
      @Comonad 9 днів тому

      Variadic parameters with short circuiting on lookup, also `|` will create a new copy whereas `chainmap` won’t.

  • @stan4676
    @stan4676 9 днів тому +1

    type ChainDict = ChainMap

    • @ArturdeSousaRocha
      @ArturdeSousaRocha 8 днів тому

      This doesn't solve the problem of the maps attribute.

  • @lujoconnor
    @lujoconnor 8 днів тому

    def get_preference(key):
    return user_preference[key] if key in user_preference else default_preference[key]
    😕

    • @Indently
      @Indently  8 днів тому

      With two dictionaries you can do that, but now imagine you have three or more.

    • @lujoconnor
      @lujoconnor 8 днів тому

      @indently sure, but:
      For d in my_dicts:
      If key in d:
      Return d[key]
      I feel like Python devs have an obsession with convenience, and chainmap is a prime example

    • @Indently
      @Indently  8 днів тому

      It's true that you can deconstruct any built-in function and manually write the functionality. But personally I would never re-write something that's already included in Python. I'm not going to compete with the professionals that spent years on optimising these functions for everyday use.
      Convenience is why most of us code in Python (and money probably too).

  • @Finkelfunk
    @Finkelfunk 9 днів тому

    To me, it makes absolutely NO sense why the first map is the "child" and all the other maps are "parents". Shouldn't it be the other way around? Or is it called "child" because it is the node that is always updated when inserting something? Still conceptually the names are kind of horribly chosen.

    • @maximkovalkov1334
      @maximkovalkov1334 9 днів тому +3

      Well, perhaps it’s by analogy with inheritance in OOP. Python itself first looks up a method on an object’s class, and if it doesn’t exist, it then looks at the _parent_ class (or classes) for a method with that name. If there are attributes/methods with the same name present in both the parent and the child classes, then the child takes precedence ( _overriding_ the parent)

  • @dirtydevotee
    @dirtydevotee 6 днів тому +1

    - To all new Python coders who might be watching this: don't EVER call your dictionary "D1" or you will be fired.
    - Also, he says at 4:13 that type annotations are "...just telling Python that this is of type 'ChainMap'...", but that is incorrect. Type annotations were introduced in 3.5 and Python does not care about them at all. They are solely for coders to tell other devs what type is expected. You can, however, use "__annotations__" or type conversion ("int(input("Enter an integer: "))") to create enforcement checks, flags, etc.

    • @xetera
      @xetera 3 дні тому

      how is that incorrect? You're hinting the type checker on the type of the variable. Maybe it would've been more accurate if he said something like "you're telling pyright the type of the variable" or whatever type checker you use but that just seems nitpicky.
      If anything, not telling the audience why and when you'd want to use chainmap over the way simpler alternative {**a, **b} is the real problem

    • @dirtydevotee
      @dirtydevotee 3 дні тому

      @@xetera I'm not sure how I could clarify my original post. Python doesn't care about type annotations; they're for the humans.

    • @xetera
      @xetera 3 дні тому

      @dirtydevotee And python tooling like pyright. Otherwise it would just be a comment.

  • @irapk79
    @irapk79 3 дні тому

    nce

  • @zaphkielplays5675
    @zaphkielplays5675 9 днів тому

    early comment love your videos

  • @DrDeuteron
    @DrDeuteron 9 днів тому +5

    >>>d1 = dict(a=1, b=2)
    >>>d2 = dict(c=3, d=4)
    is much clearer python. It directly types a dictionary with string keys assigned to int literals. Are the type hints just for your IDE? imo human readability comes before your IDE. But why would you type a dict anyway? If you want you special dict[str]=int, just subclass the builtin and assert it.

    • @Satish-st5le
      @Satish-st5le 9 днів тому +2

      He is doing a demonstration and making us introduce a new concept for people who don't know about it. It's not about you, its about how you teach others.

    • @landsgevaer
      @landsgevaer 9 днів тому +4

      Why is that "much clearer python"?

    • @kamurashev
      @kamurashev 9 днів тому

      @@landsgevaerare you really wanna hear the boring story of “pythonistic way” they come about whenever they want to make you believe their way is the best way?

    • @sebastian8436
      @sebastian8436 9 днів тому +2

      "Your code is not readable and I am the best programmer in the world"😂🥳

    • @endersteph
      @endersteph 9 днів тому +2

      This is not much clearer and is in fact unpythonic. Writing it correctly as d = {"a": 0} for example also correctly types d as dict[str, int] for the IDE's typechecker. Specifying it as d: dict[str, int] = {"a": 0} is just for readability, just like any other type hint in python.

  • @valentinrafael9201
    @valentinrafael9201 4 дні тому

    These functions are so ridiculous. Who is this for? People bad at algorithms that don’t care about calling 69 functions just o have some trivial thing done for them? It’s a Node where they are passed by reference (cuz it’s an array ). Could just make a soa in np and get better performance.

  • @aquaunknown
    @aquaunknown 9 днів тому +3

    19 views in one minute

    • @DanJimmy
      @DanJimmy 8 днів тому

      He really fell off

  • @mariusmeyer903
    @mariusmeyer903 4 дні тому

    How can I stop getting the german versions of the videos? They are awful.

    • @Indently
      @Indently  4 дні тому

      I honestly don’t know, I didn’t see any settings on my end to change the translations anywhere.

  • @mahboobalam5689
    @mahboobalam5689 6 днів тому +1

    Too much overkill with all those type definitions, when these don't do literally anything at the compile side, at least for this demo. The elegance of python programming being succinct is being corrupted bit by bit.

    • @denom
      @denom 6 днів тому +1

      Have you ever worked in a very large Python codebase, which is not even yours? Typehints will save your life.

    • @vandelayindustries2971
      @vandelayindustries2971 2 дні тому

      @@denom Yes and there they are useful especially for function signatures. But annotating a little variable in a tutorial with list[str] when you assign ["abc"] to it 3 characters later just makes your code less readable.

    • @Indently
      @Indently  День тому

      As a teacher, consistency is important. I make plenty of mistakes when explaining things and even for what you call "a little variable", I sometimes inadvertently insert the wrong datatype literally 3 characters later, and when you're explaining things, a little mistake like that can take time to find.
      But with type annotations I spot it immediately if I make a mistake. I annotate 100% of the time, that's my preference and it saves me far more than if I were to skip typing these little variables.
      The best part about type annotations in tutorials, is that you don't need to copy them, and you can follow whatever conventions you want.

  • @coolcodingcat
    @coolcodingcat 6 днів тому

    In the last one: what about cm.parent.parent?