msg93171 - (view) |
Author: Milko Krachounov (milko.krachounov) |
Date: 2009-09-27 14:23 |
hasattr(x, '__call__') has been suggested as a replacement for
callable(x) in the documentation and in the warning when running
python2.6 with -3. It is also what 2to3 replaces it with. However, the
two are not equivalent.
1. I can add a __call__ attribute to my object with "obj.__call__ =
lambda x:x". That will not make the object callable, callable() on
Python 2.6 returns False, but hasattr(obj, '__call__') returns True.
2. I can implement a __getattr__ that returns something for every
possible attribute name. Again, this doesn't make the object callable,
nor does callable(obj) in Python 2.6 return True.
I think a closer replacement for "callable(x)" would be "'__call__' in
vars(type(x))".
|
msg93175 - (view) |
Author: Ezio Melotti (ezio.melotti) * |
Date: 2009-09-27 15:59 |
In the PEP3100 [1] there's written, in the "to be removed" list:
"callable(): just use hasattr(x, '__call__') (?) [2] [done]"
and the note refers to a pdf [2] that instead says:
"callable(): just call it, already"
If callable() was removed to encourage the EAFP [3] style, replacing "if
callable(x):" with "if hasattr(x, '__call__'):" is just a less-accurate
and less-readable way of doing the same (wrong) thing [4] (also note the
(?) next to the hasattr() expression).
This might be OK for automated tools like 2to3, but not for the doc and
the warning. Humans should probably replace the expression with a
try/except instead of using hasattr().
Even for 2to3 there are better and more accurate solutions though, like
the one proposed by the OP (not really readable) and isinstance(x,
collections.Callable).
(Note: what the OP said is correct for new-style classes, for old-style
classes callable() and hasattr() have the same result).
[1]: http://www.python.org/dev/peps/pep-3100/
[2]: Python Regrets:
http://www.python.org/doc/essays/ppt/regrets/PythonRegrets.pdf
[3]: http://docs.python.org/glossary.html#term-eafp
[4]: http://docs.python.org/glossary.html#term-lbyl
|
msg93178 - (view) |
Author: Milko Krachounov (milko.krachounov) |
Date: 2009-09-27 16:08 |
My suggestion is not only unreadable, but wrong. It's even less accurate
than hasattr(x, '__call__'), as it doesn't look in all the classes in
the MRO. Using isinstance(x, collections.Callable) should probably be
the correct replacement for 2to3 and situation where checking this is
really needed, as it does look for __call__ in the whole MRO.
|
msg93477 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-10-03 05:12 |
hasattr(type(x), "__call__") is technically a more valid replacement due
to the usual matter of metaclass confusion.
(Although putting special methods on instance objects is a recipe for
trouble in more ways than just this one).
|
msg93478 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-10-03 05:13 |
Although we should seriously consider properly exposing special method
lookup at the Python level...
|
msg93479 - (view) |
Author: Ezio Melotti (ezio.melotti) * |
Date: 2009-10-03 06:13 |
Benjamin already replaced hasattr(x, "__call__") with hasattr(type(x),
"__call__") in the Python 3.0 "What's New" in r75090 and r75094, but
this still doesn't match completely the behavior of callable():
>>> class Foo(object): pass
...
>>> foo = Foo()
>>> callable(foo)
False
>>> hasattr(type(foo), '__call__')
True
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Foo' object is not callable
There are also other places where hasattr(x, "__call__") is still
suggested/used (e.g. PEP3100).
|
msg93488 - (view) |
Author: Andreas Stührk (Trundle) * |
Date: 2009-10-03 10:25 |
As every type is an instance of `type`, every type also has a
`__call__` attribute which means ``hasattr(type(x), '__call__')`` is
always true. `callable()` checks whether `tp_call` is set on the type,
which cannot be done in Python directly.
|
msg94364 - (view) |
Author: Georg Brandl (georg.brandl) * |
Date: 2009-10-22 16:26 |
2to3 still uses hasattr(x, 'callable').
|
msg94373 - (view) |
Author: Benjamin Peterson (benjamin.peterson) * |
Date: 2009-10-23 00:02 |
2009/10/22 Georg Brandl <report@bugs.python.org>:
>
> Georg Brandl <georg@python.org> added the comment:
>
> 2to3 still uses hasattr(x, 'callable')
Do you have strong opinions about this? IMO, hasattr(x, '__call__') is
compatible enough.
|
msg94376 - (view) |
Author: Georg Brandl (georg.brandl) * |
Date: 2009-10-23 07:37 |
Not really, that was the last thing to get this issue closed.
|
msg95400 - (view) |
Author: Jean-Paul Calderone (exarkun) * |
Date: 2009-11-17 20:56 |
How about bringing callable back since there is no obvious replacement
for it? It's valuable as a LYBL check in circumstances where an object
submitted far away from the place where it's invoked. Such uses can't
easily be replaced with direct calls to follow the recommended EAFP style.
|
msg95403 - (view) |
Author: Antoine Pitrou (pitrou) * |
Date: 2009-11-17 21:15 |
I also think isinstance(x, collections.Callable) is the correct
replacement. Even though it might give a different answer on weird
corner cases, it is semantically what you are looking for.
(too bad it has a stupid module placement)
|
msg95416 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-11-18 10:16 |
Antoine Pitrou wrote:
> Antoine Pitrou <pitrou@free.fr> added the comment:
>
> I also think isinstance(x, collections.Callable) is the correct
> replacement. Even though it might give a different answer on weird
> corner cases, it is semantically what you are looking for.
> (too bad it has a stupid module placement)
Yes, I was very surprised not to find it as abc.Callable...
Regards,
Nick.
|
msg96871 - (view) |
Author: Joe Amenta (joe.amenta) |
Date: 2009-12-24 19:52 |
One such weird corner case:
from collections import Callable
class Spam(object):
def __call__(self):
return self
can_of_spam = Spam()
print callable(can_of_spam) == isinstance(can_of_spam, Callable) # True
del Spam.__call__
can_of_spam.__call__ = lambda can: 'spam'
print callable(can_of_spam) == isinstance(can_of_spam, Callable) # False
Regardless, attached a patch for the new proposed semantics
|
msg96877 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-12-25 07:11 |
That isn't a semantic change, it is exactly the same semantics as
callable() in 2.x:
>>> class Spam(object):
... def __call__(self): pass
...
>>> callable(Spam())
True
>>> del Spam.__call__
>>> callable(Spam())
False
|
msg96878 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-12-25 07:13 |
Just confirming that 2.x also correctly ignores instance attributes (as
it should, since it looks at the tp_call slot):
>>> odd = Spam()
>>> odd.__call__ = lambda: 'very odd'
>>> callable(odd)
False
|
msg96879 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-12-25 07:16 |
The patch however does not look correct - the import should be added as
a global import at the beginning of affected files rather than inline
with each callable check.
|
msg96880 - (view) |
Author: Andreas Stührk (Trundle) * |
Date: 2009-12-25 11:28 |
What Joe Amenta stumbled across is that ABCMeta caches its subclass
checks. If you call ``isinstance(spam, Callable)`` and after that delete
`type(spam).__call__`, every other call of ``isinstance(spam, Callable)``
will still return True.
|
msg96882 - (view) |
Author: Alyssa Coghlan (ncoghlan) * |
Date: 2009-12-25 13:25 |
Ah yes, I misread the example. Agreed that that is a change in behaviour
then - it is possible to clear the caches if absolutely necessary, but
doing so isn't particularly portable.
|
msg96883 - (view) |
Author: Joe Amenta (joe.amenta) |
Date: 2009-12-25 17:32 |
I believe that this patch works like you described...
Attached a patch with more test cases to show this.
(the [1:] parts are to make the test cases readable; they will still pass
if all the leading newlines are removed from the triple-quoted strings and
all [1:] are removed :-)
|
msg96885 - (view) |
Author: Joe Amenta (joe.amenta) |
Date: 2009-12-25 17:47 |
To elaborate on my last comment:
- touch_import looks for the required import binding in any scope, and it
will add a global import if not found, otherwise it leaves it alone
- the import added does not have a newline prefix, so if the newlines were
left in, (without [1:]) the tests would fail.
However, I found that the test cases are easier to read if they all
start on the same indentation level, so adding a newline for the reader
but telling the parser to ignore it makes readable, correct test cases.
|
msg96967 - (view) |
Author: Benjamin Peterson (benjamin.peterson) * |
Date: 2009-12-28 20:56 |
Fixed in r77093.
|
|
Date |
User |
Action |
Args |
2022-04-11 14:56:53 | admin | set | github: 51255 |
2009-12-28 20:56:27 | benjamin.peterson | set | status: open -> closed resolution: accepted messages:
+ msg96967
|
2009-12-25 17:47:39 | joe.amenta | set | messages:
+ msg96885 |
2009-12-25 17:32:54 | joe.amenta | set | files:
+ new_callable_semantics_moretests.patch
messages:
+ msg96883 |
2009-12-25 13:25:23 | ncoghlan | set | messages:
+ msg96882 |
2009-12-25 11:28:13 | Trundle | set | messages:
+ msg96880 |
2009-12-25 07:16:15 | ncoghlan | set | messages:
+ msg96879 |
2009-12-25 07:13:49 | ncoghlan | set | messages:
+ msg96878 |
2009-12-25 07:11:53 | ncoghlan | set | messages:
+ msg96877 |
2009-12-24 19:52:21 | joe.amenta | set | files:
+ new_callable_semantics.patch
nosy:
+ joe.amenta messages:
+ msg96871
keywords:
+ patch |
2009-11-18 10:16:33 | ncoghlan | set | messages:
+ msg95416 title: The replacement suggested for callable(x) in py3k is not equivalent -> The replacement suggested for callable(x) in py3k is not equivalent |
2009-11-17 21:15:57 | pitrou | set | nosy:
+ pitrou messages:
+ msg95403
|
2009-11-17 20:56:55 | exarkun | set | nosy:
+ exarkun messages:
+ msg95400
|
2009-10-23 07:37:07 | georg.brandl | set | messages:
+ msg94376 |
2009-10-23 00:02:48 | benjamin.peterson | set | messages:
+ msg94373 |
2009-10-22 16:26:18 | georg.brandl | set | assignee: georg.brandl -> benjamin.peterson messages:
+ msg94364 |
2009-10-03 10:25:30 | Trundle | set | nosy:
+ Trundle messages:
+ msg93488
|
2009-10-03 06:13:27 | ezio.melotti | set | nosy:
+ benjamin.peterson messages:
+ msg93479
|
2009-10-03 05:13:14 | ncoghlan | set | messages:
+ msg93478 |
2009-10-03 05:12:32 | ncoghlan | set | nosy:
+ ncoghlan messages:
+ msg93477
|
2009-09-27 16:08:30 | milko.krachounov | set | messages:
+ msg93178 |
2009-09-27 15:59:26 | ezio.melotti | set | priority: normal nosy:
+ ezio.melotti messages:
+ msg93175
|
2009-09-27 14:23:53 | milko.krachounov | create | |