"result type only depends on operand types"...?
Back on March 10 in the thread on PEP 285 Guido wrote: """ This is a very general rule that I like a lot: that the type of a result should only depend on the type of the arguments, not on their values. I expect that this rule will make reasoning about programs (as in PsyCo or PyChecker) easier to do. I recently caved in and allowed an exception: 2**2 returns an int, but 2**-2 returns a float. This was a case of "practicality beats purity". I don't see True-1 in the same league though. """ And yet...:
type(2**10) <type 'int'> type(2**100) <type 'long'>
...doesn't this apply to many operators on ints in 2.2? Yet _another_ (set of) exception(s) with practicality beating purity? Perhaps, but if a "very general rule" has so many exceptions in frequent and fundamental cases, is it a rule at all...? Alex
On Sun, Mar 31, 2002, Alex Martelli wrote:
Back on March 10 in the thread on PEP 285 Guido wrote:
""" This is a very general rule that I like a lot: that the type of a result should only depend on the type of the arguments, not on their values. I expect that this rule will make reasoning about programs (as in PsyCo or PyChecker) easier to do. """
And yet...:
type(2**10) <type 'int'> type(2**100) <type 'long'>
...doesn't this apply to many operators on ints in 2.2? Yet _another_ (set of) exception(s) with practicality beating purity? Perhaps, but if a "very general rule" has so many exceptions in frequent and fundamental cases, is it a rule at all...?
Channeling Guido: this isn't an "exception", this is a phased step in the progress toward unifying ints and longs. Eventually, the distinction will go away except for explicitly declared platform ints, and an overflow error on a platform int will once again raise an exception rather than transforming automatically. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ Why is this newsgroup different from all other newsgroups?
On Saturday 31 March 2002 9:45, Aahz wrote: """ The problem is that in some cases the __init__ for a class needs to dispatch based on the type of the operand. For example, int() takes both numbers and strings. What should we recommend as "standard practice" for this issue if isinstance() is disrecommended? """ What I recommend, FWIW, until and unless PEP 246 eventuates and makes the world wonderful: # if there's a specifically relevant special-method, such as __int__ is for # int(), you can of course first try getting thearg.__int__ -- if that # fails, or if nothing that relevant applies, you can then proceed with # something along the lines of: try: thearg+'' except TypeError: pass else: "do the stringlike case here" try: thearg+0 except TypeError: pass else: "do the numberlike case here" Why would you want your function to break if called with an instance of UserString, say, or a user-defined number type? Smooth polymorphism is high on the list of Python's strong points -- why break it, when you can preserve this excellent quality? Alex
On Saturday 31 March 2002 9:45, Andrew Koenig wrote: ... """ Liskov substitutibility would seem to suggest deriving int from bool, not the other way around. That is: If I have a program that uses bool values, I can change it so that it uses int values without affecting the behavior of the program. The reverse is not true. I wonder if this is the circle-ellipse problem over again? """ I think not, because circle/ellipse, square/rectangle and bag-of-bananas/ bag-of-fruit are problems only under mutability. If an object is immutable (either because of general language rules -- a FP language, say -- or because of specific considerations, such as a const reference in C++) you CAN pass a reference to an immutable circle wherever a reference to an immutable ellipse is expected (etc) and keep Liskov happy. And Python numbers _are_ immutable... so I don't think the problem is related to the well-known category you mention. Python's int and the proposed bool can't satisfy Liskov anyway, either way, due to str and/or repr. If I "know" that x is one of the proposed bool objects, I could "assert str(x).istitle()" (or repr(x).istitle(), depending on which function is meant to return 'True' and 'False') -- if x is an int, that fails. If I know that x is an int, I can similarly asserts tr(x).isdigit() -- but if x is a bool, that fails. [Some would say this is too strict a reading of Liskov for practical use... do you think it is?] Apart from str and repr issues, I cannot find any assertion I can make under the proposed PEP, that will work for any x that is an int, but breaks if x is a bool. So (net of str/repr issue), any instance of bool IS-A int, so inheriting bool from int seems OK (net of str/repr issues). OTOH, if I know that x is one of the proposed bool values, I can assert 0<=x<2, and that will fail in many cases if x is an int instead. So, inheriting int from bool seems unwarranted. Or, am I missing something? Alex
What I recommend, FWIW, until and unless PEP 246 eventuates and makes the world wonderful:
# if there's a specifically relevant special-method, such as __int__ is for # int(), you can of course first try getting thearg.__int__ -- if that # fails, or if nothing that relevant applies, you can then proceed with # something along the lines of:
try: thearg+'' except TypeError: pass else: "do the stringlike case here"
try: thearg+0 except TypeError: pass else: "do the numberlike case here"
I'm vaguely unhappy with catching exceptions here -- there might be side effects, and they may mask bugs in user-defined classes that try to implement __add__ (especially since TypeError is a common symptom of a bug).
Why would you want your function to break if called with an instance of UserString, say, or a user-defined number type? Smooth polymorphism is high on the list of Python's strong points -- why break it, when you can preserve this excellent quality?
I'm for this; but it's hard to pick the right test in many cases. Many types define both __str__ and __int__ -- but either may lose information (in the case of a float, these *both* lose information!). This leaves me in the uncomfortable position that I don't know what to recommend. :-( --Guido van Rossum (home page: http://www.python.org/~guido/)
On Sun, Mar 31, 2002, Guido van Rossum wrote:
Alex Martelli:
Why would you want your function to break if called with an instance of UserString, say, or a user-defined number type? Smooth polymorphism is high on the list of Python's strong points -- why break it, when you can preserve this excellent quality?
I'm for this; but it's hard to pick the right test in many cases. Many types define both __str__ and __int__ -- but either may lose information (in the case of a float, these *both* lose information!).
Yup, this is precisely what I'm concerned with. My use case for any discussion of this subject is my BCD project (which I haven't worked on in several months, but never mind). I need to be able to accept floats, ints/longs, and string representations of numbers, all at the maximum possible precision for each type. I suppose I could create helper functions called e.g. BCDfromString() and force people to do explicit conversions, but that seems messy to me -- and it contravenes the way int()/str()/float() work. (Alex, IIRC, the last time we had this discussion you agreed that I didn't have any choice.) Note that these days with new-style classes the situation is actually better: inherit from the built-in types, and isinstance() works correctly. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ Why is this newsgroup different from all other newsgroups?
On Sunday 31 March 2002 23:21, Aahz wrote: ...
in several months, but never mind). I need to be able to accept floats, ints/longs, and string representations of numbers, all at the maximum possible precision for each type. I suppose I could create helper ... (Alex, IIRC, the last time we had this discussion you agreed that I didn't have any choice.)
The "maximum possible precision" constraint may leave you no choice (within current Python, bereft of PEP 246) for e.g. longs and floats, but I still don't see why it's better to reject UserString than to test with a try/except on x+''.
Note that these days with new-style classes the situation is actually better: inherit from the built-in types, and isinstance() works correctly.
To me, this means you're basically forced to use inheritance (which is mostly meant as inheritance of implementation) for _flagging_ purposes. I find it detestable, and the inability of multiply inheriting from several builtin types still leaves me in a quandary when I want to flag my type's compatibility with more than one of them -- I have to choose by guessing which of them I'm more liable to be typechecked against... Unicode doesn't inherit from str, yet by the 'flagging' criterion it should, for example (or dearly hope every deuced isinstance-happy function in every deuced library remembers in its deuced typechecks to add one for Unicode next to the one for str). gmpy.mpf instances don't subclass float and it would be (IMHO) an utterly silly thing for them to do so -- yet they WOULD be usable if, instead of by isinstance, they were tested by their abilities, in many cases. And what builtin are you subclassing your BCD class from to make its instances usable in other frameworks full of those isinstance checks...? There is no perfect solution, without PEP 246, but it seems to me that (except where peculiar specific conditions such as the need to lose no precision force the issue) isinstance tends to be more imperfect than others. Incidentally, to clarify: _with_ PEP 246, the need to lose no precision at all might _still_ give problems; e.g., if you asked an mpf to conform to float, you might indeed be losing significance -- in this case, somebody (you as the BCD maintainer, I as the gmpy maintainer, OR, crucially, any third party non-invasively to both packages) would have to code a specific adapter. The PEP246 advantage would be to GIVE said 3rd party (or either of us) the right peg on which to hang such a specific adapter -- it still wouldn't magically write one for us:-). Alex
On Sunday 31 March 2002 23:13, Guido van Rossum wrote: ...
try: thearg+0 except TypeError: pass else: "do the numberlike case here"
I'm vaguely unhappy with catching exceptions here -- there might be side effects, and they may mask bugs in user-defined classes that try to implement __add__ (especially since TypeError is a common symptom of a bug).
Good point, but... if a user-defined class raises TypeError when I try to add 0 to an instance thereof, then I _can't_ use said instance as a number... whether because of a bug in said class, or because said class is not intended to be number-like, I *can't* use it, so I won't. I will then similarly discover I can't use it as a string either, and end up by raising TypeError because by hypothesis I don't know what else to do. This doesn't seem so much of a problem to me to warrant rejecting perfectly good numberlike and stringlike classes' instances, by isinstance'ing away. If a user-defined class is coded to have side effects on __add__, I think any attempt to use instances of that class is doomed to produce weird effects. By that token, the user could subclass int (so isinstance passes) and still do weird things in any overridden method, so I think this is a case of "against stupidity, the god themselves struggle in vain" and would not lose sleep over it.
Why would you want your function to break if called with an instance of UserString, say, or a user-defined number type? Smooth polymorphism is high on the list of Python's strong points -- why break it, when you can preserve this excellent quality?
I'm for this; but it's hard to pick the right test in many cases.
Oh yes, definitely.
Many types define both __str__ and __int__ -- but either may lose information (in the case of a float, these *both* lose information!).
Every subclass of object has __str__, so having it supplies no information (of any practical usefulness) about the class. __int__ may well lose information -- that's how it's defined, after all -- but having it might be taken as one possible test of numberhood
This leaves me in the uncomfortable position that I don't know what to recommend. :-(
In your shoes, I'd recommend PEP 246 -- since, if I were in your shoes, I'd have approved it long ago. (We don't need to have interfaces as a distinct concept from types/classes to approve PEP 246 -- any types can serve as 'protocols' to make PEP 246 a reality). It would offer a very good solution to problems in this category, in my opinion. Alex
Good point, but... if a user-defined class raises TypeError when I try to add 0 to an instance thereof, then I _can't_ use said instance as a number... whether because of a bug in said class, or because said class is not intended to be number-like, I *can't* use it, so I won't. I will then similarly discover I can't use it as a string either, and end up by raising TypeError because by hypothesis I don't know what else to do.
But hiding the true source of the error. If there was a bug in the user code, I'd like to hear about *that*, not about your inability to deal with it. Otherwise, why don't you just use a bare "except:" clause <sarcastic wink>?
This doesn't seem so much of a problem to me to warrant rejecting perfectly good numberlike and stringlike classes' instances, by isinstance'ing away.
Agreed, and I think the solution ought to be sought in asking a question involving hasattr().
__int__ may well lose information -- that's how it's defined, after all -- but having it might be taken as one possible test of numberhood
Unfortunately, non-numeric types are known to implement __int__ for some obscure purpose.
This leaves me in the uncomfortable position that I don't know what to recommend. :-(
In your shoes, I'd recommend PEP 246 -- since, if I were in your shoes, I'd have approved it long ago. (We don't need to have interfaces as a distinct concept from types/classes to approve PEP 246 -- any types can serve as 'protocols' to make PEP 246 a reality). It would offer a very good solution to problems in this category, in my opinion.
Maybe you can summarize it again using a different vocabulary? I find that PEP very hard to read, but I recall liking your informal explanation of it. Unfortunately I don't recall enough of that explanation to go ahead and approve the PEP (I have no idea what effect that would have). --Guido van Rossum (home page: http://www.python.org/~guido/)
From: Guido van Rossum <guido@python.org>
This leaves me in the uncomfortable position that I don't know what to recommend. :-(
In your shoes, I'd recommend PEP 246 -- since, if I were in your shoes, I'd have approved it long ago. (We don't need to have interfaces as a distinct concept from types/classes to approve PEP 246 -- any types can serve as 'protocols' to make PEP 246 a reality). It would offer a very good solution to problems in this category, in my opinion.
Maybe you can summarize it again using a different vocabulary? I find that PEP very hard to read, but I recall liking your informal explanation of it. Unfortunately I don't recall enough of that explanation to go ahead and approve the PEP (I have no idea what effect that would have).
It's a tool, IMHO what is missing is a common dictionary of protocols and idioms, Alex says we don't need that, just let people have the tool and be inspired by other frameworks. E.g. how should one spell the example test: adapt(thearg,str) # but then what about unicode and should str have # an __adapt__ ? or adapt(thearg,StringLike) but then should every one define his StringLike, or should the std lib define one, should StringLike __conform__ use the +'' trick or not. Further the PEP suggests that a global registry for 3rd part adaptations would be useful (and give more degree-of-freedom to cope with the above issues) but it does not specify it, it specify just a global __import__-like hook for adaptation, but __import__ problems suggest that is not a valid solution... regards, Samuele Pedroni.
----- Original Message ----- From: "Samuele Pedroni" <pedroni@inf.ethz.ch>
Maybe you can summarize it again using a different vocabulary? I find that PEP very hard to read, but I recall liking your informal explanation of it. Unfortunately I don't recall enough of that explanation to go ahead and approve the PEP (I have no idea what effect that would have).
It's a tool, IMHO what is missing is a common dictionary of protocols and idioms,
Agreed, establishing some useful protocols would really help, especially if these protocols correspond to objects provided by the core language and standard library. Warning: we might feel compelled to try to define what constitutes a Sequence, Number, or Mapping (not a bad idea anyway, IMO).
Alex says we don't need that, just let people have the tool and be inspired by other frameworks.
I don't think we'd need to try to be exhaustive, but IMO establishing some examples of good practice is important. It's also important to define the protocols that everyone will define over and over again if we don't do it (ahem: Sequence, Number, Mapping...)
E.g. how should one spell the example test:
adapt(thearg,str) # but then what about unicode and should str have # an __adapt__ ?
or
adapt(thearg,StringLike)
but then should every one define his StringLike, or should the std lib define one, should StringLike __conform__ use the +'' trick or not.
Further the PEP suggests that a global registry for 3rd part adaptations would be useful (and give more degree-of-freedom to cope with the above issues) but it does not specify it, it specify just a global __import__-like hook for adaptation, but __import__ problems suggest that is not a valid solution...
[Guido van Rossum]
This doesn't seem so much of a problem to me to warrant rejecting perfectly good numberlike and stringlike classes' instances, by isinstance'ing away.
Agreed, and I think the solution ought to be sought in asking a question involving hasattr().
Having recently removed most calls to hasattr() from PyCrust's introspection routines, I'd be curious to get more of your opinion on this issue. Specifically, I removed calls to hasattr() because xmlrpclib always returns true for hasattr(), rendering it effectively meaningless.
import xmlrpclib p = xmlrpclib.ServerProxy("http://betty.userland.com", verbose=1) hasattr(p, 'bogus') 1 p.bogus [big error message snipped]
I discussed this with Mr. Lundh and ultimately worked around this by removing calls to hasattr(), but I'm still left feeling a bit uncomfortable by this whole situation. Perhaps I was being naive, but I thought I could rely on hasattr(). According to Mr. Lundh: "Having an attribute doesn't mean that you carry out any possible operation on this attribute. In this case, the server proxy will return a method proxy for any possible attribute (hasattr and getattr always succeeds). You won't get an error until you attempt to *call* the method proxy..." This bit of advice seems to fall toward the opposite end of the spectrum from the advice to look to hasattr() for a solution. So I figured I would open this can of worms. I hope this isn't too off-topic. --- Patrick K. O'Brien Orbtech
[Guido van Rossum]
This doesn't seem so much of a problem to me to warrant rejecting perfectly good numberlike and stringlike classes' instances, by isinstance'ing away.
Agreed, and I think the solution ought to be sought in asking a question involving hasattr().
Having recently removed most calls to hasattr() from PyCrust's introspection routines, I'd be curious to get more of your opinion on this issue. Specifically, I removed calls to hasattr() because xmlrpclib always returns true for hasattr(), rendering it effectively meaningless.
Hm. I didn't know xmlrpc did that, and I think it's pretty unique in this respect. It looks like a hack, and one that can only work as long as attributes on xmlrpc instances are always supposed to be methods. Why didn't you choose to special-case xmlrpc? It appears that introspecting an xmlrpc instance is hopeless anyway... (Depending on what you need the introspection for, of course. You haven't told us the whole story.) It's true, you can't trust hasattr() 100%. In fact, there's no way to determine with 100% accuracy whether an object has a particular attribute or not. But that doesn't mean you can't make an educated guess. --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido wrote: [xmlrpclib creates attributes on demand]
Hm. I didn't know xmlrpc did that, and I think it's pretty unique in this respect.
I've seen other RPC code do this. Some soaplib? objects used to infinitely recurse, if you repred, them due to a subtle __getattr__ bug.
It looks like a hack, and one that can only work as long as attributes on xmlrpc instances are always supposed to be methods.
I imagine that we are talking about xmlrpclib.Server. This class creates callable objects, representing remote method calls, when attributes are requested. This allows for a pretty natural method call syntax e.g. server = xmlrpclib.Server('http://...') server.do_something_remote() server.do_something_else() XML-RPC does not have an attribute concept.
Why didn't you choose to special-case xmlrpc? It appears that introspecting an xmlrpc instance is hopeless anyway... (Depending on what you need the introspection for, of course. You haven't told us the whole story.)
It's not necessarily hopeless but getting a list of available methods (and therefore attribute that should be offered) costs a round-trip and isn't supported by all XML-RPC servers (like Python's SimpleXMLRPCServer, for example). Cheers, Brian
Channeling Guido: this isn't an "exception", this is a phased step in the progress toward unifying ints and longs.
You say it so much better than I could. :-)
Eventually, the distinction will go away except for explicitly declared platform ints, and an overflow error on a platform int will once again raise an exception rather than transforming automatically.
Um, what's a platform int? Unless you're talking about a NumPy feature? --Guido van Rossum (home page: http://www.python.org/~guido/)
On Sun, Mar 31, 2002, Guido van Rossum wrote:
Eventually, the distinction will go away except for explicitly declared platform ints, and an overflow error on a platform int will once again raise an exception rather than transforming automatically.
Um, what's a platform int? Unless you're talking about a NumPy feature?
Well, I'm assuming (perhaps falsely) that there will be some way of saying, "give me a fixed-width register integer". But I'm not going to worry about it; if that's not part of the current plan and people whine about it, they can write an extension and contribute it to the core after it proves itself. (Possibly make it part of the os package as a sub-module if/when it gets integrated.) If NumPy ever gets integrated into the core, that would be the appropriate place to put it. (I don't use NumPy; it might actually do that already.) (Note that I didn't actually re-read PEP 237 before channeling you. ;-) -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ Why is this newsgroup different from all other newsgroups?
Um, what's a platform int? Unless you're talking about a NumPy feature?
Well, I'm assuming (perhaps falsely) that there will be some way of saying, "give me a fixed-width register integer".
Not in Python source code. I don't see why you'd want that. Of course, C code has this and has always had this, e.g. the "i" format code in PyArg_ParseTuple().
But I'm not going to worry about it; if that's not part of the current plan and people whine about it, they can write an extension and contribute it to the core after it proves itself. (Possibly make it part of the os package as a sub-module if/when it gets integrated.)
A wise man once said: worrying is interest paid on problems not yet due.
If NumPy ever gets integrated into the core, that would be the appropriate place to put it. (I don't use NumPy; it might actually do that already.)
I think NumPy has a way to create arrays whose elements are variously-sized machine ints and floats. I don't know that it has corresponding scalar types -- I thing these all get mapped to the standard Python types. --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido]
This is a very general rule that I like a lot: that the type of a result should only depend on the type of the arguments, not on their values.
[Alex Martelli]
And yet...:
type(2**10) <type 'int'> type(2**100) <type 'long'>
...doesn't this apply to many operators on ints in 2.2?
Yes indeed.
Yet _another_ (set of) exception(s) with practicality beating purity?
In this case, you're looking at a transitional stage in a *change* to Python's type system, eventually eliminating the int/long distinction. 2.2 implemented PEP 237's Phase A; there's more to come, spread out over years: http://python.sourceforge.net/peps/pep-0237.html
Perhaps, but if a "very general rule" has so many exceptions in frequent and fundamental cases, is it a rule at all...?
Maybe a rule so general is better thought of as a guiding principle. Like "one man, one vote", we eventually made exceptions of inclusion and exclusion for women, felons, and people with a lot of money <wink>. the-laws-of-gravity-don't-apply-ly y'rs - tim
Back on March 10 in the thread on PEP 285 Guido wrote:
""" This is a very general rule that I like a lot: that the type of a result should only depend on the type of the arguments, not on their values. I expect that this rule will make reasoning about programs (as in PsyCo or PyChecker) easier to do.
I recently caved in and allowed an exception: 2**2 returns an int, but 2**-2 returns a float. This was a case of "practicality beats purity". I don't see True-1 in the same league though. """
And yet...:
type(2**10) <type 'int'> type(2**100) <type 'long'>
...doesn't this apply to many operators on ints in 2.2? Yet _another_ (set of) exception(s) with practicality beating purity? Perhaps, but if a "very general rule" has so many exceptions in frequent and fundamental cases, is it a rule at all...?
Oh, go away. That's part of PEP 237. --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (8)
-
Aahz
-
Alex Martelli
-
Brian Quinlan
-
David Abrahams
-
Guido van Rossum
-
Patrick K. O'Brien
-
Samuele Pedroni
-
Tim Peters