Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #72904 > unrolled thread
| Started by | jongiddy <jongiddy@gmail.com> |
|---|---|
| First post | 2014-06-06 23:45 -0700 |
| Last post | 2014-06-08 11:00 -0600 |
| Articles | 17 on this page of 37 — 8 participants |
Back to article view | Back to comp.lang.python
Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-06 23:45 -0700
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-07 13:20 -0600
Re: Uniform Function Call Syntax (UFCS) Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2014-06-08 13:27 +1200
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 01:26 -0700
Re: Uniform Function Call Syntax (UFCS) Paul Sokolovsky <pmiscml@gmail.com> - 2014-06-08 15:06 +0300
Re: Uniform Function Call Syntax (UFCS) Marko Rauhamaa <marko@pacujo.net> - 2014-06-08 18:56 +0300
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 10:38 -0600
Re: Uniform Function Call Syntax (UFCS) Paul Sokolovsky <pmiscml@gmail.com> - 2014-06-08 19:40 +0300
Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 04:33 +0000
Re: Uniform Function Call Syntax (UFCS) Marko Rauhamaa <marko@pacujo.net> - 2014-06-09 09:25 +0300
Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 09:09 +0000
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 19:13 +1000
Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 13:37 +0000
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-10 01:08 +1000
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 09:24 -0700
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 09:34 -0700
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 10:54 -0600
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 03:10 +1000
Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 03:20 +0000
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 13:44 +1000
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 23:38 -0700
Re: Uniform Function Call Syntax (UFCS) Roy Smith <roy@panix.com> - 2014-06-08 23:45 -0400
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 02:25 -0700
Re: Uniform Function Call Syntax (UFCS) Roy Smith <roy@panix.com> - 2014-06-08 10:59 -0400
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 08:39 -0700
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 02:48 +1000
Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 03:53 +0000
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 14:53 +1000
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-09 08:24 -0600
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-09 23:43 -0700
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 11:08 -0600
Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 03:13 +1000
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 11:24 -0600
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 13:35 -0700
Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 01:15 -0700
Re: Uniform Function Call Syntax (UFCS) Paul Sokolovsky <pmiscml@gmail.com> - 2014-06-08 14:52 +0300
Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 11:00 -0600
Page 2 of 2 — ← Prev page 1 [2]
| From | jongiddy <jongiddy@gmail.com> |
|---|---|
| Date | 2014-06-08 23:38 -0700 |
| Message-ID | <c5540e34-dd73-444e-b6a5-a418b9085df5@googlegroups.com> |
| In reply to | #73001 |
On Monday, 9 June 2014 04:44:22 UTC+1, Chris Angelico wrote:
> This could be solved, though, by having a completely different symbol
> that means "the thing on my left is actually the first positional
> parameter in the function call on my right", such as in your example:
>
> > plus(1, 2) | divide(2)
>
> This would be absolutely identical to:
>
> divide(plus(1, 2), 2)
>
> Maybe you could even make it so that:
>
> plus(1, 2) x=| divide(y=2)
>
> is equivalent to
>
> divide(x=plus(1, 2), y=2)
>
> for the sake of consistency, and to allow the pipeline to inject
> something someplace other than the first argument.
>
> I'm not sure whether it'd be as useful in practice, though. It would
> depend partly on the exact syntax used. Obviously the pipe itself
> can't be used as it already means bitwise or, and this needs to be
> really REALLY clear about what's going on. But a data-flow notation
> would be of value in theory, at least.
Perhaps a pipeline symbol plus an insertion marker would work better in Python:
plus(1, 2) ~ divide(x=^, y=2)
f.readlines() ~ map(int, ^) ~ min(^, key=lambda n: n % 10).str() ~ base64.b64encode(^, b'?-') ~ print(^)
Stdio.read_file("foo.jpg") ~ Image.JPEG_decode(^).autocrop().rotate(0.5).grey() ~ Image.PNG_encode(^) ~ Stdio.write_file("foo.png", ^)
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2014-06-08 23:45 -0400 |
| Message-ID | <roy-644CE9.23454208062014@news.panix.com> |
| In reply to | #73000 |
In article <53952807$0$29988$c3e8da3$5496439d@news.astraweb.com>, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: > (Note that Forth is brilliant here, as it exposes the argument stack and > gives you a rich set of stack manipulation commands.) As does PostScript (which, despite its reputation as a printer format, is really a full-fledged programming language). I suspect that people who didn't grow up with RPN (i.e. H/P calculators) find it amazingly obtuse. In much the same way I find Objective-C amazingly obtuse. Oh, wait, that's the other thread.
[toc] | [prev] | [next] | [standalone]
| From | jongiddy <jongiddy@gmail.com> |
|---|---|
| Date | 2014-06-08 02:25 -0700 |
| Message-ID | <1dd863ba-09e5-439b-8669-db65f3e999eb@googlegroups.com> |
| In reply to | #72947 |
On Sunday, 8 June 2014 02:27:42 UTC+1, Gregory Ewing wrote: > > Also it doesn't sit well with Python's "one obvious > way to do it" guideline, because it means there are > *two* equally obvious ways to call a function. Actually, one of the best arguments against introducing UFCS is that Python currently provides two equivalent ways to check if an instance has an attribute: ask-permission using hasattr and ask-forgiveness using AttributeError. On the negative side, these currently equivalent (aside from performance) techniques could give different results using UFCS, potentially breaking some code. On the positive side, that means the proposal would add one "two ways to do something" and eliminate another "two ways to do something", giving a net Zen of Python effect of zero.
[toc] | [prev] | [next] | [standalone]
| From | Roy Smith <roy@panix.com> |
|---|---|
| Date | 2014-06-08 10:59 -0400 |
| Message-ID | <roy-68FCA5.10591408062014@news.panix.com> |
| In reply to | #72959 |
In article <1dd863ba-09e5-439b-8669-db65f3e999eb@googlegroups.com>, jongiddy <jongiddy@gmail.com> wrote: > On Sunday, 8 June 2014 02:27:42 UTC+1, Gregory Ewing wrote: > > > > Also it doesn't sit well with Python's "one obvious > > way to do it" guideline, because it means there are > > *two* equally obvious ways to call a function. > > Actually, one of the best arguments against introducing UFCS is that Python > currently provides two equivalent ways to check if an instance has an > attribute: ask-permission using hasattr and ask-forgiveness using > AttributeError. > > On the negative side, these currently equivalent (aside from performance) > techniques could give different results using UFCS, potentially breaking some > code. Why? I assume a language which promoted the global namespace to be in the attribute search path (which, as far as I can tell, is what we're talking about here) would implement hasattr and raising AttributeError in a consistent way.
[toc] | [prev] | [next] | [standalone]
| From | jongiddy <jongiddy@gmail.com> |
|---|---|
| Date | 2014-06-08 08:39 -0700 |
| Message-ID | <927afb61-be0e-43a1-8aab-107e77a013fc@googlegroups.com> |
| In reply to | #72964 |
On Sunday, 8 June 2014 15:59:14 UTC+1, Roy Smith wrote:
> Why? I assume a language which promoted the global namespace to be in
> the attribute search path (which, as far as I can tell, is what we're
> talking about here) would implement hasattr and raising AttributeError
> in a consistent way.
It's slightly different. Although I used len() as an example, the idea is to allow any function to be used in this way, including local symbols.
e.g. I could define:
def squared(x):
return x * x
i = 3
i.squared() => 9
j = AClassThatImplements__mul__()
j.squared() => whatever j * j returns
but also:
class AnotherClass:
def __mul__(self, other):
...
def squared(self):
return specialised_method_for_calculating_squares()
k = AnotherClass()
k.squared() => calls method, not function
In this case, there is a problem with letting hasattr('squared') return True for these first two instances. See Ian's post for a description of the problem.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-06-09 02:48 +1000 |
| Message-ID | <mailman.10882.1402246096.18130.python-list@python.org> |
| In reply to | #72965 |
On Mon, Jun 9, 2014 at 1:39 AM, jongiddy <jongiddy@gmail.com> wrote:
> e.g. I could define:
>
> def squared(x):
> return x * x
>
> i = 3
> i.squared() => 9
>
> j = AClassThatImplements__mul__()
> j.squared() => whatever j * j returns
>
> but also:
> class AnotherClass:
> def __mul__(self, other):
> ...
> def squared(self):
> return specialised_method_for_calculating_squares()
>
> k = AnotherClass()
> k.squared() => calls method, not function
>
> In this case, there is a problem with letting hasattr('squared') return True for these first two instances. See Ian's post for a description of the problem.
class Circle:
def squared(self):
raise NotImplementedError("Proven impossible in 1882")
The trouble is that logically Circle does have a 'squared' attribute,
while 3 doesn't; and yet Python guarantees this:
foo.squared()
# is equivalent [1] to
func = foo.squared
func()
Which means that for (3).squared() to be 9, it has to be possible to
evaluate (3).squared, which means that hasattr (which is defined by
attempting to get the attribute and seeing if an exception is thrown)
has to return True.
Except that it's even more complicated than that, because hasattr
wasn't defined in your module, so it has a different set of globals.
In fact, this would mean that hasattr would become quite useless.
(Hmm, PEP 463 might become a prerequisite of your proposal...) It also
means that attribute lookup becomes extremely surprising any time the
globals change; currently, "x.y" means exactly the same thing for any
given object x and attribute y, no matter where you do it.
The only way I can think of for all this to make sense is actually
doing it the other way around. Instead of having x.y() fall back on
y(x), have y(x) attempt x.y() first. To pull this off, you'd need a
special bouncer around every global or builtin... which may be tricky.
class MagicDict(dict):
def __getitem__(self, item):
# If this throws, let the exception propagate
obj = super().__getitem__(item)
if not callable(obj): return obj
def bouncer(*a, **kw):
if len(a)==1 and not kw:
try: return getattr(a[0], item)()
except AttributeError: pass
return obj(*a, **kw)
return bouncer
import __main__
# Except that this bit doesn't work.
__main__.__dict__ = MagicDict(__main__.__dict__)
It's theoretically possible, along these lines, I think. Whether it's
actually any good or not is another question, though!
ChrisA
[1] Modulo performance. CPython, AFAIK, does this exactly as written,
but other Pythons may and do optimize the actual "foo.squared()" form
to reduce heap usage. But in terms of visible effects, equivalent.
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2014-06-09 03:53 +0000 |
| Message-ID | <53952fa9$0$29988$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #72974 |
On Mon, 09 Jun 2014 02:48:13 +1000, Chris Angelico wrote:
> class Circle:
> def squared(self):
> raise NotImplementedError("Proven impossible in 1882")
>
> The trouble is that logically Circle does have a 'squared' attribute,
> while 3 doesn't; and yet Python guarantees this:
>
> foo.squared()
> # is equivalent [1] to
> func = foo.squared
> func()
>
> Which means that for (3).squared() to be 9, it has to be possible to
> evaluate (3).squared,
Given UFCS, that ought to return the global squared function, curried
with 3 as its first (and only) argument.
UFCS would be a pretty big design change to Python, but I don't think it
would be a *problem* as such. It just means that x.y, hasattr(x, y) etc.
would mean something different to what they currently mean.
> which means that hasattr (which is defined by
> attempting to get the attribute and seeing if an exception is thrown)
> has to return True.
Yes. And this is a problem why?
Obviously it would mean that the semantics of hasattr will be different
than they are now, but it's still a coherent set of semantics.
In fact, one can already give a class a __getattr__ method which provides
UFCS functionality. (Hmmm, you need a way to get the caller's globals.
You know, this keeps coming up. I think it's high-time Python offered
this as a supported function.) That's no more a problem than any other
dynamically generated attribute.
Stick that __getattr__ in object itself, and UFCS is now language wide.
That would make an awesome hack for anyone wanting to experiment with
this!
> Except that it's even more complicated than that, because hasattr wasn't
> defined in your module, so it has a different set of globals.
hasattr doesn't care about globals, nor does it need to. hasattr behaves
like the equivalent to:
def hasattr(obj, name):
try:
obj.name
except AttributeError:
return False
return True
give or take. And yes, if accessing your attribute has side effects,
using hasattr does too:
py> class Spam(object):
... @property
... def spam(self):
... print("Spam spam spam spam LOVERLY SPAAAAM!!!!")
... return "spam"
...
py> x = Spam()
py> hasattr(x, "spam")
Spam spam spam spam LOVERLY SPAAAAM!!!!
True
If that's a worry to you, you can try inspect.getattr_static.
> In fact,
> this would mean that hasattr would become quite useless. (Hmm, PEP 463
> might become a prerequisite of your proposal...) It also means that
> attribute lookup becomes extremely surprising any time the globals
> change; currently, "x.y" means exactly the same thing for any given
> object x and attribute y, no matter where you do it.
*cough*
class Example:
def __getattr__(self, name):
if name == 'module_name':
if __name__ == '__main__':
return "NOBODY expects the Spanish Inquisition!"
else:
return __name__
raise AttributeError("no attribute %r" % name)
:-)
--
Steven D'Aprano
http://import-that.dreamwidth.org/
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-06-09 14:53 +1000 |
| Message-ID | <mailman.10905.1402289625.18130.python-list@python.org> |
| In reply to | #73003 |
On Mon, Jun 9, 2014 at 1:53 PM, Steven D'Aprano <steve+comp.lang.python@pearwood.info> wrote: >> which means that hasattr (which is defined by >> attempting to get the attribute and seeing if an exception is thrown) >> has to return True. > > Yes. And this is a problem why? > > Obviously it would mean that the semantics of hasattr will be different > than they are now, but it's still a coherent set of semantics. Coherent perhaps, but in direct opposition to the OP's statement about how hasattr should return False even if there's a global to be found. A coherent meaning for this kind of thing would almost certainly not be possible within the OP's requirements, although it's entirely possible something sensible could be put together. (By the way, would (3).squared return a curried reference to squared as of when you looked it up, or would it return something that late-binds to whatever 'squared' is in scope as of when you call it? If the latter, then hasattr would have to always return True, and getattr would have to return something that does the late-bind lookup and turns NameError into AttributeError.) ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2014-06-09 08:24 -0600 |
| Message-ID | <mailman.10914.1402324360.18130.python-list@python.org> |
| In reply to | #73003 |
[Multipart message — attachments visible in raw view] — view raw
On Jun 8, 2014 9:56 PM, "Steven D'Aprano" > > which means that hasattr (which is defined by > > attempting to get the attribute and seeing if an exception is thrown) > > has to return True. > > Yes. And this is a problem why? Earlier in this thread I pointed out that returning True creates problems for duck typing. But I'm now convinced that's preferable to making getattr and hasattr inconsistent.
[toc] | [prev] | [next] | [standalone]
| From | jongiddy <jongiddy@gmail.com> |
|---|---|
| Date | 2014-06-09 23:43 -0700 |
| Message-ID | <d3c52137-534f-4f65-a398-c7f6cb5d38b6@googlegroups.com> |
| In reply to | #73032 |
So, just to summarise the discussion: There was some very mild support for readable pipelines, either using UFCS or an alternative syntax, but the Pythonic way to make combinations of function and method applications readable is to assign to variables over multiple lines. Make the code read down, not across. The idea that a class method could override a function using UFCS didn't get much traction. From Zen of Python, "explicit is better than implicit" means no differences in behaviour, depending on context. The fact that x.y and x.__getattr__ may behave differently under UFCS is also a problem. Since hasattr testing and AttributeError catching are both commonly used now, this could cause real problems, so could probably not be changed until Python 4. Finally, Gilbert & Sullivan are definitely due a revival.
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2014-06-08 11:08 -0600 |
| Message-ID | <mailman.10885.1402247374.18130.python-list@python.org> |
| In reply to | #72965 |
On Sun, Jun 8, 2014 at 10:48 AM, Chris Angelico <rosuav@gmail.com> wrote: > Except that it's even more complicated than that, because hasattr > wasn't defined in your module, so it has a different set of globals. > In fact, this would mean that hasattr would become quite useless. hasattr is a builtin, so it has no globals at all. It would have to use the calling scope for UFCS resolution as in my example implementation.
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2014-06-09 03:13 +1000 |
| Message-ID | <mailman.10887.1402247607.18130.python-list@python.org> |
| In reply to | #72965 |
On Mon, Jun 9, 2014 at 3:08 AM, Ian Kelly <ian.g.kelly@gmail.com> wrote: > On Sun, Jun 8, 2014 at 10:48 AM, Chris Angelico <rosuav@gmail.com> wrote: >> Except that it's even more complicated than that, because hasattr >> wasn't defined in your module, so it has a different set of globals. >> In fact, this would mean that hasattr would become quite useless. > > hasattr is a builtin, so it has no globals at all. It would have to > use the calling scope for UFCS resolution as in my example > implementation. Same difference. It can't simply look for the name in globals(), it has to figure out based on the caller's globals. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2014-06-08 11:24 -0600 |
| Message-ID | <mailman.10890.1402249956.18130.python-list@python.org> |
| In reply to | #72965 |
On Sun, Jun 8, 2014 at 11:13 AM, Chris Angelico <rosuav@gmail.com> wrote: > On Mon, Jun 9, 2014 at 3:08 AM, Ian Kelly <ian.g.kelly@gmail.com> wrote: >> On Sun, Jun 8, 2014 at 10:48 AM, Chris Angelico <rosuav@gmail.com> wrote: >>> Except that it's even more complicated than that, because hasattr >>> wasn't defined in your module, so it has a different set of globals. >>> In fact, this would mean that hasattr would become quite useless. >> >> hasattr is a builtin, so it has no globals at all. It would have to >> use the calling scope for UFCS resolution as in my example >> implementation. > > Same difference. It can't simply look for the name in globals(), it > has to figure out based on the caller's globals. But that would all be done in getattr, so I don't think it affects hasattr's implementation at all. Since hasattr doesn't push anything onto the stack, getattr doesn't have to care whether it was called directly from Python or indirectly via getattr; either way the scope it needs is just the top frame of the stack. Could be a different matter in other implementations, though.
[toc] | [prev] | [next] | [standalone]
| From | jongiddy <jongiddy@gmail.com> |
|---|---|
| Date | 2014-06-08 13:35 -0700 |
| Message-ID | <23f69b68-6efb-49b7-bd01-ada3dea027d3@googlegroups.com> |
| In reply to | #72982 |
On Sunday, 8 June 2014 18:24:28 UTC+1, Ian wrote: > > But that would all be done in getattr, so I don't think it affects > hasattr's implementation at all. Since hasattr doesn't push anything > onto the stack, getattr doesn't have to care whether it was called > directly from Python or indirectly via getattr; either way the scope > it needs is just the top frame of the stack. > > Could be a different matter in other implementations, though. In CPython, the UFCS would not be done in PyObject_GetAttr() as that would affect hasattr() as well. Instead, it would be implemented in the bytecode for LOAD_ATTR. If LOAD_ATTR was about to return an AttributeError, e.g. for [].len, it would perform the equivalent of a LOAD_NAME operation, with the difference that if the name is not found or is not callable, it returns AttributeError instead of NameError. If the name is found, then it would return something: for [].len, it would return the len() function wrapped to know that it's first argument was the list, which might be done by creating a fake Method object, as shown in Ian's code. But getattr([], 'len') and hasattr([], 'len') would both return False. I'm beginning to think it is too un-Pythonic - too much implicitness, unless it can be spelt differently, something like [].len(_) or [].len(...) to explicitly indicate that it plans to call a function, but might call a method if one is available.
[toc] | [prev] | [next] | [standalone]
| From | jongiddy <jongiddy@gmail.com> |
|---|---|
| Date | 2014-06-08 01:15 -0700 |
| Message-ID | <7c2fd803-616d-4daa-b570-3c5de140a1da@googlegroups.com> |
| In reply to | #72928 |
Thanks for the extensive feedback. Here's my thoughts on how to address these issues.
On Saturday, 7 June 2014 20:20:48 UTC+1, Ian wrote:
>
> It's a nice feature in a statically typed language, but I'm not sure
> how well it would work in a language as dynamic as Python. There are
> some questions that would need to be addressed.
>
> 1) Where should the function (or perhaps callable) be looked for? The
> most obvious place is the global scope. I think it would be a bit too
> far-reaching and inconsistent with other language features to reach
> directly inside imported modules (not to mention that it could easily
> get to be far too slow in a module with lots of imports). As a result
> it would have to be imported using the "from module import function"
> syntax, rather than the somewhat cleaner "import module" syntax.
>
> While there's nothing wrong with such imports, I'm not sure I like the
> thought of the language encouraging them any more than necessary.
It would only work on functions in scope. x.len() would only work if len(x) would work. I actually think this would work better in Python than in D. In D, "import module;" imports all the symbols from the module, so it is easier to invoke a function unexpectedly. In Python, "import module" does not fill the namespace with lots of callable symbols, so UFCS would generally work with built-ins, local functions, or functions explicitly imported with "from module import...". In this case, the need to use the "from module import fname" form can document that something unusual is happening.
> 2) What about getattr and hasattr? If I call hasattr(x,
> "some_method"), and x has no such attribute, but there is a function
> in the global scope named "some_method", should it return True?
> If we instead have hasattr return False though, and have getattr raise
> an exception, then we have this very magical and confusing
> circumstance where getattr(x, 'method') raises an exception but
> x.method does not. So I don't think that's really a good scenario
> either.
AS you suggest, the preferable route is that hasattr should return False. The object clearly does not have that attribute. It is a property of the current module that the object can use "instance.fname". While the behaviour that hasattr("fname") returns False, but instance.fname works is an exception, and a function could be added to test this quickly, so new code that cares could use:
if hasattr(instance, "fname") or inscopecallable('fname'):
The bigger problem I find is reading other code that uses UFCS and not realising that a "method" is not actually a method of the class, but requires importing a module. That can cause confusion when trying to use it in your own code. However, the need to use "from module import fname" would at least link the method name and the module.
> Also the idea makes me nervous in the thought that an incorrect
> attribute access could accidentally and somewhat randomly pick up some
> object from the environment.
As before, I think the limited number of strange callable objects in most modules in Python protects against this. Of course, "from module import *" might cause problems, but that is already true. You need to be extra careful doing this, and should only do it for modules when you have a reasonable understanding of their exported names.
> But if you want to experiment with the idea, here's a (lightly tested)
> mixin that implements the behavior:
Thanks for the headstart! I'll need to read up on descriptors to understand that last bit fully (when a function has a __get__ method).
One problem with your untested code, the superclasses would need to be checked before using UFCS, so the structure is:
try:
return super().__getattr__(attr)
except AttributeError:
# resolve using UFCS
[toc] | [prev] | [next] | [standalone]
| From | Paul Sokolovsky <pmiscml@gmail.com> |
|---|---|
| Date | 2014-06-08 14:52 +0300 |
| Message-ID | <mailman.10875.1402228342.18130.python-list@python.org> |
| In reply to | #72957 |
Hello, On Sun, 8 Jun 2014 01:15:43 -0700 (PDT) jongiddy <jongiddy@gmail.com> wrote: > Thanks for the extensive feedback. Here's my thoughts on how to > address these issues. > > On Saturday, 7 June 2014 20:20:48 UTC+1, Ian wrote: > > > > It's a nice feature in a statically typed language, but I'm not sure > > how well it would work in a language as dynamic as Python. There > > are some questions that would need to be addressed. > > > > 1) Where should the function (or perhaps callable) be looked for? > > The most obvious place is the global scope. I think it would be a > > bit too far-reaching and inconsistent with other language features > > to reach directly inside imported modules (not to mention that it > > could easily get to be far too slow in a module with lots of > > imports). As a result it would have to be imported using the "from > > module import function" syntax, rather than the somewhat cleaner > > "import module" syntax. > > > > While there's nothing wrong with such imports, I'm not sure I like > > the thought of the language encouraging them any more than > > necessary. > > It would only work on functions in scope. x.len() would only work if > len(x) would work. In other words, you propose you add yet another check for each function call. But what many people has to say about Python is that it's "slow". There should be lookout for how to make it faster, not yet slower. [] > > The bigger problem I find is reading other code that uses UFCS and > not realising that a "method" is not actually a method of the class, > but requires importing a module. That can cause confusion when > trying to use it in your own code. Indeed, this UFCS idea adds inefficiency and confusion, but doesn't appear to solve any reasonable problem or add any firm benefit. -- Best regards, Paul mailto:pmiscml@gmail.com
[toc] | [prev] | [next] | [standalone]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2014-06-08 11:00 -0600 |
| Message-ID | <mailman.10884.1402246882.18130.python-list@python.org> |
| In reply to | #72957 |
On Sun, Jun 8, 2014 at 2:15 AM, jongiddy <jongiddy@gmail.com> wrote: > One problem with your untested code, the superclasses would need to be checked before using UFCS, so the structure is: > > try: > return super().__getattr__(attr) > except AttributeError: > # resolve using UFCS And then if UFCS finds nothing, make sure the AttributeError gets reraised.
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | comp.lang.python
csiph-web