4 ms·
Can I hijack this post for a nit-pick? While I love Python and feel most comfortable with it, it always bothered me that it make a distinction between accessin
by anticristi 5y ago
Can I hijack this post for a nit-pick?
While I love Python and feel most comfortable with it, it always bothered me that it make a distinction between accessing a dictionary item and accessing an object member. E.g.:
a["getStuff"]() # call the "getStuff" function of dictionary "a"
b.getStuff() # call the "getStuff" method of object "b"
In contrast, in JavaScript, these two are equivalant.
a["getStuff"]()
a.getStuff()
I find JavaScript more dev-friendly. Any idea why Python didn't opt for the same approach? Would make sense to me, since "everything is a dictionary/object".
- rhacker 5y agoI ran into this recently while learning it... confused me for a long time because I'm typically writing TS for my day job.
- supervisual 5y agoI would argue opposite, For a long time JS had this issue where when you iterated on keys in a dictionary you had to ensure you were skipping methods etc via .hasOwnProperty check. Python by making clear distinction between contents and functionality avoids this.
- neoncontrails 5y agoHighly recommend looking into Munch, "a dictionary that supports attribute-style access, a la JavaScript." https://github.com/Infinidat/munch https://github.com/Infinidat/munch In theory, implementing attribute-style access on user-defined Python object is just a matter of overriding its __(get|set)attr__ methods, for example: In [1]: import collections In [2]: class DotDict(collections.UserDict): 2 def __getattr__(self, k): 3 try: 4 return self.__getitem__(k) 5 except: 6 return super().__getattribute__(k) 7 def __setattr__(self, k, v): 8 try: 9 self.__setitem__(k, v) 10 except Exception as e: 11 super().__setattr__(k,v) In [3]: d = DotDict({'a': 'b', 'c': [1,2,3], 'square': lambda x: x*x}) In [4]: d.square(4) Out[4]: 16 In [5]: d Out[5]: {'a': 'b', 'c': [1, 2, 3], 'square': <function <lambda> at 0x7f9ef5d5b0d0>} In [6]: import datetime In [7]: d.created_at = datetime.datetime.now() In [8]: d Out[8]: {'a': 'b', 'c': [1, 2, 3], 'square': <function <lambda> at 0x7f9ef5d5b0d0>, 'created_at': datetime.datetime(2021, 7, 19, 19, 42, 32, 341393)} In practice, this can lead to headaches. Take, for example, what happens if we try to serialize `d` (which has a non-serializable lambda function in its keys) using the built-in pickle module: In [9]: import pickle In [10]: pickle.dumps(d) Traceback (most recent call last): File "<stdin>", line 1, in <module> _pickle.PicklingError: Can't pickle <class 'DotDict'>: attribute lookup DotDict on builtins failed Thankfully there's a third-party library called cloudpickle which can serialize just about any python object to bytes -- even including the user-defined DotDict class (!): In [12]: deserialized_d = cloudpickle.loads(cloudpickle.dumps(d)) In [13]: deserialized_d Out[13]: {'a': 'b', 'c': [1, 2, 3], 'square': <function <lambda> at 0x7f9ef650fee0>, 'created_at': datetime.datetime(2021, 7, 19, 19, 42, 32, 341393)} Since the entire class definition is serialized along with the instance, the deserialized copy preserves the object's attribute-style interface as before: In [14]: deserialized_d.created_at.strftime("%c") Out[14]: 'Mon Jul 19 19:42:32 2021' But I recommend Munch over rolling your own because subclassing `dict` is fraught with a surprising number of edge cases.