[Python-ideas] adding dictionaries
Steven D'Aprano
steve at pearwood.info
Tue Jul 29 15:35:56 CEST 2014
On Mon, Jul 28, 2014 at 11:15:44PM -0700, Andrew Barnert wrote:
> On Monday, July 28, 2014 8:34 PM, Steven D'Aprano <steve at pearwood.info> wrote:
> [snip]
> > * when called from a class, it should behave like a class method:
> > MyMapping.merged(a, b, c) should return an instance of MyMapping;
> >
> > * but when called from an instance, it should behave like an instance
> > method, with self included in the chain of mappings to merge:
> > a.merged(b, c) rather than a.merged(a, b, c).
> >
> >
> > I have a descriptor type which implements the behaviour from the last
> > two bullet points, so from a technical standpoint it's not hard to
> > implement this. But I can imagine a lot of push-back from the more
> > conservative developers about adding a *fourth* method type (even if it
> > is private) to the Python builtins, so it would take a really compelling
> > use-case to justify adding a new method type and a new dict method.
> >
> > (Personally, I think this hybrid class/instance method type is far more
> > useful than staticmethod, since I've actually used it in production
> > code, but staticmethod isn't going away.)
>
>
> How is this different from a plain-old (builtin or normal) method?
I see I failed to explain clearly, sorry about that.
With class methods, the method always receives the class as the first
argument. Regardless of whether you write dict.fromkeys or
{1:'a'}.fromkeys, the first argument is the class, dict.
With instance methods, the method receives the instance. If you call it
from a class, the method is "unbound" and you are responsible for
providing the "self" argument.
To me, this hypothetical merged() method sometimes feels like an
alternative constructor, like fromkeys, and therefore best written as a
class method, but sometimes like a regular method. Since it feels like a
hybrid to me, I think a hybrid descriptor approach is best, but as I
already said I can completely understand if conservative developers
reject this idea.
In the hybrid form I'm referring to, the first argument provided is the
class when called from the class, and the instance when called from an
instance. Imagine it written in pure Python like this:
class dict:
@hybridmethod
def merged(this, *args, **kwargs):
if isinstance(this, type):
# Called from the class
new = this()
else:
# Called from an instance.
new = this.copy()
for arg in args:
new.update(arg)
new.update(kwargs)
return new
If merged is a class method, we can avoid having to worry about the
case where your "a" mapping happens to be a list of (key,item) pairs:
a.merged(b, c, d) # Fails if a = [(key, item), ...]
dict.merged(a, b, c, d) # Always succeeds.
It also allows us to easily specify a different mapping type for the
result:
MyMapping.merged(a, b, c, d)
although some would argue this is just as clear:
MyMapping().merged(a, b, c, d)
albeit perhaps not quite as efficient if MyMapping is expensive to
instantiate. (You create an empty instance, only to throw it away
again.)
On the other hand, there are use-cases where merged() best communicates
the intent if it is a regular instance method. Consider:
settings = application_defaults.merged(
global_settings,
user_settings,
commandline_settings)
seems more clear to me than:
settings = dict.merged(
application_defaults,
global_settings,
user_settings,
commandline_settings)
especially in the case that application_defaults is a dict literal.
tl;dr It's not often that I can't decide whether a method ought to be a
class method or an instance method, the decision is usually easy, but
this is one of those times.
--
Steven
More information about the Python-ideas
mailing list