Received: from localhost (HELO mail.python.org) (127.0.0.1)
by albatross.python.org with SMTP; 29 Jul 2014 15:41:10 +0200
Received: from ipmail05.adl6.internode.on.net (unknown [150.101.137.143])
by mail.python.org (Postfix) with ESMTP
for <python-ideas-+ZN9ApsXKcEdnm+***@public.gmane.org>; Tue, 29 Jul 2014 15:41:08 +0200 (CEST)
Received: from ppp118-209-248-249.lns20.mel6.internode.on.net (HELO
pearwood.info) ([118.209.248.249])
by ipmail05.adl6.internode.on.net with ESMTP; 29 Jul 2014 23:05:59 +0930
Received: by pearwood.info (Postfix, from userid 1000)
id 33E08120605; Tue, 29 Jul 2014 23:35:56 +1000 (EST)
Content-Disposition: inline
In-Reply-To: <1406614544.48360.YahooMailNeo-zpER/x4socO2Y7dhQGSVAJOW+***@public.gmane.org>
User-Agent: Mutt/1.4.2.2i
X-BeenThere: python-ideas-+ZN9ApsXKcEdnm+***@public.gmane.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: Discussions of speculative Python language ideas
<python-ideas.python.org>
List-Unsubscribe: <https://mail.python.org/mailman/options/python-ideas>,
<mailto:python-ideas-request-+ZN9ApsXKcEdnm+***@public.gmane.org?subject=unsubscribe>
List-Archive: <http://mail.python.org/pipermail/python-ideas/>
List-Post: <mailto:python-ideas-+ZN9ApsXKcEdnm+***@public.gmane.org>
List-Help: <mailto:python-ideas-request-+ZN9ApsXKcEdnm+***@public.gmane.org?subject=help>
List-Subscribe: <https://mail.python.org/mailman/listinfo/python-ideas>,
<mailto:python-ideas-request-+ZN9ApsXKcEdnm+***@public.gmane.org?subject=subscribe>
Errors-To: python-ideas-bounces+gcpi-python-ideas=m.gmane.org-+ZN9ApsXKcEdnm+***@public.gmane.org
Sender: "Python-ideas"
<python-ideas-bounces+gcpi-python-ideas=m.gmane.org-+ZN9ApsXKcEdnm+***@public.gmane.org>
Archived-At: <http://permalink.gmane.org/gmane.comp.python.ideas/28480>
Post by Andrew Barnert[snip]
MyMapping.merged(a, b, c) should return an instance of MyMapping;
* but when called from an instance, it should behave like an instance
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