I have spent some time searching for a bug in my code, it was due to different work of "is" with () and []: >>> () is () True >>> [] is [] False (Python 2.7.2+ (default, Oct 4 2011, 20:03:08) [GCC 4.6.1] ) Is this what it should be or maybe yielding unified result is better? D.
On Sat, Apr 21, 2012 at 5:10 AM, dmitrey wrote:
> I have spent some time searching for a bug in my code, it was due to
> different work of "is" with () and []:
>>>> () is ()
> True
>>>> [] is []
> FalseOkay, let's take a step back. What do you expect "is" to be doing? It
doesn't check for equality.
ChrisA
On 4/20/12 8:10 PM, dmitrey wrote: > I have spent some time searching for a bug in my code, it was due to > different work of "is" with () and []: >>>> () is () > TrueThe empty tuple is unique, immutable, and common so the Python runtime optimizes this by reusing the same object, much like small integers are interned. This is not something your code should rely on, though.>>>> [] is [] > FalseEmpty lists are mutable and may be modified differently, so they cannot be the same object. Every time you use an empty list literal, it must create a new object.> (Python 2.7.2+ (default, Oct 4 2011, 20:03:08) > [GCC 4.6.1] ) > > Is this what it should be or maybe yielding unified result is better?If your code is relying on the difference, or a lack of one, it's buggy.
On 20/04/2012 20:10, dmitrey wrote: > I have spent some time searching for a bug in my code, it was due to > different work of "is" with () and []: >>>> () is () > True >>>> [] is [] > False > > (Python 2.7.2+ (default, Oct 4 2011, 20:03:08) > [GCC 4.6.1] ) > > Is this what it should be or maybe yielding unified result is better? > D.The reason that "[] is []" evaluates to false is because lists are mutable and each expression "[]" evaluates to a different object. Therefore you can change one without changing the other:>>> a, b = [], [] >>> a.append(None) >>> a[None] >>> b [] On the other hand tuples are immutable, so there's no danger in having each literal () evaluate to the same object - since you can't change a tuple but only replace a reference to a tuple with a reference to another tuple, there's no need to have the two "()"s in something like >>> a, b = (), () evaluate to different objects, since there is no danger that one can affect the value of b by doing stuff to a. I believe it says somewhere in the Python docs that it's undefined and implementation-dependent whether two identical expressions have the same identity when the result of each is immutable, though I can't find the quotation right now. If my recollection is correct then you probably shouldn't rely on something like "() is ()" giving the same answer across platforms.
On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote:
> I believe it says somewhere in the Python docs that it's undefined and
> implementation-dependent whether two identical expressions have the same
> identity when the result of each is immutableI was curious where that might be on my system, and found the disagreement with the ((),) case and up.
(Python 2.7.1, Darwin 11.3.0, GCC 4.2.1)
On 4/20/2012 9:34 PM, wrote: > On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: > >> I believe it says somewhere in the Python docs that it's undefined and >> implementation-dependent whether two identical expressions have the same >> identity when the result of each is immutableBad design. Where "is" is ill-defined, it should raise ValueError. A worse example, one which is very implementation-dependent: http://stackoverflow.com/questions/306313/pyt...>>> a = 256 >>> b = 256 >>> a is bTrue # this is an expected result>>> a = 257 >>> b = 257 >>> a is bFalse Operator "is" should be be an error between immutables unless one is a built-in constant. ("True" and "False" should be made hard constants, like "None". You can't assign to None, but you can assign to True, usually with unwanted results. It's not clear why True and False weren't locked down when None was.) John Nagle
On Mon, Apr 23, 2012 at 5:43 AM, John Nagle wrote:
> Operator "is" should be be an error between immutables
> unless one is a built-in constant. ("True" and "False"
> should be made hard constants, like "None". You can't assign
> to None, but you can assign to True, usually with
> unwanted results. It's not clear why True and False
> weren't locked down when None was.)Only in Python 2. In Python 3:
>>> True=3
SyntaxError: assignment to keyword
ChrisA
On Sunday, April 22, 2012 1:43:36 PM UTC-6, John Nagle wrote:
> On 4/20/2012 9:34 PM, wrote:
> > On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote:
> >
> >> I believe it says somewhere in the Python docs that it's undefined and
> >> implementation-dependent whether two identical expressions have the same
> >> identity when the result of each is immutable
>
> Bad design. Where "is" is ill-defined, it should raise ValueError.
>
> A worse example, one which is very implementation-dependent:
>
> http://stackoverflow.com/questions/306313/pyt...
>
> >>> a = 256
> >>> b = 256
> >>> a is b
> True # this is an expected result
> >>> a = 257
> >>> b = 257
> >>> a is b
> False
>
> Operator "is" should be be an error between immutables
> unless one is a built-in constant. ("True" and "False"
> should be made hard constants, like "None". You can't assign
> to None, but you can assign to True, usually with
> unwanted results. It's not clear why True and False
> weren't locked down when None was.)
>
> John NagleThree points. First, since there's no obvious way of telling whether an arbitrary user-created object is immutable, trying to make "is" fail in that case would be a major change to the language. Given the next point, I expect that a request to change Python to do it would be rejected immediately.
Second: the definition of "is" states that it determines whether two objects are the same object; this has nothing to do with mutability or immutability. It's an implementation detail whether an immutable object is implemented as a singleton. Some common cases (None, True, False and the empty tuple) are specified to be that in the language definition, but it's not specified for the general case.
The id([]) == id([]) thing is a place where cPython's implementation is showing through. It won't work that way in any implementation that uses garbage collection and object compaction. I think Jython does it that way, I'm not sure about either IronPython or PyPy.
Third: True and False are reserved names and cannot be assigned to in the 3.x series. They weren't locked down in the 2.x series when they were introduced because of backward compatibility.
John Roth
On 4/22/2012 3:17 PM, John Roth wrote: > On Sunday, April 22, 2012 1:43:36 PM UTC-6, John Nagle wrote: >> On 4/20/2012 9:34 PM, wrote: >>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >>> >>>> I believe it says somewhere in the Python docs that it's >>>> undefined and implementation-dependent whether two identical >>>> expressions have the same identity when the result of each is >>>> immutable >> >> Bad design. Where "is" is ill-defined, it should raise >> ValueError. >> >> A worse example, one which is very implementation-dependent: >> >> http://stackoverflow.com/questions/306313/pyt... >> >> >> >>> a = 256 >>>>> b = 256 a is b >> True # this is an expected result >>>>> a = 257 b = 257 a is b >> False >> >> Operator "is" should be be an error between immutables unless one >> is a built-in constant. ("True" and "False" should be made hard >> constants, like "None". You can't assign to None, but you can >> assign to True, usually with unwanted results. It's not clear why >> True and False weren't locked down when None was.) >> >> John Nagle > > Three points. First, since there's no obvious way of telling whether > an arbitrary user-created object is immutable, trying to make "is" > fail in that case would be a major change to the language.If a program fails because such a comparison becomes invalid, it was broken anyway. The idea was borrowed from LISP, which has both "eq" (pointer equality) and and "equals" (compared equality). It made somewhat more sense in the early days of LISP, when the underlying representation of everything was well defined.> Second: the definition of "is" states that it determines whether two > objects are the same object; this has nothing to do with mutability > or immutability. > > The id([]) == id([]) thing is a place where cPython's implementation > is showing through. It won't work that way in any implementation that > uses garbage collection and object compaction. I think Jython does it > that way, I'm not sure about either IronPython or PyPy.That represents a flaw in the language design - the unexpected exposure of an implementation dependency.> Third: True and False are reserved names and cannot be assigned to in > the 3.x series. They weren't locked down in the 2.x series when they > were introduced because of backward compatibility.That's one of the standard language designer fuckups. Somebody starts out thinking that 0 and 1 don't have to be distinguished from False and True. When they discover that they do, the backwards compatibility sucks. C still suffers from this. John Nagle
On 4/22/2012 3:43 PM, John Nagle wrote: > On 4/20/2012 9:34 PM, wrote: >> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >> >>> I believe it says somewhere in the Python docs that it's undefined and >>> implementation-dependent whether two identical expressions have the same >>> identity when the result of each is immutable > > Bad design. Where "is" is ill-defined, it should raise ValueError.There is no ambiguity about the meaning of 'is' in Python. It is always well-defined. So the suggestion is a nullity. The equivalent function form in Python is def is(a, b): return id(a) == id(b) It is important here that the expressions corresponding to both a and b are evaluated and the resulting objects bound to names a and b first and that they are not unbound and possibly destroyed until after the return is computed. The inline form of the function call is __tem1__ = a __tem2__ = b result = id(a) == id(b) del __tem1__, __tem2__ Leaving out the temporaries is a (common) mistake, but it changes the meaning. The id(object) function is also well-defined: it is the immutable internal integer id assigned to the object upon creation by the interpreter. The interpreter may use that internal number to associate names with object and to find objects given a name, but how it does do is up to the interpreter. In any case, the return value is always the same for a given object. That is the meaning of 'well-defined'. Python is an object-based language. Its objects have identity, class, and value. Most functions in Python are *value* functions. The result is independent of object id. However, id is an *object* function. It is, in fact, the basic object function. Class and value can change for some objects, but identity never does. Identity is independent of class and value and not predictable from them. I believe you are confusing 'unpredictable from other, uncorrelated object attributes' with 'ill-defined'. If you want 'ill-defined', consider the other comparison operators, which can be redefined in user subclasses. For such comparisons, a op b may not be the same as b op a. The use of 'is' in production code is to be a predictable comparison operator. That way, we can write 'if a is None:' rather than having to write 'if None is a:' to ensure getting the expected and desired result.> >>> a = 256 > >>> b = 256 > >>> a is b > True # this is an expected result'Expected' is in the mind of the designer or beholder.> >>> a = 257 > >>> b = 257 > >>> a is b > FalseThe other use of 'is' and 'id' is to reveal and test implementation details.> Operator "is" should be be an error between immutables > unless one is a built-in constant.The CPython-specific part of the Python test suite has test like the above to test such implementation details. Do you really want to disable that and make it impossible ;-? > "True" and "False" should be made hard constants, like "None". The developers agree, and did exactly this about 3 years ago.> You can't assign to None, but you can assign to True, usually with > unwanted results.Not any more, except in 2.x, for back compatibility. > It's not clear It is completely clear and has been discussed here many times. > why True and False weren't locked down when None was. Are you really unaware that before False and True were added officially, many people routinely wrote in their code: False, True = 0, 1 and that locking the names, when it was done in 3.0, broke such code? And that is was not done earlier to avoid breaking code earlier, in accordance with the general policy to mostly avoid such breakage?
On Sun, Apr 22, 2012 at 9:22 PM, Terry Reedy wrote: > On 4/22/2012 3:43 PM, John Nagle wrote: >> >> On 4/20/2012 9:34 PM, wrote: >>> >>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >>> >>>> I believe it says somewhere in the Python docs that it's undefined and >>>> implementation-dependent whether two identical expressions have the same >>>> identity when the result of each is immutable >> >> >> Bad design. Where "is" is ill-defined, it should raise ValueError. > > > There is no ambiguity about the meaning of 'is' in Python. It is always > well-defined. So the suggestion is a nullity. The equivalent function form > in Python isBollocks it's well defined. We've already agreed that "1 is 1" may or may not return True. Then let's just go to the definition: http://mathworld.wolfram.com/Well-Defined.htm... "An expression is called "well-defined" (or unambiguous) if its definition assigns it a unique interpretation or value. Otherwise, the expression is said to not be well-defined or to be ambiguous. " The same complaint applies to your suggestion that id() is well-defined, and I stopped reading there. -- Devin
On 4/23/2012 12:38 AM, Devin Jeanpierre wrote: > On Sun, Apr 22, 2012 at 9:22 PM, Terry Reedy< wrote: >> On 4/22/2012 3:43 PM, John Nagle wrote: >>> >>> On 4/20/2012 9:34 PM, wrote: >>>> >>>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >>>> >>>>> I believe it says somewhere in the Python docs that it's undefined and >>>>> implementation-dependent whether two identical expressions have the same >>>>> identity when the result of each is immutable >>> >>> >>> Bad design. Where "is" is ill-defined, it should raise ValueError. >> >> >> There is no ambiguity about the meaning of 'is' in Python. It is always >> well-defined. So the suggestion is a nullity. The equivalent function form >> in Python is > > Bollocks it's well defined. We've already agreed that "1 is 1" may or > may not return True.'We' have not so agreed. The function should tell the truth. Let me repeat: 'is' is a function whose two inputs are not eternal mathematical values but rather are computational implementation objects with finite lifetimes. It is wrong to interpret the Python expression '1 is 1' as a mathematical expression. It simply is not such. Trying to do so only leads to confusion, as this thread show. The 'is' function answers a question about the current state of a particular temporal computational process. Its output is well-determined given its actual input. Like other introspection functions, it is useful for testing an implementation.> Then let's just go to the definition: > http://mathworld.wolfram.com/Well-Defined.htm... > > "An expression is called "well-defined" (or unambiguous) if its > definition assigns it a unique interpretation or value.The definition of 'is' does just that.> The same complaint applies to your suggestion that id() is > well-defined, and I stopped reading there.In much or most of mathematics, there is little or no concept of 'identify' separate from 'value'. In Python, for Python objects, there is. This is necessary if Python is to model real life as well as do mathematical computations. If you do not like it, ignore it. Since the existence of objects is time-dependent, the mapping from objects to ids also is. As any one time, the mapping is not only well-defined but bijective, so there is an inverse mapping from ids to objects.
On Mon, Apr 23, 2012 at 3:59 PM, Terry Reedy wrote: >> >> Bollocks it's well defined. We've already agreed that "1 is 1" may or >> may not return True. > > > 'We' have not so agreed. The function should tell the truth. Let me repeat: > 'is' is a function whose two inputs are not eternal mathematical values but > rather are computational implementation objects with finite lifetimes.Er, well, no. I'm willing to concede that I was wrong for being so eager to say "y'all are wrong". I was, I misunderstood the objection you and Steven were making, which is that "is" is well-defined, but object identities are not. However, you appear to be trying to shift the goalposts. Either "1 is 1" is always True, or always False, or sometimes one or the other. If I'm mistaken and it so happens that numeric constants are guaranteed somewhere to always be cached, then replace it with the empty tuple. It is a fact that the result is not always the same, and you're trying to dodge around that. :/> It is wrong to interpret the Python expression '1 is 1' as a mathematical > expression. It simply is not such. Trying to do so only leads to confusion, > as this thread show.Of course it's a mathematical expression. There's a very well researched theory of computation that links Python all the way to abstract purely-functional calculi, allowing us to mathematically model everything. But if you mean, "this isn't like high school arithmetic", sure. It depends on a lot more than these things usually do (you say it depends on time, but in the simple cases above, it depends on how number objects and tuple objects are created.)> The 'is' function answers a question about the current state of a particular > temporal computational process. Its output is well-determined given its > actual input. Like other introspection functions, it is useful for testing > an implementation.Sure, I understand this now. I am a bit regretful, the discussion of whether "is" itself is ambiguous, or the actual values, doesn't really matter. What matters is that sometimes "() is ()" will return True, and sometimes it will return False. It doesn't really matter whether it's "is" that's ambiguous, or the identity of newly made tuples. The point is that something is ambiguous. And maybe that's worth changing? -- Devin
On 4/23/2012 4:37 PM, Devin Jeanpierre wrote: > However, you appear to be trying to shift the goalposts. Either "1 is > 1" is always True, or always False, or sometimes one or the other. If > I'm mistaken and it so happens that numeric constants are guaranteed > somewhere to always be cached, then replace it with the empty tuple. > It is a fact that the result is not always the same,The result is always the current truth of the matter. > and you're trying to dodge around that. :/ In a previous post you chided Stephen for an ad hominem comment. Above you make two. Both are false. I am insisting that Python's 'is' should be judged as what it is, which is a non-mathematical introspection function. And I am hardly dodging around something I have understood and been telling and warning people about on comp.lang.python and thisn list for 15 years. That is a nasty lie.>> It is wrong to interpret the Python expression '1 is 1' as a mathematical >> expression. It simply is not such. Trying to do so only leads to confusion, >> as this thread show. > > Of course it's a mathematical expression.Bollocks. It is a question about a non-mathematical* fact of 'the world', which is a Python computing session. * As I understand the adjective 'mathematical'. Consider the expression 'Bob is Robert'. If it means "The string 'Bob' is the string 'Robert'" then is would be a mathematical expression of string theory and the answer would be False. If it means "The person connected with the name 'Bob' at this time in the currect context is the same person connected with 'Robert'", then it is not a mathematical question but is contingent# on the facts of the world. # Contingency is not ambiguity. Anyway, I think I am done with this thread.
On Tue, Apr 24, 2012 at 12:12 AM, Terry Reedy wrote:
>> and you're trying to dodge around that. :/
>
> In a previous post you chided Stephen for an ad hominem comment. Above you
> make two. Both are false.I accused you of not answering the question or shifting goalposts.
This is an attack against your response, not an attack against you
("ad hominem" literally means "to the person").
Also, I definitely tried to lighten that one up with a classy
emoticon. It's totally not fair that you're scoring points off it.
> Anyway, I think I am done with this thread.
... Good idea.
We clearly understand Python's behavior. I just spent the whole time
bickering about definitions. Ugh. I'm an idiot. Sorry for the trouble.
-- Devin
(P.S. can we all start saying "muppet" next?)
On Sun, 22 Apr 2012 12:43:36 -0700, John Nagle wrote: > On 4/20/2012 9:34 PM, wrote: >> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >> >>> I believe it says somewhere in the Python docs that it's undefined and >>> implementation-dependent whether two identical expressions have the >>> same identity when the result of each is immutable > > Bad design. Where "is" is ill-defined, it should raise ValueError."is" is never ill-defined. "is" always, without exception, returns True if the two operands are the same object, and False if they are not. This is literally the simplest operator in Python. John, you've been using Python for long enough that you should know this. I can only guess that you are trolling, although I can't imagine why.> A worse example, one which is very implementation-dependent: > > http://stackoverflow.com/questions/306313/pyt...unexpectedly-with-integers> > >>> a = 256 > >>> b = 256 > >>> a is b > True # this is an expected resultWhy do you expect that? What part of the language semantics makes you think that the two statements: a = 256 b = 256 must use the one object instead of creating two distinct objects?> >>> a = 257 > >>> b = 257 > >>> a is b > False > > Operator "is" should be be an error between immutables unless one is a > built-in constant.I cannot imagine any rationale for that insane design choice. For starters, it makes it impossible to use custom sentinels when you need to distinguish between None as a valid argument and "argument missing". E.g.: _SENTINEL = object() def func(x, y=_SENTINEL): if y is _SENTINEL: y = something_else() process(x, y)> ("True" and "False" should be made hard constants, > like "None". You can't assign to None, but you can assign to True, > usually with unwanted results.True and False were made keywords over three years ago, in Python 3.0. http://docs.python.org/dev/whatsnew/3.0.html#... > It's not clear why True and False weren't locked down when None was.) It's pretty clear to me. Assignment to None has never been a common thing to do, hence the amount of code broken by making None a keyword in Python 2.4 was insignificant. On the other hand, assignment to True and False was *very* common for code written prior to version 2.3, hence making True and False keywords before version 3 would have broken a lot of otherwise working code for little or no benefit.
On Mon, Apr 23, 2012 at 12:34 AM, Steven D'Aprano wrote: > On Sun, 22 Apr 2012 12:43:36 -0700, John Nagle wrote: > >> On 4/20/2012 9:34 PM, wrote: >>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >>> >>>> I believe it says somewhere in the Python docs that it's undefined and >>>> implementation-dependent whether two identical expressions have the >>>> same identity when the result of each is immutable >> >> Bad design. Where "is" is ill-defined, it should raise ValueError. > > "is" is never ill-defined. "is" always, without exception, returns True > if the two operands are the same object, and False if they are not. This > is literally the simplest operator in Python. > > John, you've been using Python for long enough that you should know this. > I can only guess that you are trolling, although I can't imagine why.Could you refrain from personal attacks? Especially considering that he said nothing unreasonable. It's you that doesn't appear to know this (relatively common? I thought it was universal...) definition of "ill-defined": http://mathworld.wolfram.com/Ill-Defined.html "() is ()" does not have one unique value in all interpretations. It can be either True or False, depending on the Python. (At least, I think this was the consensus). Therefore "is" is ill-defined in this case. -- Devin
On Mon, Apr 23, 2012 at 12:42 AM, Devin Jeanpierre
wrote:
> On Mon, Apr 23, 2012 at 12:34 AM, Steven D'Aprano
> wrote:
>> "is" is never ill-defined. "is" always, without exception, returns True
>> if the two operands are the same object, and False if they are not. This
>> is literally the simplest operator in Python.Oh, I see, is is well-defined but tuple/number creation is
ill-defined. Well, fine. That's reasonable. Just don't call someone a
troll if they mess up the distinction. (I thought it was implicit that
he meant "where a is b" is ill-defined, sorta read too quickly because
I was a bit mad that two people misinterpreted.).
Derp.
-- Devin
On 04/23/2012 12:42 AM, Devin Jeanpierre wrote: > On Mon, Apr 23, 2012 at 12:34 AM, Steven D'Aprano > wrote: >> On Sun, 22 Apr 2012 12:43:36 -0700, John Nagle wrote: >> >>> On 4/20/2012 9:34 PM, wrote: >>>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >>>> >>>>> I believe it says somewhere in the Python docs that it's undefined and >>>>> implementation-dependent whether two identical expressions have the >>>>> same identity when the result of each is immutable >>> Bad design. Where "is" is ill-defined, it should raise ValueError. >> "is" is never ill-defined. "is" always, without exception, returns True >> if the two operands are the same object, and False if they are not. This >> is literally the simplest operator in Python. >> >> John, you've been using Python for long enough that you should know this. >> I can only guess that you are trolling, although I can't imagine why. > Could you refrain from personal attacks? Especially considering that > he said nothing unreasonable. It's you that doesn't appear to know > this (relatively common? I thought it was universal...) definition of > "ill-defined": > > http://mathworld.wolfram.com/Ill-Defined.html > > "() is ()" does not have one unique value in all interpretations. It > can be either True or False, depending on the Python. (At least, I > think this was the consensus). Therefore "is" is ill-defined in this case. > > -- DevinSteven did not say the expression "() is ()" is well-defined. He said that "is" is well defined, which it is. The part which is not is whether the runtime will decide to reuse the same object when told to create a new instance of an immutable value. By the time "is" gets invoked, the two operands are either bound to the same object or not, and "is" tells the truth at that point.
On 4/22/2012 9:34 PM, Steven D'Aprano wrote:
> On Sun, 22 Apr 2012 12:43:36 -0700, John Nagle wrote:
>
>> On 4/20/2012 9:34 PM, wrote:
>>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote:
>>>
>>>> I believe it says somewhere in the Python docs that it's undefined and
>>>> implementation-dependent whether two identical expressions have the
>>>> same identity when the result of each is immutable
>>
>> Bad design. Where "is" is ill-defined, it should raise ValueError.
>
> "is" is never ill-defined. "is" always, without exception, returns True
> if the two operands are the same object, and False if they are not. This
> is literally the simplest operator in Python.
>
> John, you've been using Python for long enough that you should know this.
> I can only guess that you are trolling, although I can't imagine why.Because the language definition should not be what CPython does.
As PyPy advances, we need to move beyond that.
John Nagle
On 4/23/12 5:04 PM, John Nagle wrote: > On 4/22/2012 9:34 PM, Steven D'Aprano wrote: >> On Sun, 22 Apr 2012 12:43:36 -0700, John Nagle wrote: >> >>> On 4/20/2012 9:34 PM, wrote: >>>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote: >>>> >>>>> I believe it says somewhere in the Python docs that it's undefined and >>>>> implementation-dependent whether two identical expressions have the >>>>> same identity when the result of each is immutable >>> >>> Bad design. Where "is" is ill-defined, it should raise ValueError. >> >> "is" is never ill-defined. "is" always, without exception, returns True >> if the two operands are the same object, and False if they are not. This >> is literally the simplest operator in Python. >> >> John, you've been using Python for long enough that you should know this. >> I can only guess that you are trolling, although I can't imagine why. > > Because the language definition should not be what CPython does.It isn't. That's the point of leaving some things like the interning of certain special objects undefined, to make room for other implementations. You seem to be objecting to that for some bizarre reason.
On Apr 23, 9:34 am, Steven D'Aprano <steve
wrote:
> "is" is never ill-defined. "is" always, without exception, returns True
> if the two operands are the same object, and False if they are not. This
> is literally the simplest operator in Python.Circular definition: In case you did not notice, 'is' and 'are' are
(or is it is?) the same verb.
Am 24.04.2012 08:02 schrieb rusi:
> On Apr 23, 9:34 am, Steven D'Aprano<steve
> wrote:
>
>> "is" is never ill-defined. "is" always, without exception, returns True
>> if the two operands are the same object, and False if they are not. This
>> is literally the simplest operator in Python.
>
> Circular definition: In case you did not notice, 'is' and 'are' are
> (or is it is?) the same verb.Steven's definition tries not to define the "verb" "is", but it defines
the meanung of the *operator* 'is'.
He says that 'a is b' iff a and be are *the same objects*. We don't need
to define the verb "to be", but the target of the definition is the
entity "object" and its identity.
Thomas
On Apr 24, 4:06 pm, Thomas Rachel <nutznetz-0c1b6768-bfa9-48d5- wrote: > Am 24.04.2012 08:02 schrieb rusi: > > > On Apr 23, 9:34 am, Steven D'Aprano<steve > > wrote: > > >> "is" is never ill-defined. "is" always, without exception, returns True > >> if the two operands are the same object, and False if they are not. This > >> is literally the simplest operator in Python. > > > Circular definition: In case you did not notice, 'is' and 'are' are > > (or is it is?) the same verb. > > Steven's definition tries not to define the "verb" "is", but it defines > the meanung of the *operator* 'is'. > > He says that 'a is b' iff a and be are *the same objects*. We don't need > to define the verb "to be", but the target of the definition is the > entity "object" and its identity.Identity, sameness, equality and the verb to be are all about the same concept(s) and their definitions are *intrinsically* circular; see http://plato.stanford.edu/entries/identity/#2 And the seeming simplicity of the circular definitions hide the actual complexity of 'to be' for python: http://docs.python.org/reference/expressions.... (footnote 7) for math/philosophy: http://www.math.harvard.edu/~mazur/preprints/...
On 4/24/2012 15:25, rusi wrote:
> On Apr 24, 4:06 pm, Thomas Rachel<nutznetz-0c1b6768-bfa9-48d5-
> wrote:
>> Am 24.04.2012 08:02 schrieb rusi:
>>
>>> On Apr 23, 9:34 am, Steven D'Aprano<steve
>>> wrote:
>>
>>>> "is" is never ill-defined. "is" always, without exception, returns True
>>>> if the two operands are the same object, and False if they are not. This
>>>> is literally the simplest operator in Python.
>>
>>> Circular definition: In case you did not notice, 'is' and 'are' are
>>> (or is it is?) the same verb.
>>
>> Steven's definition tries not to define the "verb" "is", but it defines
>> the meanung of the *operator* 'is'.
>>
>> He says that 'a is b' iff a and be are *the same objects*. We don't need
>> to define the verb "to be", but the target of the definition is the
>> entity "object" and its identity.
>
> Identity, sameness, equality and the verb to be are all about the same
> concept(s) and their definitions are *intrinsically* circular; see
> http://plato.stanford.edu/entries/identity/#2
>
> And the seeming simplicity of the circular definitions hide the actual
> complexity of 'to be'
> for python: http://docs.python.org/reference/expressions....
> (footnote 7)
> for math/philosophy: http://www.math.harvard.edu/~mazur/preprints/...What you say is true in general, but not from an operational point of
view, especially if we restrict the set of objects whose sameness or
identity we want to check:
Let O be a set of tuples (id, data) where
{(id, data1), (id, data2)} subset O => data1 = data2
Def. (id1, data1) and (id2, data2) in O are /the same/ iff id1 = id2.
Now, it's easy to find a bijection between O and the set of Python's
objects which are in memory at any single point in time.
Anyway, you're being unnecessarily pedantic.
Kiuhnm
Am 24.04.2012 15:25 schrieb rusi: > Identity, sameness, equality and the verb to be are all about the same > concept(s) and their definitions are *intrinsically* circular; see > http://plato.stanford.edu/entries/identity/#2Mybe in real life language. In programming and mathematics there are several forms of equality, where identity (≡) is stronger than equality (=). Two objects can be equal (=) without being identical (≡), but not the other way. As the ≡ is quite hard to type, programming languages tend to use other operators for this. E.g., in C, you can have int a; int b; a = 4; b = 4; Here a and b are equal, but not identical. One can be changed without changing the other. With int x; int *a=&x, *b=&x; *a and *b are identical, as they point to the same location. *a = 4 results in *b becoming 4 as well. In Python, you can have the situations described here as well. You can have a list and bind it to 2 names, or you can take 2 lists and bind them to that name. a = [3] b = [3] Here a == b is True, while a is b results in False. Thomas> > And the seeming simplicity of the circular definitions hide the actual > complexity of 'to be' > for python: http://docs.python.org/reference/expressions.... > (footnote 7) > for math/philosophy: http://www.math.harvard.edu/~mazur/preprints/...
On Wed, 25 Apr 2012 13:42:31 +0200, Thomas Rachel wrote: > Two objects can be equal (=) without being identical (≡), but not the > other way. >>> x = float('nan') >>> y = x >>> x is yTrue >>> x == y False By the way, in mathematics, ≡ normally means "is equivalent to", which is not quite the same as "identical to". http://mathworld.wolfram.com/Equivalent.html
On Thu, Apr 26, 2012 at 3:27 AM, Steven D'Aprano
wrote:
> By the way, in mathematics, ≡ normally means "is equivalent to", which is
> not quite the same as "identical to".That's perhaps because, in mathematics, nobody would even think of
asking if this 4 is the same as that 4. What sort of question is it?
Four is four! How could you tell one four from another? Mathematics is
not the same as programming, and the whole concept of objects in
memory simply isn't a mathematical one at all.
ChrisA
In article <4f9833ff$0$29965$c3e8da3$5496439d@news.astraweb.com>,
Steven D'Aprano wrote:
> On Wed, 25 Apr 2012 13:42:31 +0200, Thomas Rachel wrote:
>
> > Two objects can be equal (=) without being identical (â¡), but not the
> > other way.
>
>
> >>> x = float('nan')
> >>> y = x
> >>> x is y
> True
> >>> x == y
> FalseI love it. Thanks for posting that.
On 4/24/2012 8:02, rusi wrote:
> On Apr 23, 9:34 am, Steven D'Aprano<steve
> wrote:
>
>> "is" is never ill-defined. "is" always, without exception, returns True
>> if the two operands are the same object, and False if they are not. This
>> is literally the simplest operator in Python.
>
> Circular definition: In case you did not notice, 'is' and 'are' are
> (or is it is?) the same verb.Python is not English.
Double-quoted 'is' is a Python operator, while non-quoted 'is' and 'are'
are forms of the English verb 'to be'. If you change the name of the
operator or the language in which you define the operator, you'll
realize that there's no real circularity in that definition.
Kiuhnm
On 4/22/2012 21:43, John Nagle wrote:
> On 4/20/2012 9:34 PM, wrote:
>> On Friday, April 20, 2012 12:34:46 PM UTC-7, Rotwang wrote:
>>
>>> I believe it says somewhere in the Python docs that it's undefined and
>>> implementation-dependent whether two identical expressions have the same
>>> identity when the result of each is immutable
>
> Bad design. Where "is" is ill-defined, it should raise ValueError.
>
> A worse example, one which is very implementation-dependent:
>
> http://stackoverflow.com/questions/306313/pyt...
>
>
> >>> a = 256
> >>> b = 256
> >>> a is b
> True # this is an expected result
> >>> a = 257
> >>> b = 257
> >>> a is b
> False
>
> Operator "is" should be be an error between immutables
> unless one is a built-in constant.[...]
I can't think of a single case where 'is' is ill-defined.
You're blaming 'is' for revealing what's really going on. 'is' is /not/
implementation-dependent. It's /what's going on/ that's
implementation-dependent.
"a is b" is true iff 'a' and 'b' are the same object. Why should 'is'
lie to the user?
It does exactly what it says it does: it tells the user whether two
objects are the same.
In C++, for greater efficiency, two objects are compared for equality by
first checking their addresses and then their contents. 'is' would be
perfect for that.
What I don't like is 'isinstance'. I'd prefer an infix operator 'isa' or
'is_a'.
Kiuhnm
Kiuhnm <kiuhnm03.4t.yahoo.it> writes: > I can't think of a single case where 'is' is ill-defined.If I can't predict the output of print (20+30 is 30+20) # check whether addition is commutative print (20*30 is 30*20) # check whether multiplication is commutative by just reading the language definition and the code, I'd have to say "is" is ill-defined.> You're blaming 'is' for revealing what's really going on. 'is' is > /not/ implementation-dependent. It's /what's going on/ that's > implementation-dependent. > "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' > lie to the user?Whether a and b are the same object is implementation-dependent.
On Mon, Apr 23, 2012 at 1:01 PM, Paul Rubin lid> wrote: > > Kiuhnm <kiuhnm03.4t.yahoo.it> writes: > > I can't think of a single case where 'is' is ill-defined. > > If I can't predict the output of > > print (20+30 is 30+20) # check whether addition is commutative > print (20*30 is 30*20) # check whether multiplication is commutative > > by just reading the language definition and the code, I'd have to say > "is" is ill-defined. >The "is" operator is perfectly defined. But it doesn't check to see whether two objects hold equivalent values, it checks whether they are the same thing. You're not interested in whether 20+30 and 30+20 are the same object, you're interested in whether they return equivalent values which should be checked with ==.> > You're blaming 'is' for revealing what's really going on. 'is' is > > /not/ implementation-dependent. It's /what's going on/ that's > > implementation-dependent. > > "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' > > lie to the user? > > Whether a and b are the same object is implementation-dependent. > --And if I try running "from java.util import ArrayList" in CPython it will give me an import error. It doesn't mean that the import mechanism is broken because it returns different results in different implementations of Python.
On Mon, Apr 23, 2012 at 1:21 PM, Benjamin Kaplan wrote: > On Mon, Apr 23, 2012 at 1:01 PM, Paul Rubin lid> wrote: > The "is" operator is perfectly defined. But it doesn't check to see > whether two objects hold equivalent values, it checks whether they are > the same thing. You're not interested in whether 20+30 and 30+20 are > the same object, you're interested in whether they return equivalent > values which should be checked with ==.The only way to check if two values are the same or not is to compare via is or compare the return values of id(). So you can say that is or id() are ill-defined, or you can say that the identity of new numbers is ill-defined, and it doesn't really make a difference, ever, because is and id() are the only way in which object identity even _exists_ in Python.>> Whether a and b are the same object is implementation-dependent. >> -- > > And if I try running "from java.util import ArrayList" in CPython it > will give me an import error. It doesn't mean that the import > mechanism is broken because it returns different results in different > implementations of Python.Nobody is calling Python broken. "ill defined" != "broken". -- Devin
On 4/23/2012 1:55 PM, Devin Jeanpierre wrote: > On Mon, Apr 23, 2012 at 1:21 PM, Benjamin Kaplan > wrote: >> On Mon, Apr 23, 2012 at 1:01 PM, Paul Rubin<lid> wrote: >> The "is" operator is perfectly defined. But it doesn't check to see >> whether two objects hold equivalent values, it checks whether they are >> the same thing. You're not interested in whether 20+30 and 30+20 are >> the same object, you're interested in whether they return equivalent >> values which should be checked with ==. > > The only way to check if two values are the same or not is to compare > via is or compare the return values of id().To quote you, 'bollocks'. The right way to compare values is with value comparison operators. > So you can say that is or > id() are ill-defined, or you can say that the identity of new numbers > is ill-defined,'numbers' do not have identity, at least not separate from value. Concrete computational process objects do.
On Mon, Apr 23, 2012 at 4:06 PM, Terry Reedy wrote: >> The only way to check if two values are the same or not is to compare >> via is or compare the return values of id(). > > To quote you, 'bollocks'. The right way to compare values is with value > comparison operators.I meant "the same object".> >> So you can say that is or >> >> id() are ill-defined, or you can say that the identity of new numbers >> is ill-defined, > > > 'numbers' do not have identity, at least not separate from value. > Concrete computational process objects do.I meant "number objects". I hope I did not cause any genuine confusion. -- Devin
On 4/23/2012 19:01, Paul Rubin wrote: > Kiuhnm<kiuhnm03.4t.yahoo.it> writes: >> I can't think of a single case where 'is' is ill-defined. > > If I can't predict the output of > > print (20+30 is 30+20) # check whether addition is commutative > print (20*30 is 30*20) # check whether multiplication is commutative > > by just reading the language definition and the code, I'd have to say > "is" is ill-defined.Counterexample: import datetime print(datetime.datetime.now())>> You're blaming 'is' for revealing what's really going on. 'is' is >> /not/ implementation-dependent. It's /what's going on/ that's >> implementation-dependent. >> "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' >> lie to the user? > > Whether a and b are the same object is implementation-dependent.My point exactly. Kiuhnm
On Mon, Apr 23, 2012 at 11:01 AM, Paul Rubin lid> wrote:
> Kiuhnm <kiuhnm03.4t.yahoo.it> writes:
>> I can't think of a single case where 'is' is ill-defined.
>
> If I can't predict the output of
>
> print (20+30 is 30+20) # check whether addition is commutative
> print (20*30 is 30*20) # check whether multiplication is commutative
>
> by just reading the language definition and the code, I'd have to say
> "is" is ill-defined.It seems to me that one could equally argue that "+" is ill-defined,
because the output of "x + y" could just as easily be 1729 or 42. In
either case the problem is not in the operator, but in determining
exactly what the left and right sides evaluate to.
On Mon, 23 Apr 2012 10:01:24 -0700, Paul Rubin wrote: > Kiuhnm <kiuhnm03.4t.yahoo.it> writes: >> I can't think of a single case where 'is' is ill-defined. > > If I can't predict the output of > > print (20+30 is 30+20) # check whether addition is commutative > print (20*30 is 30*20) # check whether multiplication is > commutative > > by just reading the language definition and the code, I'd have to say > "is" is ill-defined.The failure to predict the result of the above has nothing to do with "is". Your test doesn't test what you think it does, because the output doesn't just depend on the behaviour of "is". Aside from the properties of arithmetic, the result printed depends on at least six factors: * the behaviour of "is" (known) * the behaviour of the Python interpreter when evaluating arithmetic expressions (undefined) * the behaviour of the interpreter when creating objects (undefined) * the presence or absence of a keyhole optimizer (undefined) * and how aggressive it is (undefined) * any other optimizations the compiler might apply (undefined) The *one* factor which actually is well-defined by the language and completely predictable is the thing that you are John are blaming for your failure to predict the outcome! That's simply surreal. It isn't the "is" operator that is ill-defined. What actually is ill- defined is the circumstances where a Python implementation uses the same object for equal results. And that is as it should be. The decision to recreate or reuse objects when needed should not be part of the language definition. Why do you care whether a, b = 1+2, 5-2 creates two objects or one? At most, that's a quality of implementation issue. It certainly doesn't affect the semantics of the language.>> You're blaming 'is' for revealing what's really going on. 'is' is /not/ >> implementation-dependent. It's /what's going on/ that's >> implementation-dependent. >> "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' >> lie to the user? > > Whether a and b are the same object is implementation-dependent.And that has absolutely nothing to do with the behaviour of "is". The "is" operator is not responsible for whether a and b are the same object. The "is" operator has an exact definition. There is nothing ill-defined about the "is" operator. That definition is simple enough for me to quote it in full: The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. x is not y yields the inverse truth value. Taken from the last paragraph of here: http://docs.python.org/py3k/reference/express... Short of having "is" be a null-op that always returns False, there are no changes you can make to the definition of "is" that will make "a is b" any more predictable.
On Mon, Apr 23, 2012 at 3:58 PM, Steven D'Aprano wrote: >> Whether a and b are the same object is implementation-dependent. > > And that has absolutely nothing to do with the behaviour of "is". The > "is" operator is not responsible for whether a and b are the same object.Heh, it has everything to do with the behavior of is. Although I know what you mean.> Short of having "is" be a null-op that always returns False, there are no > changes you can make to the definition of "is" that will make "a is b" > any more predictable.Well, no. Immutable objects could always compare equal, for example. This is more expensive though. is as-it-stands is very quick to execute, which is probably attractive to some people (especially for its used in detecting special constants). -- Devin
On Mon, Apr 23, 2012 at 4:27 PM, Devin Jeanpierre
wrote:
> Well, no. Immutable objects could always compare equal, for example.
> This is more expensive though. is as-it-stands is very quick to
> execute, which is probably attractive to some people (especially for
> its used in detecting special constants).I don't know what made me write that so wrong. I meant "immutable
objects that are equal could always compare the same via is".
That makes 4 posts in a row. Sorry for the spam.
-- Devin
On 24 April 2012 06:40, Devin Jeanpierre wrote:
> On Mon, Apr 23, 2012 at 4:27 PM, Devin Jeanpierre
> wrote:
> > Well, no. Immutable objects could always compare equal, for example.
> > This is more expensive though. is as-it-stands is very quick to
> > execute, which is probably attractive to some people (especially for
> > its used in detecting special constants).
>
> I don't know what made me write that so wrong. I meant "immutable
> objects that are equal could always compare the same via is".
>And doing that would make zero sense, because it directly contradicts the
whole *point* of "is". The point of "is" is to tell you whether or not two
references are to the same object. This is a *useful* property.
I'll leave aside the question of how you determine if an object is
immutable, and restrict the discussion to a few built-in types that are
known to be immutable.
If two objects are not the same object, then lying and saying they are
would remove the opportunity for various programming techniques, such as
interning. Of course, you could say that all immutable objects should be
interned automatically. There are a couple problems with this that I can
think of off the top of my head.
The first problem is memory. If every immutable object is interned then
welcome to the world of ever-expanding memory usage. Ah - but Python has
got around this for interned strings! They're ejected from the intern cache
when there are no more references. Surely we could do the same for integers
and other immutables?
That brings us to performance. You do not want computations involving
immutable objects to suffer severe performance degradation just to make
equal immutable objects have the same identity. But if every single step of
a numerical calculation involved the following sequence of possible steps,
that's exactly what you would be doing:
1. Calculate result;
2. Lookup result in integer intern cache (involves hash() and ==);
- unavoidable
3. Add result to integer intern cache (involves hash() and ==, and maybe
resizing the cache);
- necessary if your result is not currently referenced anywhere else in the
Python VM
4. Lookup previous intermediate result in integer intern
cache (involves hash() and ==);
- necessary if you have a previous intermediate result
5. Eject previous intermediate result from integer intern
cache (involves hash() and ==).
- necessary if you have a previous intermediate result that is not
currently referenced anywhere else in the Python VM
Now think of the Python implementation of any checksum algorithm. Nearly
every intermediate result (a reasonably-large hash) is not going to be used
anywhere else in the VM, and will require all 4 extra steps. Ouch.
Instead, CPython makes the (sensible) choice to intern heavily-used
integers permanently - (-5, 256) IIRC - and leaves the rest up to the
programmer.
Strings are a different question. Unlike integers, where == is cheap, ==
for strings can be prohibitively expensive. Consider the case that for
whatever reason you create a 1GB string. Now imagine creating or deleting a
reference to *any* string potentially involves calling == on the 1GB
string. Ouch again.
Instead, CPython makes the (sensible) choice to automatically intern short
strings that look like names (in the Python sense) and leave everything
else up to the programmer. It's possible for the programmer to manually
intern their 1GB string, but they've then got to deal with the consequences
of doing so.
Tim Delaney
On Mon, Apr 23, 2012 at 6:26 PM, Tim Delaney wrote: > And doing that would make zero sense, because it directly contradicts the > whole *point* of "is". The point of "is" is to tell you whether or not two > references are to the same object. This is a *useful* property.It's useful for mutable objects, yes. How is it useful for immmutable things? They behave identically everywhere where you don't directly ask the question of "a is b" or "id(a) == id(b)". And whenever you ask that question, the answer is an implementation detail: sometimes equality implies "is", sometimes not, it depends on the values and the implementation of Python, because the implementation is free to intern liberally. Therefore it shouldn't make any difference if we make equality _always_ imply "is", since that was always on the table as an implementation detail. I think the real breakage is that now "a is b" is not equivalent to "id(a) == id(b)" (unless you try to hack that as well.)> I'll leave aside the question of how you determine if an object is > immutable, and restrict the discussion to a few built-in types that are > known to be immutable.Right. This is what everyone does, when they implement this "modified is" (e.g. ECMAScript Harmony's "is" operator works this way: http://wiki.ecmascript.org/doku.php?id=harmon... ).> If two objects are not the same object, then lying and saying they are would > remove the opportunity for various programming techniques, such as > interning. Of course, you could say that all immutable objects should be > interned automatically. There are a couple problems with this that I can > think of off the top of my head.Hold up, why do we give up interning? As you mention later, interning only involves hashing and equality comparisons, not id() or is. (e.g. we can intern `y` just fine with `y = cache.setdefault(y, y)`) -- Devin
On 24 April 2012 09:08, Devin Jeanpierre wrote: > On Mon, Apr 23, 2012 at 6:26 PM, Tim Delaney > wrote: > > And doing that would make zero sense, because it directly contradicts the > > whole *point* of "is". The point of "is" is to tell you whether or not > two > > references are to the same object. This is a *useful* property. > > It's useful for mutable objects, yes. How is it useful for immmutable > things? They behave identically everywhere where you don't directly > ask the question of "a is b" or "id(a) == id(b)".Not always. NaNs are an exception - they don't even compare equal to themselves. And hence a very good reason why "is" and == are separate operations.> Therefore it shouldn't make any difference if we make equality > _always_ imply "is", since that was always on the table as an > implementation detail. >See above. Identity does not necessarily imply equality.> I think the real breakage is that now "a is b" is not equivalent to > "id(a) == id(b)" (unless you try to hack that as well.)The operations are indeed equivalent if the lifetimes of both objects cover the entire computation. Since you are not rebinding names here, there is no way that the operations could not be equivalent (unless multithreading is involved and the name bindings change underneath you, but I think we can discount that for the purposes of this discussion). There is no breakage here - just (I'm suspecting willful) lack of understanding.>>> a = "a" >>> b = a >>> a is bTrue >>> id(a) == id(b) True>>> a = "a" >>> b = "b" >>> a is bFalse >>> id(a) == id(b) False Absolutely consistent results. The fact that a Python implementation is free to reuse IDs once that ID is no longer in use (the behaviour prompting this thread in the first place) is irrelevant here. At no time will "a is b" and "id(a) == id(b)" give inconsistent results when the names "a" and "b" remain bound to the same objects across the comparisons.> remove the opportunity for various programming techniques, such as > > interning. Of course, you could say that all immutable objects should be > > interned automatically. There are a couple problems with this that I can > > think of off the top of my head. > > Hold up, why do we give up interning? As you mention later, interning > only involves hashing and equality comparisons, not id() or is. (e.g. > we can intern `y` just fine with `y = cache.setdefault(y, y)`)I should now conclude that you have no interest in actually discussing this but just want to take an obstinate position and never consider moving from it. But I'll give one last chance. What is the whole point of interning? It's to get a canonical object to reference:>>> a = "a " + "b" >>> b = "a b" >>> a is bFalse>>> a = intern(a) >>> b = intern(b) >>> a is bTrue For (most*) immutable objects, if there is only ever a single canonical instance with that value, == and "is" are effectively the same operation (what you seem to want). Python's types are implemented this way - they do an "is" check before moving onto the more expensive equality check (except where overridden*). That's the benefit. But as you will see if you go back and read my message, there is a tradeoff in memory and/or performance involved in the canonicalising. Somewhere between no canonicalising and canonicalising everything (inclusive) is a sweet spot in terms of this tradeoff, and it varies depending on other decisions that have been made for the language and (of course) the actual program being run. * NaNs again. Tim Delaney
On 4/24/12 1:03 AM, Tim Delaney wrote: > On 24 April 2012 09:08, Devin Jeanpierre > > wrote: > > On Mon, Apr 23, 2012 at 6:26 PM, Tim Delaney > > wrote: > > And doing that would make zero sense, because it directly contradicts the > > whole *point* of "is". The point of "is" is to tell you whether or not two > > references are to the same object. This is a *useful* property. > > It's useful for mutable objects, yes. How is it useful for immmutable > things? They behave identically everywhere where you don't directly > ask the question of "a is b" or "id(a) == id(b)". > > > Not always. NaNs are an exception - they don't even compare equal to themselves. > And hence a very good reason why "is" and == are separate operations.I think you misread what Devin wrote. "id(a) == id(b)" not "a == b".
On 24 April 2012 10:18, Robert Kern wrote:
> On 4/24/12 1:03 AM, Tim Delaney wrote:
>
>> On 24 April 2012 09:08, Devin Jeanpierre
>> **>> wrote:
>>
>> On Mon, Apr 23, 2012 at 6:26 PM, Tim Delaney
>> <mailto:timothy.c.delaney@**gmail.com<>>
>> wrote:
>> > And doing that would make zero sense, because it directly
>> contradicts the
>> > whole *point* of "is". The point of "is" is to tell you whether or
>> not two
>> > references are to the same object. This is a *useful* property.
>>
>> It's useful for mutable objects, yes. How is it useful for immmutable
>> things? They behave identically everywhere where you don't directly
>> ask the question of "a is b" or "id(a) == id(b)".
>>
>>
>> Not always. NaNs are an exception - they don't even compare equal to
>> themselves.
>> And hence a very good reason why "is" and == are separate operations.
>>
>
> I think you misread what Devin wrote. "id(a) == id(b)" not "a == b".No - I was addressing "they behave identically everywhere ..." in the
quote. NaNs are a case whre they do not behave identically everywhere.
Tim Delaney
On Mon, Apr 23, 2012 at 8:03 PM, Tim Delaney wrote: > On 24 April 2012 09:08, Devin Jeanpierre wrote: >> >> On Mon, Apr 23, 2012 at 6:26 PM, Tim Delaney >> wrote: >> > And doing that would make zero sense, because it directly contradicts >> > the >> > whole *point* of "is". The point of "is" is to tell you whether or not >> > two >> > references are to the same object. This is a *useful* property. >> >> It's useful for mutable objects, yes. How is it useful for immmutable >> things? They behave identically everywhere where you don't directly >> ask the question of "a is b" or "id(a) == id(b)". > > > Not always. NaNs are an exception - they don't even compare equal to > themselves. And hence a very good reason why "is" and == are separate > operations.You can't use "is": with NaNs any more than any other number. Sometimes it does what you want, sometimes not, depending on whether Python interns NaNs. The only real way to check if a value is NaN is to use math.isnan. The differing behavior of == and "is" is _not_ reliable -- sometimes a is b will return False even though both a and b are NaN. There are systems under which "is" _would_ be reliable for this. I'm not sure whether I'd want to argue for it, but I'm most of the way anyway. You would have to either intern all NaNs, or else change the definition of "is". Also, when I say "two things behave identically everywhere", I mean that we have two objects, `a` and `b`, such that for any function f(), you'd have the exact same effects of your program called f(a) or f(b) in the same place. (This is addressed in the following page: http://home.pipeline.com/~hbaker1/ObjectIdent... ) There is no way to observe any difference between immutable values that are equal except via id() and the is operator. They are equal, and they will never stop being equal. Presumably equality is designed sanely so that they have the same structure, so iteration etc. all have the same results. There is no way to distinguish them based on any of their properties, except to resort to the is operator and id() function. This is not true of mutable values. For example, consider the following program: a = [] b = c = [] c.append(3) print b a and c cannot be completely identical, because if we replaced "c.append(3)" with "a.append(3)", the print at the bottom would have a different outcome. No such difference can ever occur with immutable objects (modulo some technical details if you really want to get picky, and also id and is). So, to address NaNs: if you have a, and a is a NaN, and so is b -- what program would behave differently if you swapped a and b? how do you tell them apart? My assertion from before is that they behave identically -- they behave the same way -- everywhere where you don't directly ask the question of "a is b" or "id(a) == id(b)".>> >> Therefore it shouldn't make any difference if we make equality >> _always_ imply "is", since that was always on the table as an >> implementation detail. > > > See above. Identity does not necessarily imply equality.You have the implication backwards. I was saying that, for immutable objects, perhaps equality should imply identity. I was furthermore justifying this by saying that it wouldn't break anything that wasn't already broken. i.e., the only things that would be broken were the things that didn't remember that Python can intern immutable objects.>> >> I think the real breakage is that now "a is b" is not equivalent to >> "id(a) == id(b)" (unless you try to hack that as well.) > > > The operations are indeed equivalent if the lifetimes of both objects cover > the entire computation. Since you are not rebinding names here, there is no > way that the operations could not be equivalent (unless multithreading is > involved and the name bindings change underneath you, but I think we can > discount that for the purposes of this discussion). > > There is no breakage here - just (I'm suspecting willful) lack of > understanding.It's you that doesn't understand. Perhaps it is willful lack of understanding? ;) I was discussing what happens if you change "is" such that "a is b" in all current situations, plus the situations where a and b are immutable and equal. The email you originally sent was in response to one in which I change the way "is" works, and you had a complaint against that change. In response, I said that I don't think the complaint is a good one, but I suggested one other complaint. Under the change that I suggested, id(a) == id(b) would no longer be equivalent to a is b, because `a is b` can be true even for objects in different places in memory. I think, in that context, a lot of what you say probably doesn't make sense the way you meant it to, but I'll address it anyway.> I should now conclude that you have no interest in actually discussing this > but just want to take an obstinate position and never consider moving from > it.There are two situations. In one, I'm a troll, and your statement doesn't do anything because trolls only feed on that sort of thing. In the other, I'm being genuine, and you're not being nice. I have an interest in discussing this, but you apparently don't see it that way. What's happening from my perspective is that we're both talking past each other and not understanding what we're saying. I've tried to communicate better in this message as a response to that.> For (most*) immutable objects, if there is only ever a single canonical > instance with that value, == and "is" are effectively the same operation > (what you seem to want). Python's types are implemented this way - they do > an "is" check before moving onto the more expensive equality check (except > where overridden*). That's the benefit. But as you will see if you go back > and read my message, there is a tradeoff in memory and/or performance > involved in the canonicalising. Somewhere between no canonicalising and > canonicalising everything (inclusive) is a sweet spot in terms of this > tradeoff, and it varies depending on other decisions that have been made for > the language and (of course) the actual program being run.I'm not talking about canonicalizing the way you were discussing it. I was attempting to show that such canoncalization is not necessary. You said that such canonicalization would be necessary because interning isn't possible once you change `is`. I disagreed, and presented a way of using a dict that behaves like interning, but without using `is` or identity comparisons. I'll be more explicit and specifically define such a function: cache = {} def intern(x): return cache.setdefault(x, x) Do we agree that A) this function works without using identity comparisons, and B) this function performs the task of interning? > * NaNs again. As for NaNs, to intern those you'd need to explicitly check for them, since they interfere with the dict storage mechanics. So the above intern function would not work. This would, however: cache = {} cachednan = float('nan') def intern(x): try: if math.isnan(x): return cachednan except TypeError: pass return cache.setdefault(x, x) I hope, again, that I've demonstrated that we don't need to canonicalize everything just to implement interning. -- Devin
On 24 April 2012 11:08, Devin Jeanpierre wrote: > cache = {} > > def intern(x): > return cache.setdefault(x, x) > > Do we agree that A) this function works without using identity > comparisons, and B) this function performs the task of interning?Of course. I never claimed otherwise. It is however returning a reference to a canonical object equal to the object being referred to by "x". In this case, the canonical/standard form is "the first equal object that was put into the cache". cache = {} def intern(x): return cache.setdefault(x, x)>>> a = "x y" >>> b = "x y" >>> a is bFalse >>> id(a) == id(b) False>>> a = intern(a) >>> b = intern(b) >>> a is bTrue >>> id(a) == id(b) True My claim is that doing this automatically for all integers and/or strings could lead to prohibitively-expensive performance characteristics, and done wrong to prohibitively-expensive memory characteristics. Now, you could declare "is" and == as the same operation for a whitelist of types, but people have actual uses for being able to distinguish different instances of equal immutable types. You shouldn't need to do it in your normal day-to-day programming, but there are real uses for it. cache = {}> cachednan = float('nan') > > def intern(x): > try: > if math.isnan(x): > return cachednan > except TypeError: > pass > return cache.setdefault(x, x) > > I hope, again, that I've demonstrated that we don't need to > canonicalize everything just to implement interning.Except that the above is a canonicalisation function. Tim Delaney
On Mon, Apr 23, 2012 at 11:03 PM, Tim Delaney
wrote:
> My claim is that doing this automatically for all integers and/or strings
> could lead to prohibitively-expensive performance characteristics, and done
> wrong to prohibitively-expensive memory characteristics.OK. I agree, and I'm not in favor of doing that. It's a nice
optimization, there's no point doing it in cases where it leads to
_worse_ performance.
-- Devin
On 2012-04-23, Paul Rubin wrote: > Kiuhnm <kiuhnm03.4t.yahoo.it> writes: >> I can't think of a single case where 'is' is ill-defined. > > If I can't predict the output of > > print (20+30 is 30+20) # check whether addition is commutative > print (20*30 is 30*20) # check whether multiplication is commutative > > by just reading the language definition and the code, I'd have to say > "is" is ill-defined."is" was never designed for comparing literals or expressions. the expression 20+30 is 30+20 maybe syntactically correct but is nevertheless pretty senseless and you can, of course, not check commutativity with it. For checking commutativity you have to use: 20+30 == 30+20> >> You're blaming 'is' for revealing what's really going on. 'is' is >> /not/ implementation-dependent. It's /what's going on/ that's >> implementation-dependent. >> "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' >> lie to the user? > > Whether a and b are the same object is implementation-dependent. >>> a = something >>> b = a >>> a is bTrue Should be guaranteed and implementation-independent. Bernd
On Mon, 23 Apr 2012 10:01:24 -0700, Paul Rubin wrote: >> I can't think of a single case where 'is' is ill-defined. > > If I can't predict the output of > > print (20+30 is 30+20) # check whether addition is commutative print > (20*30 is 30*20) # check whether multiplication is commutative > > by just reading the language definition and the code, I'd have to say "is" > is ill-defined.If anything is ill-defined, then it's "+" and "*", i.e. it's unspecified whether the value which they return is a unique object or a reference to some other object. More accurately, the existence of "is", "is not" and "id" cause many other constructs to have "ill-defined" behaviour.>> "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' >> lie to the user? > > Whether a and b are the same object is implementation-dependent.And what's wrong with that? If you want a language which precisely specifies all observable behaviour, you're going to end up with a rather useless language. For a start, it can't have a time() function. For similar reasons, you can't have networking or any form of preemptive concurrency (which includes any form of inter-process communication on an OS which uses preemptive multi-tasking).
On Apr 25, 10:38 am, Nobody wrote: > On Mon, 23 Apr 2012 10:01:24 -0700, Paul Rubin wrote: > >> I can't think of a single case where 'is' is ill-defined. > > > If I can't predict the output of > > > print (20+30 is 30+20) # check whether addition is commutative print > > (20*30 is 30*20) # check whether multiplication is commutative > > > by just reading the language definition and the code, I'd have to say "is" > > is ill-defined. > > If anything is ill-defined, then it's "+" and "*", i.e. it's unspecified > whether the value which they return is a unique object or a reference to > some other object. >Such a definition precludes meaningful operator overloading and is highly problematic for floating-point numbers. There's also no way to enforce it, but I think you know that too. :) Identity and equality are distinct concepts in programming languages. There's nothing that can be done about that, and no particularly good reason to force certain language behaviors because some "programmers" have difficulty with the distinction. Though, maybe it's better to use a different keyword than 'is' though, due to the plain English connotations of the term; I like 'sameobj' personally, for whatever little it matters. Really, I think taking away the 'is' operator altogether is better, so the only way to test identity is: id(x) == id(y) Though I would prefer: addr(x) == addr(y) myself, again, for what little it matters. The right thing to do when confronted with this problem is teach the difference and move on. As an aside, the whole problem with 'is' and literals is perhaps the only really good argument for a 'new' keyword/operator like C++ and Java have. Then it's more explicit to the programmer that they've created two objects (in this case, anyway).> More accurately, the existence of "is", "is not" and "id" cause many other > constructs to have "ill-defined" behaviour. > > >> "a is b" is true iff 'a' and 'b' are the same object. Why should 'is' > >> lie to the user? > > > Whether a and b are the same object is implementation-dependent. > > And what's wrong with that? If you want a language which precisely > specifies all observable behaviour, you're going to end up with a rather > useless language. For a start, it can't have a time() function. For > similar reasons, you can't have networking or any form of preemptive > concurrency (which includes any form of inter-process communication on an > OS which uses preemptive multi-tasking).Fully specified does not mean fully deterministic. What makes a specification of "Any value in the range 0 through N" less 'full' than a specification of "X" or a constant? Adam
On 4/25/2012 4:49 PM, Adam Skutt wrote: > Identity and equality are distinct concepts in programming languages. > There's nothing that can be done about that, and no particularly good > reason to force certain language behaviors because some "programmers" > have difficulty with the distinction. > > Though, maybe it's better to use a different keyword than 'is' though, > due to the plain English > connotations of the term; I like 'sameobj' personally, for whatever > little it matters. Really, I think taking away the 'is' operator > altogether is better, so the only way to test identity is: > id(x) == id(y) > Though I would prefer: > addr(x) == addr(y) > myself, again, for what little it matters.The fact that id(x) is machine_addr(x) in CPython is specific to CPython, not required by the language spec, and not true in implementations that move objects around when garbage collecting.
On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: > Though, maybe it's better to use a different keyword than 'is' though, > due to the plain English > connotations of the term; I like 'sameobj' personally, for whatever > little it matters. Really, I think taking away the 'is' operator > altogether is better, so the only way to test identity is: > id(x) == id(y)Four reasons why that's a bad idea: 1) The "is" operator is fast, because it can be implemented directly by the interpreter as a simple pointer comparison (or equivalent). The id() idiom is slow, because it involves two global lookups and an equality comparison. Inside a tight loop, that can make a big difference in speed. 2) The "is" operator always has the exact same semantics and cannot be overridden. The id() function can be monkey-patched. 3) The "is" idiom semantics is direct: "a is b" directly tests the thing you want to test, namely whether a is b. The id() idiom is indirect: "id(a) == id(b)" only indirectly tests whether a is b. 4) The id() idiom already breaks if you replace names a, b with expressions: >>> id([1,2]) == id([3,4]) True> Though I would prefer: > addr(x) == addr(y)But that's absolutely wrong. id(x) returns an ID, not an address. It just happens that, as an accident of implementation, the CPython interpreter uses the object address as an ID, because objects can't move. That's not the case for all implementations. In Jython, objects can move and the address is not static, and so IDs are assigned on demand starting with 1: steve@runes:~$ jython Jython 2.5.1+ (Release_2_5_1, Aug 4 2010, 07:18:19) [OpenJDK Client VM (Sun Microsystems Inc.)] on java1.6.0_18 Type "help", "copyright", "credits" or "license" for more information. >>> id(42) 1 >>> id("Hello World!") 2 >>> id(None) 3 Other implementations may make other choices. I don't believe that the language even defines the id as a number, although I could be wrong about that. Personally, I prefer the Jython approach, because it avoids those annoying questions like "How do I dereference the address of an object?" (answer: Python is not C, you can't do that), and IDs are globally unique and never reused for the lifetime of the process.
On Apr 25, 8:01 pm, Steven D'Aprano <steve wrote: > On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: > > Though, maybe it's better to use a different keyword than 'is' though, > > due to the plain English > > connotations of the term; I like 'sameobj' personally, for whatever > > little it matters. Really, I think taking away the 'is' operator > > altogether is better, so the only way to test identity is: > > id(x) == id(y) > > Four reasons why that's a bad idea: > > 1) The "is" operator is fast, because it can be implemented directly by > the interpreter as a simple pointer comparison (or equivalent). The id() > idiom is slow, because it involves two global lookups and an equality > comparison. Inside a tight loop, that can make a big difference in speed.The runtime can optimize the two operations to be equivalent, since they are logically equivalent operations. If you removed 'is', there's little reason to believe it would do otherwise.> > 2) The "is" operator always has the exact same semantics and cannot be > overridden. The id() function can be monkey-patched. >I can't see how that's useful at all. Identity is a fundamental property of an object; hence retrieval of it must be a language operation. The fact Python chooses to do otherwise is unfortunate, but also irrelevant to my position.> 3) The "is" idiom semantics is direct: "a is b" directly tests the thing > you want to test, namely whether a is b. The id() idiom is indirect: > "id(a) == id(b)" only indirectly tests whether a is b.The two expressions are logically equivalent, so I don't see how this matters, nor how it is true.> > 4) The id() idiom already breaks if you replace names a, b with > expressions: > > >>> id([1,2]) == id([3,4]) > > TrueIt's not broken at all. The lifetime of temporary objects is intentionally undefined, and that's a /good/ thing. What's unfortunate is that CPython optimizes temporaries differently between the two logically equivalent expressions. As long as this holds: >>> class A(object): ... def __del__(self): ... print "Farewell to: %d" % id(self) ... >>> A() is A() Farewell to: 4146953292 Farewell to: 4146953260 False >>> id(A()) == id(A()) Farewell to: 4146953420 Farewell to: 4146953420 True then there's nothing "broken" about the behavior of either expression. I personally think logically equivalent expressions should give the same results, but since both operations follow the rules of object identity correctly, it's not the end of the world. It's only surprising to the programmer if: 1) They don't understand identity. 2) They don't understand what objects are and are not temporaries. Code that relies on the identity of a temporary object is generally incorrect. This is why C++ explicitly forbids taking the address (identity) of temporaries. As such, the language behavior in your case is inconsequential. Making demons fly out of the programmer's nose would be equally appropriate. The other solution is to do what Java and C# do: banish id() entirely and only provide 'is' (== in Java, Object.ReferenceEquals() in C#). That seems just as fine, really, Practically, it's also probably the better solution for CPython, which is fine by me. My preference forkeeping id() and removing 'is' probably comes from my background as a C ++ programmer, and I already said it matters very little. > But that's absolutely wrong. id(x) returns an ID, not an address. > It just > happens that, as an accident of implementation, the CPython interpreter > uses the object address as an ID, because objects can't move. That's not > the case for all implementations. In Jython, objects can move and the > address is not static, and so IDs are assigned on demand starting with 1: > > steve@runes:~$ jython > Jython 2.5.1+ (Release_2_5_1, Aug 4 2010, 07:18:19) > [OpenJDK Client VM (Sun Microsystems Inc.)] on java1.6.0_18 > Type "help", "copyright", "credits" or "license" for more information.>>> id(42) > 1 > >>> id("Hello World!") > 2 > >>> id(None) > > 3 >An address is an identifier: a number that I can use to access a value[1]. I never said that id() must return an address the host CPU understands (virtual, physical, or otherwise). Most languages use addresses that the host CPU cannot understand without assistance at least sometimes, including C on some platforms.> Other implementations may make other choices. I don't believe that the > language even defines the id as a number, although I could be wrong about > that.http://docs.python.org/library/functions.html... says it must be an integer of some sort. Even if it didn't say that, it hardly seems as a practical imposition.> > Personally, I prefer the Jython approach, because it avoids those > annoying questions like "How do I dereference the address of an > object?" (answer: Python is not C, you can't do that),The right way to solve that question isn't to fix the runtime, but to teach people what pointer semantics actually mean, much like the identity problem we're discussing now. Adam [1] I'd be more willing to accept a more general definition that allows for non-numeric addresses, but such things are rare.
On Thu, Apr 26, 2012 at 1:50 PM, Adam Skutt wrote:
> On Apr 25, 8:01 pm, Steven D'Aprano <steve
> wrote:
>> 2) The "is" operator always has the exact same semantics and cannot be
>> overridden. The id() function can be monkey-patched.
>
> I can't see how that's useful at all. Identity is a fundamental
> property of an object; hence retrieval of it must be a language
> operation.
> ...
> The other solution is to do what Java and C# do: banish id() entirely
> and only provide 'is' (== in Java, Object.ReferenceEquals() in C#).The 'is' operator is a language feature. The id() function is not.
ChrisA
On Wed, 25 Apr 2012 20:50:21 -0700, Adam Skutt wrote: > On Apr 25, 8:01 pm, Steven D'Aprano <steve > wrote: >> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: >> > Though, maybe it's better to use a different keyword than 'is' >> > though, due to the plain English >> > connotations of the term; I like 'sameobj' personally, for whatever >> > little it matters. Really, I think taking away the 'is' operator >> > altogether is better, so the only way to test identity is: >> > id(x) == id(y) >> >> Four reasons why that's a bad idea: >> >> 1) The "is" operator is fast, because it can be implemented directly by >> the interpreter as a simple pointer comparison (or equivalent). The >> id() idiom is slow, because it involves two global lookups and an >> equality comparison. Inside a tight loop, that can make a big >> difference in speed. > > The runtime can optimize the two operations to be equivalent, since they > are logically equivalent operations. If you removed 'is', there's > little reason to believe it would do otherwise.I'm afraid you are mistaken there. *By design*, Python allows shadowing and monkey-patching of built-ins. (Although not quite to the same degree as Ruby, and thank goodness!) Given the language semantics of Python, "id(a) == id(b)" is NOT equivalent to "a is b" since the built-in id() function can be shadowed by some other function at runtime, but the "is" operator cannot be. An extremely clever optimizing implementation like PyPy may be able to recognise at runtime that the built-in id() is being called, but that doesn't change the fact that PyPy MUST support this code in order to claim to be a Python compiler: id = lambda x: 999 id(None) == id("spam") # returns True If your runtime doesn't allow that, it isn't Python.>> 2) The "is" operator always has the exact same semantics and cannot be >> overridden. The id() function can be monkey-patched. >> >> > I can't see how that's useful at all. Identity is a fundamental > property of an object; hence retrieval of it must be a language > operation. The fact Python chooses to do otherwise is unfortunate, but > also irrelevant to my position.It's useful for the same reason that shadowing any other builtin is useful. id() isn't special enough to complicate the simple, and effective, execution model just to satisfy philosophers.>> 3) The "is" idiom semantics is direct: "a is b" directly tests the >> thing you want to test, namely whether a is b. The id() idiom is >> indirect: "id(a) == id(b)" only indirectly tests whether a is b. > > The two expressions are logically equivalent, so I don't see how this > matters, nor how it is true.They are not *logically* equivalent. First you have to define what you mean by identity, then you have to define what you mean by an ID, and then you have to decide whether or not to enforce the rule that identity and IDs are 1:1 or not, and if so, under what circumstances. My library card ID may, by coincidence, match your drivers licence ID. Doesn't mean we're the same person. Entities may share identities, or may have many identities. The Borg design pattern, for example, would be an excellent candidate for ID:identity being treated as many-to-one. Even if you decide that treating IDs as 1:1 is the only thing that makes sense in your philosophy, in practice that does not hold for Python. IDs may be reused by Python. There are circumstances where different objects get the same ID. Hence, comparing IDs is not equivalent to identity testing. But I was actually referring to something more fundamental than that. The statement "a is b" is a *direct* statement of identity. "John is my father." "id(a) == id(b)" is *indirect*: "The only child of John's grandfather is the parent of the mother-in-law of my sister-in-law" sort of thing. (Excuse me if I got the relationships mixed up.)>> 4) The id() idiom already breaks if you replace names a, b with >> expressions: >> >> >>> id([1,2]) == id([3,4]) >> True > > It's not broken at all.It is broken in the sense that "id(a) == id(b)" is to be treated as equivalent to "a is b". The above example demonstrates that you CANNOT treat them as equivalent. [...] > The other solution is to do what Java and C# do: banish id() entirely Solution to *what problem*? I do not believe that there is a problem here that needs to be solved. Both id() and "is" are perfectly fine for what they do.>> But that's absolutely wrong. id(x) returns an ID, not an address. It >> just >> happens that, as an accident of implementation, the CPython interpreter >> uses the object address as an ID, because objects can't move. That's >> not the case for all implementations. In Jython, objects can move and >> the address is not static, and so IDs are assigned on demand starting >> with 1: >> >> steve@runes:~$ jython >> Jython 2.5.1+ (Release_2_5_1, Aug 4 2010, 07:18:19) [OpenJDK Client VM >> (Sun Microsystems Inc.)] on java1.6.0_18 Type "help", "copyright", >> "credits" or "license" for more information.>>> id(42) 1 >> >>> id("Hello World!") >> 2 >> >>> id(None) >> >> 3 >> >> > An address is an identifier: a number that I can use to access a > value[1].Then by your own definition, Python's id() does not return an address, since you cannot use it to access a value.
On Apr 26, 5:10 am, Steven D'Aprano <steve wrote: > On Wed, 25 Apr 2012 20:50:21 -0700, Adam Skutt wrote: > > On Apr 25, 8:01 pm, Steven D'Aprano <steve > > wrote: > >> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: > >> > Though, maybe it's better to use a different keyword than 'is' > >> > though, due to the plain English > >> > connotations of the term; I like 'sameobj' personally, for whatever > >> > little it matters. Really, I think taking away the 'is' operator > >> > altogether is better, so the only way to test identity is: > >> > id(x) == id(y) > > >> Four reasons why that's a bad idea: > > >> 1) The "is" operator is fast, because it can be implemented directly by > >> the interpreter as a simple pointer comparison (or equivalent). The > >> id() idiom is slow, because it involves two global lookups and an > >> equality comparison. Inside a tight loop, that can make a big > >> difference in speed. > > > The runtime can optimize the two operations to be equivalent, since they > > are logically equivalent operations. If you removed 'is', there's > > little reason to believe it would do otherwise. > > I'm afraid you are mistaken there. *By design*, Python allows shadowing > and monkey-patching of built-ins. (Although not quite to the same degree > as Ruby, and thank goodness!) >Yes, I understand that. You still haven't explained why this behavior is correct in this particular situation. Arguing from the position of, "What Python does must be correct" isn't a valid tactic, I'm afraid.> It's useful for the same reason that shadowing any other builtin is > useful. id() isn't special enough to complicate the simple, and > effective, execution model just to satisfy philosophers.If overriding id() is useful, then overriding 'is' must be useful too. Python is still broken. Unless you can prove the two operations shouldn't be logically equivalent (and you don't), you can't meaningfully argue for different semantics for them. You still end up with a broken language either way.> They are not *logically* equivalent. First you have to define what you > mean by identity, then you have to define what you mean by an ID, and > then you have to decide whether or not to enforce the rule that identity > and IDs are 1:1 or not, and if so, under what circumstances.You're going to have to explain the value of an "ID" that's not 1:1 with an object's identity, for at least the object's lifecycle, for a programmer. If you can't come up with a useful case, then you haven't said anything of merit. Plainly, to show they're not logically equivalent, you need to explain why the guarantee provided by id() is improper. Then, you need to generalize it to all programming languages. Python's concept of identity is not unique nor special. It uses the exact same rules as C+ +, C#, Java, and many other languages.> My library > card ID may, by coincidence, match your drivers licence ID. Doesn't mean > we're the same person.I don't know why you even remotely think this is relevant. All it does is further demonstrate that you don't understand the object- oriented concept of identity at all. Comparing library IDs and drivers license IDs is an improper operation. Languages that can have multiple IDs, possibly overlapping, /do/ disallow such idiocy.> identities. The Borg design pattern, for example, would be an excellent > candidate for ID:identity being treated as many-to-one.How would inheritance work if I did that?> Even if you decide that treating IDs as 1:1 is the only thing that makes > sense in your philosophy, in practice that does not hold for Python. IDs > may be reused by Python.They may be reused in all languages I can think of. They're only unique for the lifetime of the object, because that's all we need as programmers.> There are circumstances where different objects > get the same ID. Hence, comparing IDs is not equivalent to identity > testing.Two objects only get the same ID if one of the objects is dead. The results of such an comparison are obviously meaningless. Some runtimes even try very hard to prevent you from doing such silly things.> > But I was actually referring to something more fundamental than that. The > statement "a is b" is a *direct* statement of identity. "John is my > father." "id(a) == id(b)" is *indirect*: "The only child of John's > grandfather is the parent of the mother-in-law of my sister-in-law" sort > of thing. (Excuse me if I got the relationships mixed up.)Again, the fact that you somehow think this absurd family tree is relevant only shows you're fundamentally confused about what object oriented identity means. That's rather depressing, seeing as I've given you a link to the definition. In a mathematical sense, you're saying that given f(x) = x+2, using f(x) is somehow more "direct" (whatever the hell that even means) than using 'x+2'. That's just not true. We freely and openly interchange them all the time doing mathematics. Programming is no different.> It is broken in the sense that "id(a) == id(b)" is to be treated as > equivalent to "a is b". The above example demonstrates that you CANNOT > treat them as equivalent.They give different results, yes, and that's unfortunate. However, my code clearly demonstrates they are logically equivalent operations: the temporaries have different addresses in these cases. Both expressions return the correct answer based on their inputs. Since there are side-effects involved here, you cannot simply compare the results and conclude they are different. You must account for the side-effects, and when you do, then we conclude they have the same semantics.> Solution to *what problem*? >This confusion that many people have over what 'is' does, including yourself.> > An address is an identifier: a number that I can use to access a > > value[1]. > > Then by your own definition, Python's id() does not return an address, > since you cannot use it to access a value.The fact Python lacks explicit dereferencing doesn't change the fact that id() returns an address. Replace 'can' with 'could' or 'could potentially' or the whole phrase with 'represents' if you wish. It's a rather pointless thing to quibble over. Would you call the result of casting a C pointer to an int an address? If so, you must call the result of id() an address as well-- you can't dereference either of them. If not, then you need to provide an alternate name for the result of casting a C pointer to an int. Adam
On Thu, Apr 26, 2012 at 9:42 PM, Adam Skutt wrote:
> Would you call the result of casting a C pointer to an int an
> address? If so, you must call the result of id() an address as well--
> you can't dereference either of them. If not, then you need to
> provide an alternate name for the result of casting a C pointer to an
> int.It's an address. You can cast it back to pointer and then dereference
it. But you're talking about C, which is portable assembly language.
When I write C code, I expect to be able to shoot myself in the foot
in numerous ways. With Python, you have to actually work at it a bit,
like:
len = str
Okay, that wasn't very hard, but still, it's not like dereferencing an
uninitialized pointer!
But all this is predicated on a few things:
1) The CPU addresses memory by numbers of a certain size.
2) The C declaration "int *ptr" represents an address
3) The C declaration "(int)ptr" turns that address into an integer
that's big enough to store it
Assuming #3 to be correct is a major cause of trouble, but let's
suppose for the moment that it is. What we have is a direct 1:1
relationship between pointers, integers, and object identities. The
fact is, though, that Python does not ever guarantee this.
Side point: In Python 2, id() returns an int, not a long. Is it
possible to be running Python on a 64-bit machine with a 32-bit int
type? And if so, what does CPython do? Return the address modulo 4G?
Because that could result in collisions.
ChrisA
On 4/26/12 12:56 PM, Chris Angelico wrote: > Side point: In Python 2, id() returns an int, not a long. Is it > possible to be running Python on a 64-bit machine with a 32-bit int > type?Yes. Win64 has 64-bit pointers and 32-bit C longs (and thus 32-bit Python ints). > And if so, what does CPython do? Return the address modulo 4G? It returns a Python long. Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information.>>> import sys >>> sys.maxint2147483647 >>> id('Hello') 30521584L
On Thu, Apr 26, 2012 at 10:12 PM, Robert Kern wrote:
> Yes. Win64 has 64-bit pointers and 32-bit C longs (and thus 32-bit Python
> ints).
>
> It returns a Python long.Ah, that solves that one. Definite improvement in Python 3 with the
merging of the two types, though. Machine integers are simply a
performance optimization for small values.
ChrisA
On 26 April 2012 12:42, Adam Skutt wrote: > On Apr 26, 5:10 am, Steven D'Aprano <steve > wrote: >> On Wed, 25 Apr 2012 20:50:21 -0700, Adam Skutt wrote: >> > On Apr 25, 8:01 pm, Steven D'Aprano <steve >> > wrote: >> >> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: >> >>> [Sterile pedantry] >> >> [More sterile pedantry] >> > [Yet more sterile pedantry] >> [And more] > [Record breaking levels of sterile pedantry]Please stop! Steven, I've learnt a lot from your posts on this list over the years, but too often you spoil it with your compulsion to have the last word on every argument you get involved in at any cost. Some arguments aren't worth winning...
On Apr 26, 4:42 pm, Adam Skutt wrote: > > In a mathematical sense, you're saying that given f(x) = x+2, using > f(x) is somehow more "direct" (whatever the hell that even means) than > using 'x+2'. That's just not true. We freely and openly interchange > them all the time doing mathematics. Programming is no different.If f(x) and x+2 are freely interchangeable then you have referential transparency, a property that only purely functional languages have. In python:>>> a = [1,2] >>> m1 = [a, a] >>> m2 = [[1,2],[1,2]]may make m1 and m2 seem like the same until you assign to m1[0][0]. eg One would not be able to distinguish m1 and m2 in Haskell. On the whole I whole-heartedly agree that 'a is b' be replaced by id(a) == id(b), with id itself replaced by something more obviously implementational like addrof. The reasons are fundamental: The word 'is' is arguably the primal existential verb. Whereas 'is' in python is merely a leakage of implementation up to the programmer level: http://www.joelonsoftware.com/articles/LeakyA... Such a leakage may be justified just as C allowing inline asm may be justified. However dignifying such a leakage with the primal existential verb causes all the confusions seen on this thread (and the various stackoverflow questions etc).
On Apr 26, 10:18 am, rusi wrote:
> On Apr 26, 4:42 pm, Adam Skutt wrote:
>
>
>
> > In a mathematical sense, you're saying that given f(x) = x+2, using
> > f(x) is somehow more "direct" (whatever the hell that even means) than
> > using 'x+2'. That's just not true. We freely and openly interchange
> > them all the time doing mathematics. Programming is no different.
>
> If f(x) and x+2 are freely interchangeable then you have referential
> transparency, a property that only purely functional languages have.
> In python:I think you misunderstood what I was trying to explain. Steven is
trying to claim that there's some sort of meaningful difference
between calling an operation/algorithm/function by some name versus
handing out its definition. I was merely pointing out that we
routinely substitute the two when it is appropriate to do so.
My apologies if you somehow took that to mean that I was implying
there was referential transparency here. I couldn't think of a better
example for what I was trying to say.
Adam
On Apr 26, 7:44 pm, Adam Skutt wrote:
> On Apr 26, 10:18 am, rusi wrote:
>
> > On Apr 26, 4:42 pm, Adam Skutt wrote:
>
> > > In a mathematical sense, you're saying that given f(x) = x+2, using
> > > f(x) is somehow more "direct" (whatever the hell that even means) than
> > > using 'x+2'. That's just not true. We freely and openly interchange
> > > them all the time doing mathematics. Programming is no different.
>
> > If f(x) and x+2 are freely interchangeable then you have referential
> > transparency, a property that only purely functional languages have.
> > In python:
>
> I think you misunderstood what I was trying to explain. Steven is
> trying to claim that there's some sort of meaningful difference
> between calling an operation/algorithm/function by some name versus
> handing out its definition. I was merely pointing out that we
> routinely substitute the two when it is appropriate to do so.
>
> My apologies if you somehow took that to mean that I was implying
> there was referential transparency here. I couldn't think of a better
> example for what I was trying to say.
>
> AdamAnd my apologies... I forgot to state my main point:
Programmer accessible object identity is the principal impediment to
referential transparency.
In a functional language one can bind a name to a value -- period.
There is nothing more essence-ial -- its platonic id -- to the name
than that and so the whole can of worms connected with object identity
remains sealed within the language implementation.
On Apr 26, 1:34 pm, rusi wrote:
> On Apr 26, 7:44 pm, Adam Skutt wrote:
> > On Apr 26, 10:18 am, rusi wrote:
>
> > > On Apr 26, 4:42 pm, Adam Skutt wrote:
>
> > > > In a mathematical sense, you're saying that given f(x) = x+2, using
> > > > f(x) is somehow more "direct" (whatever the hell that even means) than
> > > > using 'x+2'. That's just not true. We freely and openly interchange
> > > > them all the time doing mathematics. Programming is no different.
>
> > > If f(x) and x+2 are freely interchangeable then you have referential
> > > transparency, a property that only purely functional languages have.
> > > In python:
>
> > I think you misunderstood what I was trying to explain. Steven is
> > trying to claim that there's some sort of meaningful difference
> > between calling an operation/algorithm/function by some name versus
> > handing out its definition. I was merely pointing out that we
> > routinely substitute the two when it is appropriate to do so.
>
> > My apologies if you somehow took that to mean that I was implying
> > there was referential transparency here. I couldn't think of a better
> > example for what I was trying to say.
>
> > Adam
>
> And my apologies... I forgot to state my main point:
> Programmer accessible object identity is the principal impediment to
> referential transparency.
> In a functional language one can bind a name to a value -- period.
> There is nothing more essence-ial -- its platonic id -- to the name
> than that and so the whole can of worms connected with object identity
> remains sealed within the language implementation.Yes, I agree that object identity is a major hold up, but I think side
effects are a bigger problem. It's possible in C++ to create types
that behave like the primitive types without too much difficulty,
hence making object identity unimportant. However, it's considerably
more difficult in C++ to write side-effect free code[1]. This is a
bit of an apple and orange thing, though. ;)
I often wonder what the world would be like if Python, C#, and Java
embraced value types more, and had better support for pure functions.
Unfortunately, building a language where all types behave like that is
rather difficult, as the Haskell guys have shown us ;).
Adam
[1] Or even just code that only uses side-effects the compiler
understands.
On Thu, 26 Apr 2012 12:22:55 -0700, Adam Skutt wrote: > I often wonder what the world would be like if Python, C#, and Java > embraced value types more, and had better support for pure functions.They would be slower, require more memory, harder to use, and far, far less popular. Some other languages just like Python, C# and Java would be invented to fill those niches, and the functional-obsessed crowd would then complain that they wished those languages would be more like Python, C# and Java.
On Apr 26, 7:33 pm, Steven D'Aprano <steve
wrote:
> On Thu, 26 Apr 2012 12:22:55 -0700, Adam Skutt wrote:
> > I often wonder what the world would be like if Python, C#, and Java
> > embraced value types more, and had better support for pure functions.
>
> They would be slower, require more memory,Funny, Haskell frequently beats C in both categories. MATLAB is
faster and more memory efficient than naive C matrix code, since it
has a very efficient copy-on-write implementation. As the various C++
matrix libraries will show you, efficient COW is much harder when you
have to deal with C++ aliasing rules.
> harder to use, and far, far less popular.
Alas, these two are probably true.
Adam
Adam Skutt writes:
>> harder to use, and far, far less popular.
> Alas, these two are probably true.Haskell is kind of abstruse and has a notoriously steep learning curve,
as it's mostly meant as a research testbed and as a playground for
language geeks. ML/OCaml is by all accounts much easier, and I know of
a couple of former Python projects that successfully migrated to OCaml
once Python's warts and low performance got too annoying. Erlang (which
is functional but untyped) has also been displacing Python in some
settings.
On Thu, 26 Apr 2012 17:16:10 -0700, Adam Skutt wrote: > On Apr 26, 7:33 pm, Steven D'Aprano <steve > wrote: >> On Thu, 26 Apr 2012 12:22:55 -0700, Adam Skutt wrote: >> > I often wonder what the world would be like if Python, C#, and Java >> > embraced value types more, and had better support for pure functions. >> >> They would be slower, require more memory, > > Funny, Haskell frequently beats C in both categories.We've both been guilty of this, but don't confuse a language implementation with a language. Haskell and C are languages, which in a sense are like Platonic ideals: languages specify behaviour and semantics, but have no costs. When talking about resource usage, you need to talk about concrete implementations of concrete tests, not hand-wavy "language X is faster". And I'm afraid that your claim of Haskell frequently beating C doesn't stand up to scrutiny. http://shootout.alioth.debian.org/u64q/benchm... I'm seeing code generated by the Haskell GHC compiler being 2-4 times slower than code from the C gcc compiler, and on average using 2-3 times as much memory (and as much as 7 times). Feel free to find your own set of benchmarks that show the opposite. I'd be interested to see under what conditions Haskell might be faster than C.
On Fri, Apr 27, 2012 at 12:47 PM, Steven D'Aprano
wrote:
> On Thu, 26 Apr 2012 17:16:10 -0700, Adam Skutt wrote:
>
>> On Apr 26, 7:33 pm, Steven D'Aprano <steve
>> wrote:
>>> On Thu, 26 Apr 2012 12:22:55 -0700, Adam Skutt wrote:
>>> > I often wonder what the world would be like if Python, C#, and Java
>>> > embraced value types more, and had better support for pure functions.
>>>
>>> They would be slower, require more memory,
>>
>> Funny, Haskell frequently beats C in both categories.
>
> I'm seeing code generated by the Haskell GHC compiler being 2-4 times
> slower than code from the C gcc compiler, and on average using 2-3 times
> as much memory (and as much as 7 times).Also, I don't see a Python interpreter written in Haskell that's
outperforming CPython.
ChrisA
Steven D'Aprano writes: > I'm seeing code generated by the Haskell GHC compiler being 2-4 times > slower than code from the C gcc compiler, and on average using 2-3 times > as much memory (and as much as 7 times).Alioth isn't such a great comparison, because on the one hand you get very carefully tuned, unidiomatic code for each language; but on the other, you're somewhat constrained by the benchmark specs. Obviously C is not much above assembler, so you can write almost-optimal programs if you code close enough to the metal and suffer enough. If you're talking about coding reasonably straightforwardly, C usually does beat Haskell (once you've debugged the core dumps...) but there are exceptions to that.> Feel free to find your own set of benchmarks that show the opposite. I'd > be interested to see under what conditions Haskell might be faster than C.Haskell wasn't included in this multi-way comparison, but Ocaml beat C by a significant factor at a straightforward vector arithmetic loop, because it didn't have to pessimize around possible pointer aliasing: http://scienceblogs.com/goodmath/2006/11/the_... GHC should be able to do similar things. Also, here's a sort of cheating Haskell example: the straightforward Haskell Fibonacci code is slower than C, but just sprinkle in a few parallelism keywords and run it on your quad core cpu: http://donsbot.wordpress.com/2007/11/29/use-t... Note the Haskell code in that example is using arbitrary-precision integers while C is using int64's. Yes, you could beat the GHC speed by writing a lot more C code to manage Posix threads, locks, etc., but in Haskell two do two things in parallel you can just say "par". There is also work going on to support parallel listcomps (just like regular ones but they run on multiple cores), and vector combinators that offload the computation to a GPU. Those things are quite hard to do in plain C, though there are some specialty libraries for it. Finally, a less-cheating example (this is from 2007 and I think things are even better now): http://neilmitchell.blogspot.com/2007/07/maki... Gives a Haskell word count program main = print . length . words =<< getContents which could also be written (if the syntax looks better to you): main = do text <- getContents print (length (words text)) The comparison C code is: int main() { int i = 0; int c, last_space = 1, this_space; while ((c = getchar()) != EOF) { this_space = isspace(c); if (last_space && !this_space) i++; last_space = this_space; } printf("%i\n", i); return 0; } and GHC/Supero beats the C code by about 10% even though both use getchar. The blog post explains, you could speed up the C code by writing a rather contorted version, unrolling it into two separate loops, one for sequences of spaces and one for non-spaces, and jumping back and forth between the loops instead of using the last_space variable. That is basically the code that Supero figures out how to generate: two separate loops with transitions in the right places, starting from very straightforward high-level input. I'm not really good at Haskell even after fooling with it on and off for several years now, and it certainly can't beat Python for ease-of-use without a lot of experience. But in the hands of experts it is incredibly powerful. It makes Python seem almost like a toy.
On Thu, Apr 26, 2012 at 7:33 PM, Steven D'Aprano wrote: > On Thu, 26 Apr 2012 12:22:55 -0700, Adam Skutt wrote: > >> I often wonder what the world would be like if Python, C#, and Java >> embraced value types more, and had better support for pure functions. > > They would be slower, require more memory, harder to use, and far, far > less popular.That's odd. PyPy supports value types and pure functions as a performance optimization. For reference, see e.g. http://morepypy.blogspot.ca/2011/03/controlli... http://morepypy.blogspot.ca/2011/08/visualiza... PyPy can detect some pure functions (or purity annotations) and can run them at (JIT-)compile-time to speed up the runtime. Also, JIT'd code uses unboxed values, rather than objects. (So it can unbox something once, then do all the arithmetic, then box again). (Of course, this is only a special case of a value type, as far as I understand the term. But hey!) Also somebody else was talking about Haskell being crazy fast, but if you want a really fast functional language, take a look at ATS (an ML variant). (Also pay close attention to the "bytes of code" comparison -- it's rare one sees a language more verbose than C) http://shootout.alioth.debian.org/u64q/benchm... -- Devin
On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > You're going to have to explain the value of an "ID" that's not 1:1 with > an object's identity, for at least the object's lifecycle, for a > programmer. If you can't come up with a useful case, then you haven't > said anything of merit.I gave an example earlier, but you seem to have misunderstood it, so I'll give more detail. In the Borg design pattern, every Borg instance shares state and are indistinguishable, with only one exception: object identity. We can distinguish two Borg instances by using "is". Since the whole point of the pattern is for Borg instances to be indistinguishable, the existence of a way to distinguish Borg instances is a flaw and may be undesirable. At least, it's exposing an implementation detail which some people argue should not be exposed. Why should the caller care whether they are dealing with a singleton object or an unspecified number of Borg objects all sharing state? A clever interpreter could make many Borg instances appear to be a singleton. A really clever one could also make a singleton appear to be many Borg instances. Note that this is virtually the same situation as that which John Nagle objects to, namely that the implementation detail of small ints being singletons is exposed. There is only ever one 0 instance, but potentially many 3579 instances. John's argument is that Python should raise an exception if you compare "2 is 2", or for that matter "3579 is 3579", which is foolish. If you're going to change the semantics of "is", why not do something useful and ensure that "3579 is 3579" returns True regardless of whether they actually are the same instance or not? That would be far more useful than raising an exception. It would complicate the definition of "is", but perhaps that's a price people are willing to pay for avoiding the (trivial) confusion about object identity. [...]>> identities. The Borg design pattern, for example, would be an excellent >> candidate for ID:identity being treated as many-to-one. > > How would inheritance work if I did that?You don't inherit from Borg instances, and instances inherit from their class the same as any other instance.
On Apr 27, 12:56 pm, Steven D'Aprano <steve wrote: > On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > > You're going to have to explain the value of an "ID" that's not 1:1 with > > an object's identity, for at least the object's lifecycle, for a > > programmer. If you can't come up with a useful case, then you haven't > > said anything of merit. > > I gave an example earlier, but you seem to have misunderstood it, so I'll > give more detail. > > In the Borg design pattern, every Borg instance shares state and are > indistinguishable, with only one exception: object identity. We can > distinguish two Borg instances by using "is". > > Since the whole point of the pattern is for Borg instances to be > indistinguishable, the existence of a way to distinguish Borg instances > is a flaw and may be undesirable. At least, it's exposing an > implementation detail which some people argue should not be exposed. >Then people should stop with such idiocy like the Borg pattern. It's a bad example from an even worse idea.> Why should the caller care whether they are dealing with a singleton > object or an unspecified number of Borg objects all sharing state? A > clever interpreter could make many Borg instances appear to be a > singleton. A really clever one could also make a singleton appear to be > many Borg instances.Trivial: to break cyclical references in a deep copy operation.> John's argument is that Python should raise an exception if you compare > "2 is 2", or for that matter "3579 is 3579", which is foolish. > >> identities. The Borg design pattern, for example, would be an excellent > >> candidate for ID:identity being treated as many-to-one. > > > How would inheritance work if I did that? > > You don't inherit from Borg instances, and instances inherit from their > class the same as any other instance.I think you misunderstood me. Define a Borg class where somehow identity is the same for all instances. Inherit from that class and add per-instance members. Now, identity can't be the same for all instances. As a result, you've just violated the Liskov Substituion Principal: code that relies on all Borg class instances having the same identity will fail when passed an instance of the subclass. It's impossible to combine identities and not violate LSP, unless you forbid subclasses. Your idea violates one of the most fundamental tenants of object-oriented programming. This is because object identity is one of the fundamental consequences of object-oriented programming. You can't do away with it, and any attempt to do so really just suggests that you don't understand OOP at all. Adam
On Sat, Apr 28, 2012 at 3:33 AM, Adam Skutt wrote:
> I think you misunderstood me. Define a Borg class where somehow
> identity is the same for all instances. Inherit from that class and
> add per-instance members. Now, identity can't be the same for all
> instances. As a result, you've just violated the Liskov Substituion
> Principal: code that relies on all Borg class instances having the
> same identity will fail when passed an instance of the subclass.Why would you subclass a Borg?
ChrisA
On Fri, Apr 27, 2012 at 11:38 AM, Chris Angelico wrote:
> On Sat, Apr 28, 2012 at 3:33 AM, Adam Skutt wrote:
>> I think you misunderstood me. Define a Borg class where somehow
>> identity is the same for all instances. Inherit from that class and
>> add per-instance members. Now, identity can't be the same for all
>> instances. As a result, you've just violated the Liskov Substituion
>> Principal: code that relies on all Borg class instances having the
>> same identity will fail when passed an instance of the subclass.
>
> Why would you subclass a Borg?For the same reasons one might want to subclass a Singleton. One of
the big advantages of Borg is that it makes this easier, and it also
allows the option of either having instances of subclasses share the
same state as instances of base classes (usually preferred, because it
preserves LSP) or of allowing each individual subclass to have its own
unique shared state.
On Fri, Apr 27, 2012 at 11:33 AM, Adam Skutt wrote: > On Apr 27, 12:56 pm, Steven D'Aprano <steve > wrote: >> On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: >> > You're going to have to explain the value of an "ID" that's not 1:1 with >> > an object's identity, for at least the object's lifecycle, for a >> > programmer. If you can't come up with a useful case, then you haven't >> > said anything of merit. >> >> I gave an example earlier, but you seem to have misunderstood it, so I'll >> give more detail. >> >> In the Borg design pattern, every Borg instance shares state and are >> indistinguishable, with only one exception: object identity. We can >> distinguish two Borg instances by using "is". >> >> Since the whole point of the pattern is for Borg instances to be >> indistinguishable, the existence of a way to distinguish Borg instances >> is a flaw and may be undesirable. At least, it's exposing an >> implementation detail which some people argue should not be exposed. >> > > Then people should stop with such idiocy like the Borg pattern. It's a > bad example from an even worse idea.I would argue exactly the opposite. One issue I have with the Singleton pattern is that the very fact that a class is a singleton (or a borg) is an implementation detail. Shared identity exposes that implementation detail, which I consider a flaw. Borg, OTOH, actually gets this right.>> Why should the caller care whether they are dealing with a singleton >> object or an unspecified number of Borg objects all sharing state? A >> clever interpreter could make many Borg instances appear to be a >> singleton. A really clever one could also make a singleton appear to be >> many Borg instances. > > Trivial: to break cyclical references in a deep copy operation.The cycle will break anyway, since there are only a finite number of instances. You just might end up with as many copy instances as there were original instances in the structure. But going back to Steven's point here, that fact should not be important to the caller.> I think you misunderstood me. Define a Borg class where somehow > identity is the same for all instances.Why? The whole point of Borg is to share state, not identity. If you really want shared identity, use a Singleton.> Inherit from that class and > add per-instance members. Now, identity can't be the same for all > instances. As a result, you've just violated the Liskov Substituion > Principal: code that relies on all Borg class instances having the > same identity will fail when passed an instance of the subclass.That has nothing to do with Borg. That has to do with having misguidedly defined identity to be the same for all instances. You wouldn't do that for a regular class. Why force it on a Borg class?
On Fri, 27 Apr 2012 10:33:34 -0700, Adam Skutt wrote: >> Why should the caller care whether they are dealing with a singleton >> object or an unspecified number of Borg objects all sharing state? A >> clever interpreter could make many Borg instances appear to be a >> singleton. A really clever one could also make a singleton appear to be >> many Borg instances. > > Trivial: to break cyclical references in a deep copy operation.I asked why the *caller* should care. If the caller has to break cyclical references manually, the garbage collector is not doing its job. If you're going to propose underpowered or buggy environments as an objection, then I'll simply respond that I'm not talking about any specific (underpowered or buggy) implementation, I'm talking about what is logically possible. [...]>> > How would inheritance work if I did that? >> >> You don't inherit from Borg instances, and instances inherit from their >> class the same as any other instance. > > I think you misunderstood me. Define a Borg class where somehow > identity is the same for all instances. Inherit from that class and add > per-instance members.I think that if you're talking about per-instance members of a Borg class, you're confused as to what Borg means. Since all instances share state, you can't have *per-instance* data.> Now, identity can't be the same for all > instances. As a result, you've just violated the Liskov Substituion > Principal: code that relies on all Borg class instances having the same > identity will fail when passed an instance of the subclass.Not at all. Obviously each Borg subclass will have it's own fake identity. Code that requires instances of different types to be identical is fundamentally broken, since the mere fact that they are different types means they can't be identical. I'll accept the blame for your confusion as I glossed over something which I thought was obvious, but clearly wasn't. When I said that Borg instances are indistinguishable except for identity, I thought that was obvious that I was talking about instances of a single type. Mea culpa. Clearly if x is an instance of Borg, and y is an instance of BorgSubclass, you can distinguish them by looking at the type. The point is that you shouldn't be able to distinguish instances of a single type.> It's impossible to combine identities and not violate LSP, unless you > forbid subclasses. Your idea violates one of the most fundamental > tenants of object-oriented programming. This is because object identity > is one of the fundamental consequences of object-oriented programming. > You can't do away with it, and any attempt to do so really just suggests > that you don't understand OOP at all.Oh please, enough of the religion of LSP. Barbara Liskov first introduced this idea in 1987, twenty years after Simula 67 first appeared and thirty years after MIT researchers came up with the concept of object oriented programming. That's hardly fundamental to the concept of OOP. People have, and still do, violate LSP all the time. LSP may be best practice but it's hardly essential. OOP was useful before LSP and it will remain useful in the face of violations. Besides: - In real life, subtypes often violate LSP. An electric car is a type of car, but it has no petrol tank. Wolf spiders have eyes, except for the Kauaʻi cave wolf spider, which is is a subtype of wolf spider but is completely eyeless. - Subclasses in Eiffel are not necessarily subtypes and may not be substitutable for superclasses. If it's good enough for Eiffel, it's good enough for my hypothetical Borg subclasses. You can always declare that Bertrand Meyer doesn't "understand OOP at all" too.
On Apr 27, 2:40 pm, Steven D'Aprano <steve wrote: > On Fri, 27 Apr 2012 10:33:34 -0700, Adam Skutt wrote: > >> Why should the caller care whether they are dealing with a singleton > >> object or an unspecified number of Borg objects all sharing state? A > >> clever interpreter could make many Borg instances appear to be a > >> singleton. A really clever one could also make a singleton appear to be > >> many Borg instances. > > > Trivial: to break cyclical references in a deep copy operation. > > I asked why the *caller* should care. If the caller has to break cyclical > references manually, the garbage collector is not doing its job.It's a necessary requirement to serialize any cyclical structure. Garbage collection has nothing to do with it. If I have some structure such that A --> B --> A, I need to be able to determine that I've seen 'A' before in order to serialize the structure to disk, or I will never write it out successfully. There are plenty of situations where we legitimately care whether two pointers are the same and don't give one whit about the state of objects they point to. You cannot conflate the two tests, and that's precisely what your 'give all borg instances the same identity' idea does.> I think that if you're talking about per-instance members of a Borg > class, you're confused as to what Borg means.I'm not. I'm talking about per-instance members of a subclass of a Borg class. There's nothing about the Borg pattern that forbids such behavior, which is one of the reasons it's such a terrible idea in general. Borg implies promises that it cannot readily keep. > Since all instances share state, you can't have *per-instance* data. I most certainly can do so in a subclass. Shared state in a parent doesn't mandate shared state in a child.> Not at all. Obviously each Borg subclass will have it's own fake > identity. > When I said that Borg instances are indistinguishable except for > identity, I thought that was obvious that I was talking about instances > of a single type. Mea culpa. > > Clearly if x is an instance of Borg, and y is an instance of > BorgSubclass, you can distinguish them by looking at the type. The point > is that you shouldn't be able to distinguish instances of a single type.No, that's not the least bit obvious nor apparent, and it still violates LSP. It means every function that takes a Borg as an argument must know about every subclass in order to distinguish between them. The serialization function above would need to do so. Imagine an object x that holds a Borg object and a BorgSubclass object. If the serialization function keeps a list of objects it has seen before and uses that to determine whether to write the object out, it will fail to write out one or the other if we implemented your harebrained 'All Borg objects have the same identity' idea. Your idea means that 'x.borg is x.subborg' must return True. It also means either x.borg isn't going to be written out, or x.subborg isn't going to be written out. The program is broken. If you modify your idea to ignore subtypes, than this function breaks: def write_many(value, channel1, channel2): channel1.write(value) if channel2 is not channel1: channel2.write(value) Calling write_many("foo", x.borg, x.subborg) now gives different behavior than write_many("foo", x.borg, x.borg). That's probably not what the programmer intended! Like it or not, whether you have only one object with shared state or infinite objects with the same shared state is not an implementation detail. Just because you write code that doesn't care about that fact does not make it an implementation detail. I can write code that depends on that fact, and there's not a single thing you can do to stop me. This is why the Borg pattern is a bad idea in general, because it encourages programmers to write code that is subtly wrong. If you have a Borg class, you can't ignore the fact that you have multiple objects even if you want to do so. You will eventually end up writing incorrect code as a result. Yet, many people try to do precisely that, your idea is attempting to do precisely that!> Oh please, enough of the religion of LSP. > > Barbara Liskov first introduced this idea in 1987, twenty years after > Simula 67 first appeared and thirty years after MIT researchers came up > with the concept of object oriented programming. That's hardly > fundamental to the concept of OOP. > People have, and still do, violate LSP all the time.People write code with security flaws all of the time too. This doesn't even being to approach being an reasonable argument. It's completely disingenuous. People come up with ideas and fail to properly formalize them all of the time. People come up with useful, revolutionary ideas and get parts of them wrong all of the time. If you violate LSP, then you enable interface users to write buggy code. Correct class hierarchies must follow it; correct interface implementations must follow it. There's nothing optional about it, it even applies even if you don't have objects at all. It's just extra inescapable for the sort of class hierarchies most OOP languages use.> Besides: > > - In real life, subtypes often violate LSP. An electric car is a type of > car, but it has no petrol tank. Wolf spiders have eyes, except for the > Kauaʻi cave wolf spider, which is is a subtype of wolf spider but is > completely eyeless.Yes, real life is more complicated than the simplistic relationships that most class hierarchies support. So what? Where did I advocate encoding such relationships using class hierarchies? Adam
Adam Skutt writes: > On Apr 27, 12:56 pm, Steven D'Aprano <steve > wrote: > > On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > > > Steven D'Aprano wrote: > > >> The Borg design pattern, for example, would be an excellent > > >> candidate for ID:identity being treated as many-to-one. > > > > > How would inheritance work if I did that? > > > > You don't inherit from Borg instances, and instances inherit from > > their class the same as any other instance. > > I think you misunderstood me. Define a Borg class where somehow > identity is the same for all instances.The resulting class would not follow the Borg pattern, so it's not a Borg class. Remember that Borg names a design pattern, so if you've got a class that doesn't follow that pattern then a complaint that it doesn't keep the promises of the Borg pattern is merely tautological.
On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > On Apr 26, 5:10 am, Steven D'Aprano <steve > wrote: >> But I was actually referring to something more fundamental than that. >> The statement "a is b" is a *direct* statement of identity. "John is my >> father." "id(a) == id(b)" is *indirect*: "The only child of John's >> grandfather is the parent of the mother-in-law of my sister-in-law" >> sort of thing. (Excuse me if I got the relationships mixed up.) > > Again, the fact that you somehow think this absurd family tree is > relevant only shows you're fundamentally confused about what object > oriented identity means. That's rather depressing, seeing as I've given > you a link to the definition.Perhaps you failed to notice that this "absurd" family tree, as you put it, consists of grandparent+parent+sibling+in-law. What sort of families are you familiar with that this seems absurd to you? I think you have inadvertently demonstrated the point I am clumsily trying to make. Even when two expressions are logically equivalent, the form of the expressions make a big difference to the comprehensibility of the text. Which would you rather read? for item in sequence[1:]: ... for item in sequence[sum(ord(c) for c in 'avocado') % 183:]: ... The two are logically equivalent, so logically you should have no preference between the two, yes?> In a mathematical sense, you're saying that given f(x) = x+2, using f(x) > is somehow more "direct" (whatever the hell that even means)I thought that the concept of direct and indirect statements would be self-evident. Let me try again. A statement is "direct" in the sense I mean if it explicitly states the thing you intend it to state. A statement is "indirect" if it requires one or more logical steps to go from the statement, as given, to the conclusion intended. "Queen Elizabeth II is the ruling monarch of the United Kingdom" is a direct statement of the fact that Queen Elizabeth II is the ruling monarch of the UK. (Do I really need to explain this?) "Queen Elizabeth II is the Commander-in-chief of the Canadian Armed Forces" is an *indirect* statement of the fact that Elizabeth is the ruling monarch of the UK. It is indirect because it doesn't explicitly say that she is monarch, but the Commander-in-Chief of the Canadian Armed Forces is always the ruling monarch of Canada, and the ruling monarch of Canada is always the ruling monarch of the UK. Hence, Elizabeth being Commander-in-Chief necessarily implies that she is ruling monarch of the United Kingdom (at least until there is change to Canadian law). "a is b" is a direct test of whether a is b. (Duh.) "id(a) == id(b)" is an indirect test of whether a is b, since it requires at least three indirect steps: the knowledge of what the id() function does, the knowledge of what the == operator does, and the knowledge that equal IDs imply identity.
On Apr 27, 1:06 pm, Steven D'Aprano <steve wrote: > On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > > On Apr 26, 5:10 am, Steven D'Aprano <steve > > wrote: > >> But I was actually referring to something more fundamental than that. > >> The statement "a is b" is a *direct* statement of identity. "John is my > >> father." "id(a) == id(b)" is *indirect*: "The only child of John's > >> grandfather is the parent of the mother-in-law of my sister-in-law" > >> sort of thing. (Excuse me if I got the relationships mixed up.) > > > Again, the fact that you somehow think this absurd family tree is > > relevant only shows you're fundamentally confused about what object > > oriented identity means. That's rather depressing, seeing as I've given > > you a link to the definition. > > Perhaps you failed to notice that this "absurd" family tree, as you put > it, consists of grandparent+parent+sibling+in-law. What sort of families > are you familiar with that this seems absurd to you?No, I noticed, but who talks like that? It's not remotely comparable to the sort of difference we're talking about.> > I think you have inadvertently demonstrated the point I am clumsily > trying to make. Even when two expressions are logically equivalent, the > form of the expressions make a big difference to the comprehensibility of > the text.And if we were talking about 30, 20, 5, maybe even 2 line function versus it's name, you might have a point. We're not talking about such things though, and it's pretty disingenuous to pretend otherwise. Yet, that's precisely what you did with your absurd family relationship.> Which would you rather read? > > for item in sequence[1:]: ... > > for item in sequence[sum(ord(c) for c in 'avocado') % 183:]: ... > > The two are logically equivalent, so logically you should have no > preference between the two, yes?No, they're not logically equivalent. The first won't even execute, as sequence is undefined. You need two lines in the first case.> A statement is "direct" in the sense I mean if it explicitly states the > thing you intend it to state.And in the case of the two ways to compare identity, both statements state exactly what I intend to state. They're synonyms.> > "a is b" is a direct test of whether a is b. (Duh.) > > "id(a) == id(b)" is an indirect test of whether a is b, since it requires > at least three indirect steps: the knowledge of what the id() function > does, the knowledge of what the == operator does, and the knowledge that > equal IDs imply identity.The problem is that using 'is' correctly requires understanding all of those three things. Adam
On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > On Apr 26, 5:10 am, Steven D'Aprano <steve > wrote: >> Solution to *what problem*? >> > This confusion that many people have over what 'is' does, including > yourself.I have no confusion over what "is" does. The "is" operator returns True if and only if the two operands are the same object, otherwise it returns False. If you think that "is" does something different, you are wrong.>> > An address is an identifier: a number that I can use to access a >> > value[1]. >> >> Then by your own definition, Python's id() does not return an address, >> since you cannot use it to access a value. > > The fact Python lacks explicit dereferencing doesn't change the fact > that id() returns an address. Replace 'can' with 'could' or 'could > potentially' or the whole phrase with 'represents' if you wish. It's a > rather pointless thing to quibble over.You can't treat id() as an address. Did you miss my post when I demonstrated that Jython returns IDs generated on demand, starting from 1? In general, there is *no way even in principle* to go from a Python ID to the memory location (address) of the object with that ID, because in general objects *may not even have a fixed address*. Objects in Jython don't, because the Java virtual machine can move them in memory. The fact that CPython happens to use the memory address of objects, suitably converted to an int object, is a red-herring. It leads to nothing but confusion.> Would you call the result of casting a C pointer to an int an address? > If so, you must call the result of id() an address as well-- you can't > dereference either of them. If not, then you need to provide an > alternate name for the result of casting a C pointer to an int.I don't need to do anything of the sort. It was *your* definition, not mine. Don't put the responsibility on me for your definition being broken. (And for the record, in C you can cast an integer into a pointer, although the results are implementation-specific. There's no equivalent in Python.)
Steven, your posts are leaking out of their respective thread(s). Is
this intentional?
~Temia
On Apr 27, 1:12 pm, Steven D'Aprano <steve wrote: > On Thu, 26 Apr 2012 04:42:36 -0700, Adam Skutt wrote: > > On Apr 26, 5:10 am, Steven D'Aprano <steve > > wrote: > >> Solution to *what problem*? > > > This confusion that many people have over what 'is' does, including > > yourself. > > I have no confusion over what "is" does.False. If you did, then you would not have suggested the difference in True/False result between "id([1,2]) == id([1, 2])" and "[1, 2] is [1, 2]" matters. You would understand that the result of an identity test with temporary objects is meaningless, since identity is only meaningful while the objects are alive. That's a fundamental mistake.> >> > An address is an identifier: a number that I can use to access a > >> > value[1]. > > >> Then by your own definition, Python's id() does not return an address, > >> since you cannot use it to access a value. > > > The fact Python lacks explicit dereferencing doesn't change the fact > > that id() returns an address. Replace 'can' with 'could' or 'could > > potentially' or the whole phrase with 'represents' if you wish. It's a > > rather pointless thing to quibble over. > > You can't treat id() as an address. Did you miss my post when I > demonstrated that Jython returns IDs generated on demand, starting from > 1? In general, there is *no way even in principle* to go from a Python ID > to the memory location (address) of the object with that ID, because in > general objects *may not even have a fixed address*. Objects in Jython > don't, because the Java virtual machine can move them in memory.Yes, there is a way. You add a function deref() to the language. In CPython, that simply treats the passed value as a memory address and treats it as an object, perhaps with an optional check. In Jython, it'd access a global table of numbers as keys with the corresponding objects as values, and return them. The value of id() is absolutely an address, even in Jython. The fact the values can move about is irrelevant. Again, if this wasn't possible, then you couldn't implement 'is'. Implementing 'is' requires a mechanism for comparing objects that doesn't involve ensuring the contents of the two operands in memory is the same.> > Would you call the result of casting a C pointer to an int an address? > > If so, you must call the result of id() an address as well-- you can't > > dereference either of them. If not, then you need to provide an > > alternate name for the result of casting a C pointer to an int. > > I don't need to do anything of the sort.Yes, you do, because you called such a thing an address when talking about CPython. Even if my definition is wrong (it's not), your definition is wrong too.> (And for the record, in C you can cast an integer into a pointer, > although the results are implementation-specific. There's no equivalent > in Python.)Yes, but the lack of that operation doesn't mean that id() doesn't return an address. Adam
On Sat, Apr 28, 2012 at 3:51 AM, Adam Skutt wrote:
> Yes, there is a way. You add a function deref() to the language. In
> CPython, that simply treats the passed value as a memory address and
> treats it as an object, perhaps with an optional check. In Jython,
> it'd access a global table of numbers as keys with the corresponding
> objects as values, and return them. The value of id() is absolutely
> an address, even in Jython. The fact the values can move about is
> irrelevant.Python already as dereferenceable addresses. Look.
def address(obj,table=[]):
for i,o in enumerate(table):
if obj is o: return i
table.append(obj)
return len(table)-1
def deref(addr):
return address.__defaults__[0][addr]
You can take the address of an object (interning it, effectively), and
later dereference it. Proves nothing.
ChrisA
Adam Skutt wrote: >> You can't treat id() as an address. Did you miss my post when I >> demonstrated that Jython returns IDs generated on demand, starting >> from 1? In general, there is *no way even in principle* to go from >> a Python ID to the memory location (address) of the object with >> that ID, because in general objects *may not even have a fixed >> address*. Objects in Jython don't, because the Java virtual >> machine can move them in memory. > > Yes, there is a way. You add a function deref() to the language.This is getting pretty absurd. By that logic you could say "With Python, you can end all life on earth! You just add a function to the language called nuclear_winter() that remotely accesses warhead launch sites in the US and Russia, enters the appropriate launch codes, and launches the entire nuclear arsenal!"
On Thu, Apr 26, 2012 at 3:10 AM, Steven D'Aprano
wrote:
> But I was actually referring to something more fundamental than that. The
> statement "a is b" is a *direct* statement of identity. "John is my
> father." "id(a) == id(b)" is *indirect*: "The only child of John's
> grandfather is the parent of the mother-in-law of my sister-in-law" sort
> of thing. (Excuse me if I got the relationships mixed up.)I might have used a different example:
1) "John is my father."
2) "John has the same social security number as my father."
The first is concise and clearly expresses the intended statement of identity.
The second could be read to imply any number of things: "John is my
father." "Somebody at the SSA messed up and gave John the same SSN as
my father." "John is an identity thief."
If the assertion I'm trying to express is that John is my father, then
the direct statement #1 is the best way to convey that, at least in
English. Likewise, "a is b" more clearly expresses the intended
comparison than does "id(a) == id(b)".
This thread has already beaten a dead horse enough that the horse came
back as a zombie and was re-killed, but I couldn't help but respond to
this part:On 01/-10/-28163 01:59 PM, Adam Skutt wrote:
> Code that relies on the identity of a temporary object is generally
> incorrect. This is why C++ explicitly forbids taking the address
> (identity) of temporaries.Except that C++ *doesn't* really forbid taking the address of a
temporary, at least indirectly:
#include <iostream>
int const * address_of(int const & x) {
return &x;
}
int main() {
std::cout << address_of(1+2) << "\n";
}
That complies without warning with GCC 4.6 '-Wall -Wextra', MSVC 2010
'/W4', and Comeau's online front end, and I am pretty confident that the
above code is perfectly legal in terms of provoking undefined behavior
(in the technical C++ sense of "your program is now allowed to set your
cat on fire").
Evan
On Thu, Apr 26, 2012 at 12:05 PM, Evan Driscoll wrote:
> This thread has already beaten a dead horse enough that the horse came back
> as a zombie and was re-killed, but I couldn't help but respond to this part:
>
>
> On 01/-10/-28163 01:59 PM, Adam Skutt wrote:
>>
>> Code that relies on the identity of a temporary object is generally
>> incorrect. This is why C++ explicitly forbids taking the address
>> (identity) of temporaries.
>
>
> Except that C++ *doesn't* really forbid taking the address of a temporary,
> at least indirectly:
>
> #include <iostream>
>
> int const * address_of(int const & x) {
> return &x;
> }
>
> int main() {
> std::cout << address_of(1+2) << "\n";
> }
>
> That complies without warning with GCC 4.6 '-Wall -Wextra', MSVC 2010 '/W4',
> and Comeau's online front end, and I am pretty confident that the above code
> is perfectly legal in terms of provoking undefined behavior (in the
> technical C++ sense of "your program is now allowed to set your cat on
> fire").Yes, you can get a const reference to a temporary object, but that's
the only thing you can do. This is intentional, so you can use
temporaries (e.g., std::string("Hello World") ) in the same contexts
where one would use a literal (e.g., 3 or 4.2). Note that it's
impossible to mutate the temporary and impossible for the reference to
outlive the temporary.
What the standard says is: "The result of the unary & operator is a
pointer to its operand. The operand shall be an lvalue or a
qualified-id." The unary & operator is known as the address-of
operator. The C++ standard is actually going further than forbidding
temporaries, it forbids rvalues, which are things one expects to see
on the Right hand side of an assignment, or =.
One of Scott Meyer's Effective C++ books covers all of this in great
detail, including how you can get a temporary that's an lvalue as
opposed to an rvalue.
Adam
On 4/25/2012 5:01 PM, Steven D'Aprano wrote:
> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote:
>
>> Though, maybe it's better to use a different keyword than 'is' though,
>> due to the plain English
>> connotations of the term; I like 'sameobj' personally, for whatever
>> little it matters. Really, I think taking away the 'is' operator
>> altogether is better, so the only way to test identity is:
>> id(x) == id(y)
>
> Four reasons why that's a bad idea:
>
> 1) The "is" operator is fast, because it can be implemented directly by
> the interpreter as a simple pointer comparison (or equivalent).This assumes that everything is, internally, an object. In CPython,
that's the case, because Python is a naive interpreter and everything,
including numbers, is "boxed". That's not true of PyPy or Shed Skin.
So does "is" have to force the creation of a temporary boxed object?
The concept of "object" vs. the implementation of objects is
one reason you don't necessarily want to expose the implementation.
John Nagle
On Thu, Apr 26, 2012 at 3:48 PM, John Nagle wrote:
> This assumes that everything is, internally, an object. In CPython,
> that's the case, because Python is a naive interpreter and everything,
> including numbers, is "boxed". That's not true of PyPy or Shed Skin.
> So does "is" have to force the creation of a temporary boxed object?Interesting point. Presumably the only types that can be unboxed are
those for which identity vs equality is pretty much immaterial, so the
question won't really matter. I'd be inclined to either have it return
False if either/both is unboxed, or else return True if both are equal
unboxed numbers, whichever is the most convenient to implement.
My opinion doesn't matter--
My opinion doesn't matter--
My opinion doesn't matter, matter, matter, matter, matter!
(WS Gilbert, "Ruddigore")
ChrisA
On Wed, 25 Apr 2012 22:48:33 -0700, John Nagle wrote: > On 4/25/2012 5:01 PM, Steven D'Aprano wrote: >> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: >> >>> Though, maybe it's better to use a different keyword than 'is' though, >>> due to the plain English >>> connotations of the term; I like 'sameobj' personally, for whatever >>> little it matters. Really, I think taking away the 'is' operator >>> altogether is better, so the only way to test identity is: >>> id(x) == id(y) >> >> Four reasons why that's a bad idea: >> >> 1) The "is" operator is fast, because it can be implemented directly by >> the interpreter as a simple pointer comparison (or equivalent). > > This assumes that everything is, internally, an object.No it doesn't. It assumes that everything provides the external interface of an object. Internally, the implementation could be anything you like, so long as it simulates an object when observed from Python.
On Apr 26, 1:48 am, John Nagle wrote:
> On 4/25/2012 5:01 PM, Steven D'Aprano wrote:
>
> > On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote:
>
> >> Though, maybe it's better to use a different keyword than 'is' though,
> >> due to the plain English
> >> connotations of the term; I like 'sameobj' personally, for whatever
> >> little it matters. Really, I think taking away the 'is' operator
> >> altogether is better, so the only way to test identity is:
> >> id(x) == id(y)
>
> > Four reasons why that's a bad idea:
>
> > 1) The "is" operator is fast, because it can be implemented directly by
> > the interpreter as a simple pointer comparison (or equivalent).
>
> This assumes that everything is, internally, an object. In CPython,
> that's the case, because Python is a naive interpreter and everything,
> including numbers, is "boxed". That's not true of PyPy or Shed Skin.
> So does "is" have to force the creation of a temporary boxed object?That's what C# does AFAIK. Java defines '==' as value comparison for
primitives and '==' as identity comparison for objects, but I don't
exactly know how one would do that in Python.
Adam
On 4/26/2012 13:45, Adam Skutt wrote:
> On Apr 26, 1:48 am, John Nagle< wrote:
>> On 4/25/2012 5:01 PM, Steven D'Aprano wrote:
>>
>>> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote:
>>
>>>> Though, maybe it's better to use a different keyword than 'is' though,
>>>> due to the plain English
>>>> connotations of the term; I like 'sameobj' personally, for whatever
>>>> little it matters. Really, I think taking away the 'is' operator
>>>> altogether is better, so the only way to test identity is:
>>>> id(x) == id(y)
>>
>>> Four reasons why that's a bad idea:
>>
>>> 1) The "is" operator is fast, because it can be implemented directly by
>>> the interpreter as a simple pointer comparison (or equivalent).
>>
>> This assumes that everything is, internally, an object. In CPython,
>> that's the case, because Python is a naive interpreter and everything,
>> including numbers, is "boxed". That's not true of PyPy or Shed Skin.
>> So does "is" have to force the creation of a temporary boxed object?
>
> That's what C# does AFAIK. Java defines '==' as value comparison for
> primitives and '==' as identity comparison for objects, but I don't
> exactly know how one would do that in Python.Why should we take from Java one of its worst misfeatures and disfigure
Python for life?
a==b compares references while a.equals(b) compares values. Really???
Come on...
Python's way is much much cleaner.
Kiuhnm
On Apr 26, 9:37 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
> On 4/26/2012 13:45, Adam Skutt wrote:
>
>
>
>
>
>
>
>
>
> > On Apr 26, 1:48 am, John Nagle< wrote:
> >> On 4/25/2012 5:01 PM, Steven D'Aprano wrote:
>
> >>> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote:
>
> >>>> Though, maybe it's better to use a different keyword than 'is' though,
> >>>> due to the plain English
> >>>> connotations of the term; I like 'sameobj' personally, for whatever
> >>>> little it matters. Really, I think taking away the 'is' operator
> >>>> altogether is better, so the only way to test identity is:
> >>>> id(x) == id(y)
>
> >>> Four reasons why that's a bad idea:
>
> >>> 1) The "is" operator is fast, because it can be implemented directly by
> >>> the interpreter as a simple pointer comparison (or equivalent).
>
> >> This assumes that everything is, internally, an object. In CPython,
> >> that's the case, because Python is a naive interpreter and everything,
> >> including numbers, is "boxed". That's not true of PyPy or Shed Skin.
> >> So does "is" have to force the creation of a temporary boxed object?
>
> > That's what C# does AFAIK. Java defines '==' as value comparison for
> > primitives and '==' as identity comparison for objects, but I don't
> > exactly know how one would do that in Python.
>
> Why should we take from Java one of its worst misfeatures and disfigure
> Python for life?There are a lot of misfeatures in Java. Lack of operating overloading
really isn't one of them. I prefer languages that include operator
overloading, but readily understand and accept the arguments against
it. Nor is the differing behavior for '==' between primitives and
objects a misfeature.
C# and Python do have a misfeature: '==' is identity comparison only
if operator== / __eq__ is not overloaded. Identity comparison and
value comparison are disjoint operations, so it's entirely
inappropriate to combine them.
I don't necessarily mind if the two operations have the same symbol,
as long as there's some other way in-context to determine which
operation is occurring. This is the case in C and C++, for example.
> Python's way is much much cleaner.
Nope. Automatically substituting identity equality for value equality
is wrong. While rare, there are legitimate reasons for the former to
be True while the latter is False. Moreover, it means that class
authors must remember to write an __eq__ when appropriate and won't
get any sort of error when they forget to do so. That can lead to
bugs.
Adam
On Fri, Apr 27, 2012 at 12:00 AM, Adam Skutt wrote:
> C# and Python do have a misfeature: '==' is identity comparison only
> if operator== / __eq__ is not overloaded. Identity comparison and
> value comparison are disjoint operations, so it's entirely
> inappropriate to combine them.So what should happen if you don't implement __eq__? Should the ==
operator throw an exception? This can be done fairly easily:
class object(object):
def __eq__(self,other):
raise NoYouDontException("Naughty programmer, mustn't do that!")
(Works only if you always explicitly subclass object, even though
that's not necessary in Python 3.)
ChrisA
On 4/26/2012 16:00, Adam Skutt wrote: > On Apr 26, 9:37 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >> On 4/26/2012 13:45, Adam Skutt wrote: >> >> >> >> >> >> >> >> >> >>> On Apr 26, 1:48 am, John Nagle< wrote: >>>> On 4/25/2012 5:01 PM, Steven D'Aprano wrote: >> >>>>> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: >> >>>>>> Though, maybe it's better to use a different keyword than 'is' though, >>>>>> due to the plain English >>>>>> connotations of the term; I like 'sameobj' personally, for whatever >>>>>> little it matters. Really, I think taking away the 'is' operator >>>>>> altogether is better, so the only way to test identity is: >>>>>> id(x) == id(y) >> >>>>> Four reasons why that's a bad idea: >> >>>>> 1) The "is" operator is fast, because it can be implemented directly by >>>>> the interpreter as a simple pointer comparison (or equivalent). >> >>>> This assumes that everything is, internally, an object. In CPython, >>>> that's the case, because Python is a naive interpreter and everything, >>>> including numbers, is "boxed". That's not true of PyPy or Shed Skin. >>>> So does "is" have to force the creation of a temporary boxed object? >> >>> That's what C# does AFAIK. Java defines '==' as value comparison for >>> primitives and '==' as identity comparison for objects, but I don't >>> exactly know how one would do that in Python. >> >> Why should we take from Java one of its worst misfeatures and disfigure >> Python for life? > > There are a lot of misfeatures in Java. Lack of operating overloading > really isn't one of them. I prefer languages that include operator > overloading, but readily understand and accept the arguments against > it. Nor is the differing behavior for '==' between primitives and > objects a misfeature.The fact that you think that that's "differing behaviour" is what makes it a misfeature. The fact that you think that '==' can take objects as operands confirms that Java *does* confuse programmers.> C# and Python do have a misfeature: '==' is identity comparison only > if operator== / __eq__ is not overloaded. Identity comparison and > value comparison are disjoint operations, so it's entirely > inappropriate to combine them.They're not "disjoint", in fact one almost always implies the other (*). Python's idea is that, by default, any object is equal to itself and only itself. The fact that this is equivalent to "identity comparison" is just a coincidence, from a conceptual point of view. (*) nan == nan is false, but, at least conceptually, a 'NotComparable' exception should be raised instead. That wouldn't be very useful, though.> I don't necessarily mind if the two operations have the same symbol, > as long as there's some other way in-context to determine which > operation is occurring. This is the case in C and C++, for example. > >> Python's way is much much cleaner. > > Nope. Automatically substituting identity equality for value equality > is wrong. While rare, there are legitimate reasons for the former to > be True while the latter is False.There shouldn't be, to be fair.> Moreover, it means that class > authors must remember to write an __eq__ when appropriate and won't > get any sort of error when they forget to do so. That can lead to > bugs.I can agree on that, but that's something you can solve with a minor modification to the language. What I was talking about is the core design of Java and Python. Kiuhnm
On Apr 26, 12:02 pm, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote: > On 4/26/2012 16:00, Adam Skutt wrote: > > On Apr 26, 9:37 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: > >> On 4/26/2012 13:45, Adam Skutt wrote: > > >>> On Apr 26, 1:48 am, John Nagle< wrote: > >>>> This assumes that everything is, internally, an object. In CPython, > >>>> that's the case, because Python is a naive interpreter and everything, > >>>> including numbers, is "boxed". That's not true of PyPy or Shed Skin. > >>>> So does "is" have to force the creation of a temporary boxed object? > > >>> That's what C# does AFAIK. Java defines '==' as value comparison for > >>> primitives and '==' as identity comparison for objects, but I don't > >>> exactly know how one would do that in Python. > > >> Why should we take from Java one of its worst misfeatures and disfigure > >> Python for life? > > > There are a lot of misfeatures in Java. Lack of operating overloading > > really isn't one of them. I prefer languages that include operator > > overloading, but readily understand and accept the arguments against > > it. Nor is the differing behavior for '==' between primitives and > > objects a misfeature. > > The fact that you think that that's "differing behaviour" is what makes > it a misfeature. The fact that you think that '==' can take objects as > operands confirms that Java *does* confuse programmers. >The equality operator can absolutely be used between two objects. Try it if you don't believe me. It always does identity comparison when given two objects. It can also be given two primitives, and in this case, it does value comparison. Despite performing different operations with the same symbol, there's little risk of confusion because I can trivially figure out if a variable is an object or an primitive.> > C# and Python do have a misfeature: '==' is identity comparison only > > if operator== / __eq__ is not overloaded. Identity comparison and > > value comparison are disjoint operations, so it's entirely > > inappropriate to combine them. > > They're not "disjoint", in fact one almost always implies the other (*)."Almost always" isn't a rebuttal. There's no requirement whatsoever for the results of identity comparison to be related to the results of value comparison, ergo they are disjoint. Changing one doesn't have to influence the other. Please note that I never advocated doing what Java does, I merely noted what it does.> Python's idea is that, by default, any object is equal to itself and > only itself.Which is just wrong-headed. Many types have no meaningful definition for value equality, ergo any code that attempts to perform the operation is incorrect.> (*) nan == nan is false, but, at least conceptually, a 'NotComparable' > exception should be raised instead. That wouldn't be very useful, though. > >> Python's way is much much cleaner. > > > Nope. Automatically substituting identity equality for value equality > > is wrong. While rare, there are legitimate reasons for the former to > > be True while the latter is False. > > There shouldn't be, to be fair.Which is the whole problem. It's nice to keep erroneous conditions out of your domain, but it's just not always possible. I don't know how you implement NaN (which you need) without allowing for this. I don't know how you implement SQL NULL without allowing for this. While lots of problems can avoid this issue, I'm not sure all problems can. Moreover, I don't know how to implement a value comparison for many objects, so the operation should just be undefined. I should point out that I was a little hasty in painting Python with the same brush as C# and excluding Java. Python and Java are equally bad: value equality defaults to identity equality but there are distinct operations for telling them apart. People want identity equality in Python write 'is', not '=='. People who explicitly want value equality in Java write 'equals()'. I apologize, and blame skipping breakfast this morning. C# is arguably worse, since '==' on objects is defined as identity equality unless it has been overridden. This means that that the intent of the operation varies with no easy way to figure it out in context, you simply have to know. C# also provides a way to test only for identity, Object.ReferenceEquals(), but it's underused. Ultimately this is really a problem of documentation: the language shouldn't encourage conflation of intent in the manner it does.> > Moreover, it means that class > > authors must remember to write an __eq__ when appropriate and won't > > get any sort of error when they forget to do so. That can lead to > > bugs. > > I can agree on that, but that's something you can solve with a minor > modification to the language. What I was talking about is the core > design of Java and Python.The only difference is I see is which comparison is performed by the == symbol. But I don't see how nor why Python's decisions are superior to Java. Plus, I never suggested that Python should do what Java does, merely noted what it did since it seemed relevant to the discussion. Adam
On 4/26/2012 20:54, Adam Skutt wrote: > On Apr 26, 12:02 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >> On 4/26/2012 16:00, Adam Skutt wrote: >>> On Apr 26, 9:37 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >>>> On 4/26/2012 13:45, Adam Skutt wrote: >> >>>>> On Apr 26, 1:48 am, John Nagle< wrote: >>>>>> This assumes that everything is, internally, an object. In CPython, >>>>>> that's the case, because Python is a naive interpreter and everything, >>>>>> including numbers, is "boxed". That's not true of PyPy or Shed Skin. >>>>>> So does "is" have to force the creation of a temporary boxed object? >> >>>>> That's what C# does AFAIK. Java defines '==' as value comparison for >>>>> primitives and '==' as identity comparison for objects, but I don't >>>>> exactly know how one would do that in Python. >> >>>> Why should we take from Java one of its worst misfeatures and disfigure >>>> Python for life? >> >>> There are a lot of misfeatures in Java. Lack of operating overloading >>> really isn't one of them. I prefer languages that include operator >>> overloading, but readily understand and accept the arguments against >>> it. Nor is the differing behavior for '==' between primitives and >>> objects a misfeature. >> >> The fact that you think that that's "differing behaviour" is what makes >> it a misfeature. The fact that you think that '==' can take objects as >> operands confirms that Java *does* confuse programmers. >> > > The equality operator can absolutely be used between two objects. Try > it if you don't believe me. It always does identity comparison when > given two objects. It can also be given two primitives, and in this > case, it does value comparison. Despite performing different > operations with the same symbol, there's little risk of confusion > because I can trivially figure out if a variable is an object or an > primitive.No, it can't be used between objects but only between primitives and references (which should be regarded as primitives, by the way). If you understand that your 'a' is not really an object but a reference to it, everything becomes clear and you see that '==' always do the same thing. You might tell me that that's just an implementation detail, but when an implementation detail is easier to understand and makes more sense than the whole abstraction which is built upon it, something is seriously wrong. The distinction between primitives and objects is unfortunate. It is as if Java tried to get rid of pointers but never completely succeeded in doing that. It's the distinction between primitives and objects that should've been an implementation detail, IMO. Python's lack of this misfeature is what I'm really fond of.Everything else you said, I can agree on. >>> C# and Python do have a misfeature: '==' is identity comparison only >>> if operator== / __eq__ is not overloaded. Identity comparison and >>> value comparison are disjoint operations, so it's entirely >>> inappropriate to combine them. >> >> They're not "disjoint", in fact one almost always implies the other (*). > > "Almost always" isn't a rebuttal. There's no requirement whatsoever > for the results of identity comparison to be related to the results of > value comparison, ergo they are disjoint. Changing one doesn't have > to influence the other. Please note that I never advocated doing what > Java does, I merely noted what it does. > >> Python's idea is that, by default, any object is equal to itself and >> only itself. > > Which is just wrong-headed. Many types have no meaningful definition > for value equality, ergo any code that attempts to perform the > operation is incorrect. > >> (*) nan == nan is false, but, at least conceptually, a 'NotComparable' >> exception should be raised instead. That wouldn't be very useful, though. > >>>> Python's way is much much cleaner. >> >>> Nope. Automatically substituting identity equality for value equality >>> is wrong. While rare, there are legitimate reasons for the former to >>> be True while the latter is False. >> >> There shouldn't be, to be fair. > > > Which is the whole problem. It's nice to keep erroneous conditions > out of your domain, but it's just not always possible. I don't know > how you implement NaN (which you need) without allowing for this. I > don't know how you implement SQL NULL without allowing for this. > While lots of problems can avoid this issue, I'm not sure all problems > can. Moreover, I don't know how to implement a value comparison for > many objects, so the operation should just be undefined. > > I should point out that I was a little hasty in painting Python with > the same brush as C# and excluding Java. Python and Java are equally > bad: value equality defaults to identity equality but there are > distinct operations for telling them apart. People want identity > equality in Python write 'is', not '=='. People who explicitly want > value equality in Java write 'equals()'. I apologize, and blame > skipping breakfast this morning. > > C# is arguably worse, since '==' on objects is defined as identity > equality unless it has been overridden. This means that that the > intent of the operation varies with no easy way to figure it out in > context, you simply have to know. C# also provides a way to test only > for identity, Object.ReferenceEquals(), but it's underused. > Ultimately this is really a problem of documentation: the language > shouldn't encourage conflation of intent in the manner it does. > >>> Moreover, it means that class >>> authors must remember to write an __eq__ when appropriate and won't >>> get any sort of error when they forget to do so. That can lead to >>> bugs. >> >> I can agree on that, but that's something you can solve with a minor >> modification to the language. What I was talking about is the core >> design of Java and Python. > > The only difference is I see is which comparison is performed by the > == symbol. But I don't see how nor why Python's decisions are > superior to Java. Plus, I never suggested that Python should do what > Java does, merely noted what it did since it seemed relevant to the > discussion.
On Apr 26, 6:34 pm, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote: > On 4/26/2012 20:54, Adam Skutt wrote: > > On Apr 26, 12:02 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: > >> On 4/26/2012 16:00, Adam Skutt wrote: > >>> On Apr 26, 9:37 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: > >> The fact that you think that that's "differing behaviour" is what makes > >> it a misfeature. The fact that you think that '==' can take objects as > >> operands confirms that Java *does* confuse programmers. > > > The equality operator can absolutely be used between two objects. Try > > it if you don't believe me. It always does identity comparison when > > given two objects. It can also be given two primitives, and in this > > case, it does value comparison. Despite performing different > > operations with the same symbol, there's little risk of confusion > > because I can trivially figure out if a variable is an object or an > > primitive. > > No, it can't be used between objects but only between primitives and > references (which should be regarded as primitives, by the way).The only way to access an object is through a reference.>> If you > understand that your 'a' is not really an object but a reference to it, > everything becomes clear and you see that '==' always do the same thing.Yes, object identity is implemented almost? everywhere by comparingthe value of two pointers (references)[1]. I've already said I'm not really sure how else one would go about implementing it. > You might tell me that that's just an implementation detail, but when an > implementation detail is easier to understand and makes more sense than > the whole abstraction which is built upon it, something is seriously wrong.I'm not sure what abstraction is being built here. I think you have me confused for someone else, possibly Steven. You're missing the big picture. The two comparisons are asking different questions: Value equality asks if the operands 'have the same state' regardless of how they exist in memory. Identity equality asks if the two operands are the same block of memory. The two are distinct because not all types support both operations. If I write a function that does a value comparison, then it should do value comparison on _every type that can be passed to it_, regardless of whether the type is a primitive or an object, whether it has value or reference semantics, and regardless of how value comparison is actually implemented. If I write some function: f(x : T, y : U) => x == y where T and U are some unknown types, then I want the function to do a value comparison for every type pairing that allows the function to compile. Likewise, if I write a function that does identity comparison, then it logically wants to do identity comparison on _every type that can be passed to it_. To accomplish this, I must have a distinct way of asking each question. In Python we have '==' and 'is'[2]; in Java we have 'Object.equals()' and '=='; in C and C++ we distinguish by the types of the variables being compared (T and T*). Java gives '==' a different meaning for primitive types, but that turns out to be OK because I can't write a function that takes both a primitive type and a reference type at the same position. Yes, the reason it does this is due to what I said above, but that doesn't have any bearing on why we pick one operation over the other as programmers.> The distinction between primitives and objects is unfortunate. It is as > if Java tried to get rid of pointers but never completely succeeded in > doing that. > It's the distinction between primitives and objects that should've been > an implementation detail, IMO. > > Python's lack of this misfeature is what I'm really fond of.If anything, you have that backwards. Look at Python: all variables in Python have pointer semantics, not value semantics. In imperative languages, pointers have greater utility over value types because not all types can obey the rules for value types. For example, I don't know how to give value semantics to something like a I/O object (e.g, file, C++ fstream, C FILE), since I don't know how to create independent copies. One can obviously create an imperative language without pointers, but I/O gets rather tricky. Adam [1] Though it need not be (and often isn't) as simple as comparing two integers. [2] Well, I suspect 'is' gets used mostly for comparisons against None, True, and False in Python.
On 27/04/2012 00:57, Adam Skutt wrote:
...And Saint Adam Skutt raised the hand grenade up on high, saying, "O
LORD, bless this Thy hand grenade that with it Thou mayest blow Thine Id
to tiny bits, in Thy mercy." And the LORD did grin and the people did
feast upon the lambs and sloths and carp and anchovies and orangutans
and breakfast cereals, and fruit bats and large chu... [At this point,
the friar is urged by Brother Maynard to "skip a bit, brother"]... And
the LORD spake, saying, "First shalt thou take out the Holy Pin, then
shalt thou count to three, no more, no less. Three shall be the number
thou shalt count, and the number of the counting shall be three. Four
shalt thou not count, neither count thou two, excepting that thou then
proceed to three. Five is right out. Once the number three, being the
third number, be reached, then lobbest thou thy Holy Hand Grenade of
Antioch towards thy foe, who being naughty in My sight, shall snuff it."
Adam Skutt wrote: > If I write a function that does a value comparison, then it should > do value comparison on _every type that can be passed to it_, > regardless of whether the type is a primitive or an object, whether > it has value or reference semantics, and regardless of how value > comparison is actually implemented. If I write some function: > f(x : T, y : U) => x == y > where T and U are some unknown types, then I want the function to > do a value comparison for every type pairing that allows the > function to compile. Likewise, if I write a function that does > identity comparison, then it logically wants to do identity > comparison on _every type that can be passed to it_.What you say here makes perfect sense, but also shows that you really shouldn't be using Python if you want stuff to work this way. In Python any value of any type can be passed to any function. The claims you are making about object identity and object equality are reasonable, but as you show here, to really handle them requires dragging in a huge amount of type-system baggage. Python's behavior is perfectly well- defined. You might think it's not the best way to do it based on abstract conceptual frameworks for how programming languages "should" work, but it works just fine.
On Apr 26, 10:56 pm, "OKB (not okblacke)"
wrote:
> Adam Skutt wrote:
> > If I write a function that does a value comparison, then it should
> > do value comparison on _every type that can be passed to it_,
> > regardless of whether the type is a primitive or an object, whether
> > it has value or reference semantics, and regardless of how value
> > comparison is actually implemented. If I write some function:
> > f(x : T, y : U) => x == y
> > where T and U are some unknown types, then I want the function to
> > do a value comparison for every type pairing that allows the
> > function to compile. Likewise, if I write a function that does
> > identity comparison, then it logically wants to do identity
> > comparison on _every type that can be passed to it_.
>
> What you say here makes perfect sense, but also shows that you
> really shouldn't be using Python if you want stuff to work this way. In
> Python any value of any type can be passed to any function. The claims
> you are making about object identity and object equality are reasonable,
> but as you show here, to really handle them requires dragging in a huge
> amount of type-system baggage.So the check gets deferred to runtime, and the programmer may need to
explictly throw 'NotImplemented' or something like that. Which is
what happens in Python. Not type-checking arguments simply moves the
burden from the language to the programmer, which is a standard
consequence of moving from static to dynamic typing.
Adam
On 4/27/2012 1:57, Adam Skutt wrote: > On Apr 26, 6:34 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >> On 4/26/2012 20:54, Adam Skutt wrote: >>> On Apr 26, 12:02 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >>>> On 4/26/2012 16:00, Adam Skutt wrote: >>>>> On Apr 26, 9:37 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >>>> The fact that you think that that's "differing behaviour" is what makes >>>> it a misfeature. The fact that you think that '==' can take objects as >>>> operands confirms that Java *does* confuse programmers. >> >>> The equality operator can absolutely be used between two objects. Try >>> it if you don't believe me. It always does identity comparison when >>> given two objects. It can also be given two primitives, and in this >>> case, it does value comparison. Despite performing different >>> operations with the same symbol, there's little risk of confusion >>> because I can trivially figure out if a variable is an object or an >>> primitive. >> >> No, it can't be used between objects but only between primitives and >> references (which should be regarded as primitives, by the way). > > The only way to access an object is through a reference. > >>> If you >> understand that your 'a' is not really an object but a reference to it, >> everything becomes clear and you see that '==' always do the same thing. > > Yes, object identity is implemented almost? everywhere by comparing > the value of two pointers (references)[1]. I've already said I'm not > really sure how else one would go about implementing it. > >> You might tell me that that's just an implementation detail, but when an >> implementation detail is easier to understand and makes more sense than >> the whole abstraction which is built upon it, something is seriously wrong. > > I'm not sure what abstraction is being built here. I think you have > me confused for someone else, possibly Steven.The abstraction is this: - There are primitives and objects. - Primitives are not objects. The converse is also true. - Primitives can become objects (boxing). - Two primitives x and y are equal iff x == y. - Two objects x and y are equal iff x.equals(y). - Two objects are the same object iff x == y. - If x is a primitive, then y = x is a deep copy. - If x is an object, then y = x is a shallow copy. - ... The truth: - Primitives can be references. - Two primitives are equal iff x == y. - Operator '.' automatically derefences references. So Java is just C++ plus Garbage colletion plus some automatic dereferencing minus a lot of other things. On the other hand, Python regards everything as an object and that's the way we should go, in my opinion. Some objects are mutable and other are immutable. This distinction doesn't mess with references. Therefore, modifying the behavior of 'is' would be, in my opinion, a huge step back.> You're missing the big picture. The two comparisons are asking > different questions: > Value equality asks if the operands 'have the same state' > regardless of how they exist in memory. > Identity equality asks if the two operands are the same block of > memory. > > The two are distinct because not all types support both operations. > > If I write a function that does a value comparison, then it should do > value comparison on _every type that can be passed to it_, regardless > of whether the type is a primitive or an object, whether it has value > or reference semantics, and regardless of how value comparison is > actually implemented. If I write some function: > f(x : T, y : U) => x == y > where T and U are some unknown types, then I want the function to do a > value comparison for every type pairing that allows the function to > compile. Likewise, if I write a function that does identity > comparison, then it logically wants to do identity comparison on > _every type that can be passed to it_."Value comparison" and "identity comparison" is a terminology that I don't agree on. Equality or equivalence is a relation which is: - reflexive - symmetric - transitive Everything else... is something else. Call it semi-equality, tricky-equality or whatever, but not equality, please. It's only natural that Python defaults to the Identity relation which is the (set-)intersection of all possible equality relations.> To accomplish this, I must have a distinct way of asking each > question. In Python we have '==' and 'is'[2]; in Java we have > 'Object.equals()' and '=='; in C and C++ we distinguish by the types > of the variables being compared (T and T*). > > Java gives '==' a different meaning for primitive types, but that > turns out to be OK because I can't write a function that takes both a > primitive type and a reference type at the same position.And as a side effect some integers are primitives and other are objects.> Yes, the > reason it does this is due to what I said above, but that doesn't have > any bearing on why we pick one operation over the other as > programmers. > >> The distinction between primitives and objects is unfortunate. It is as >> if Java tried to get rid of pointers but never completely succeeded in >> doing that. > >> It's the distinction between primitives and objects that should've been >> an implementation detail, IMO. >> >> Python's lack of this misfeature is what I'm really fond of. > > If anything, you have that backwards. Look at Python: all variables > in Python have pointer semantics, not value semantics.When everything is "white", the word "white" becomes redundant. So the fact that everything in Python have reference semantics means that we can't stop thinking about value and reference semantics.> In imperative > languages, pointers have greater utility over value types because not > all types can obey the rules for value types. For example, I don't > know how to give value semantics to something like a I/O object (e.g, > file, C++ fstream, C FILE), since I don't know how to create > independent copies.By defining a copy constructor.> One can obviously create an imperative language without pointers, but > I/O gets rather tricky.Python is already without pointers (*). A world where everyone is a lawyer is a world without lawyers (really, there isn't any other way we can get rid of them :) ). (*) By the way, some would argue that references are not pointers. Kiuhnm
On Apr 27, 11:01 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote: > On 4/27/2012 1:57, Adam Skutt wrote: > > On Apr 26, 6:34 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: > >>> If you > >> understand that your 'a' is not really an object but a reference to it, > >> everything becomes clear and you see that '==' always do the same thing. > > > Yes, object identity is implemented almost? everywhere by comparing > > the value of two pointers (references)[1]. I've already said I'm not > > really sure how else one would go about implementing it. > > >> You might tell me that that's just an implementation detail, but when an > >> implementation detail is easier to understand and makes more sense than > >> the whole abstraction which is built upon it, something is seriously wrong. > > > I'm not sure what abstraction is being built here. I think you have > > me confused for someone else, possibly Steven. > > The abstraction is this: > - There are primitives and objects. > - Primitives are not objects. The converse is also true. > - Primitives can become objects (boxing). > - Two primitives x and y are equal iff x == y. > - Two objects x and y are equal iff x.equals(y). > - Two objects are the same object iff x == y. > - If x is a primitive, then y = x is a deep copy. > - If x is an object, then y = x is a shallow copy. > - ... >This is not an abstraction at all, but merely a poor explanation of how things work in Java. Your last statement is totally incorrect, as no copying of the object occurs whatsoever. The reference is merely reseated to refer to the new object. If you're going to chide me for ignoring the difference between the reference and the referent object, then you shouldn't ignore it either, especially in the one case where it actually matters! If we try to extend this to other languages, then it breaks down completely.> The truth: > - Primitives can be references. > - Two primitives are equal iff x == y. > - Operator '.' automatically derefences references. >You have the first statement backwards. References are a primitive construct, not the other way around. While true, it's still a bad way to think about what's going on. It breaks down once we add C++ / Pascal reference types to the mix, for example. It's better to think about variables (names) and just recognize that not all variables have the same semantics. It avoids details that are irrelevant to writing actual programs and remains consistent.> Equality or equivalence is a relation which is: > - reflexive > - symmetric > - transitive > Everything else... is something else. Call it semi-equality, > tricky-equality or whatever, but not equality, please.Sure, but then it's illegal to allow the usage of '==' with floating point numbers, which will never have these properties in any usable implementation[1]. So we're back to what started this tangent, and we end up needing 'equals()' methods on our classes to distinguish between the different forms of equality. That's precisely what you want to avoid. Or we can just accept that '==' doesn't always possess those properties, which is what essentially every programming language does, and call it (value) equality. As long as we don't cross incompatible meanings, it's hard to believe that this isn't the right thing to do.> > > If anything, you have that backwards. Look at Python: all variables > > in Python have pointer semantics, not value semantics. > > When everything is "white", the word "white" becomes redundant. > So the fact that everything in Python have reference semantics means > that we can't stop thinking about value and reference semantics.Nope. The behavior of variables is absolutely essential to writing correct programs. If I write a program in Python that treats variables as if they were values, it will be incorrect.> > > In imperative > > languages, pointers have greater utility over value types because not > > all types can obey the rules for value types. For example, I don't > > know how to give value semantics to something like a I/O object (e.g, > > file, C++ fstream, C FILE), since I don't know how to create > > independent copies. > > By defining a copy constructor.Then write me a working one. I'll wait. To save yourself some time, you can start with std::fstream.> Python is already without pointers (*). > A world where everyone is a lawyer is a world without lawyers (really, > there isn't any other way we can get rid of them :) ). > > (*) By the way, some would argue that references are not pointers.They would be completely and utterly wrong, and probably imbuing pointers with properties they don't actually possess. Unless you're talking about C++ / Pascal references, which really aren't pointers and do possess a different set of semantics (alias might be a better term for them). Adam [1] Not in any fashion that's useful to the programmer, at any rate.
On Fri, 27 Apr 2012 10:15:32 -0700, Adam Skutt wrote: > If I write a program in Python that treats variables as if they were > values, it will be incorrect.I'm sorry, it is unclear to me what distinction you are making between variables and values. Can you give simple examples of both incorrect and correct code demonstrating what you mean? (I know what distinction *I* would make, but I'm not sure if it is the same one you are making.)
On 4/27/2012 19:15, Adam Skutt wrote: > On Apr 27, 11:01 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >> On 4/27/2012 1:57, Adam Skutt wrote: >>> On Apr 26, 6:34 pm, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >>>>> If you >>>> understand that your 'a' is not really an object but a reference to it, >>>> everything becomes clear and you see that '==' always do the same thing. >> >>> Yes, object identity is implemented almost? everywhere by comparing >>> the value of two pointers (references)[1]. I've already said I'm not >>> really sure how else one would go about implementing it. >> >>>> You might tell me that that's just an implementation detail, but when an >>>> implementation detail is easier to understand and makes more sense than >>>> the whole abstraction which is built upon it, something is seriously wrong. >> >>> I'm not sure what abstraction is being built here. I think you have >>> me confused for someone else, possibly Steven. >> >> The abstraction is this: >> - There are primitives and objects. >> - Primitives are not objects. The converse is also true. >> - Primitives can become objects (boxing). >> - Two primitives x and y are equal iff x == y. >> - Two objects x and y are equal iff x.equals(y). >> - Two objects are the same object iff x == y. >> - If x is a primitive, then y = x is a deep copy. >> - If x is an object, then y = x is a shallow copy. >> - ... >> > > This is not an abstraction at all, but merely a poor explanation of > how things work in Java. Your last statement is totally incorrect, as > no copying of the object occurs whatsoever. The reference is merely > reseated to refer to the new object. If you're going to chide me for > ignoring the difference between the reference and the referent object, > then you shouldn't ignore it either, especially in the one case where > it actually matters! If we try to extend this to other languages, > then it breaks down completely.With shallow copy I meant exactly that. I didn't think that my using theterm with a more general meaning would cause such a reaction. I don't agree on the other things you said, of course. > >> The truth: >> - Primitives can be references. >> - Two primitives are equal iff x == y. >> - Operator '.' automatically derefences references. >> > > You have the first statement backwards. References are a primitive > construct, not the other way around.So you're saying that I said that "Primitive constructs are references". Right... > While true, it's still a bad way > to think about what's going on. It breaks down once we add C++ / > Pascal reference types to the mix, for example.?> It's better to think about variables (names) and just recognize that > not all variables have the same semantics. It avoids details that are > irrelevant to writing actual programs and remains consistent.Maybe in your opinion. As I said, I don't agree with you. >> Equality or equivalence is a relation which is: >> - reflexive >> - symmetric >> - transitive >> Everything else... is something else. Call it semi-equality, >> tricky-equality or whatever, but not equality, please. > > Sure, but then it's illegal to allow the usage of '==' with floating > point numbers, which will never have these properties in any usable > implementation[1].???> So we're back to what started this tangent, and we > end up needing 'equals()' methods on our classes to distinguish > between the different forms of equality. That's precisely what you > want to avoid. > > Or we can just accept that '==' doesn't always possess those > properties, which is what essentially every programming language does, > and call it (value) equality. As long as we don't cross incompatible > meanings, it's hard to believe that this isn't the right thing to do. > >> >>> If anything, you have that backwards. Look at Python: all variables >>> in Python have pointer semantics, not value semantics. >> >> When everything is "white", the word "white" becomes redundant. >> So the fact that everything in Python have reference semantics means >> that we can't stop thinking about value and reference semantics. > > Nope. The behavior of variables is absolutely essential to writing > correct programs. If I write a program in Python that treats > variables as if they were values, it will be incorrect.You misunderstood what I said. You wouldn't treat variables as if they were values because you wouldn't even know what that means and that that's even a possibility. I've never heard an old C programmer talk about "value semantics" and "reference semantics". When everything is a value, your world is pretty simple.>>> In imperative >>> languages, pointers have greater utility over value types because not >>> all types can obey the rules for value types. For example, I don't >>> know how to give value semantics to something like a I/O object (e.g, >>> file, C++ fstream, C FILE), since I don't know how to create >>> independent copies. >> >> By defining a copy constructor. > > Then write me a working one. I'll wait. To save yourself some time, > you can start with std::fstream.Will you pay me for my time? Your problem is that you think that copy semantics requires real copying. I really don't see any technical difficulty in virtualizing the all thing.>> Python is already without pointers (*). >> A world where everyone is a lawyer is a world without lawyers (really, >> there isn't any other way we can get rid of them :) ). >> >> (*) By the way, some would argue that references are not pointers. > > They would be completely and utterly wrong, and probably imbuing > pointers with properties they don't actually possess. Unless you're > talking about C++ / Pascal references, which really aren't pointers > and do possess a different set of semantics (alias might be a better > term for them).As always, anyone else is completely and utterly wrong, in your opinion. Let's leave it at that. Kiuhnm
On Sat, Apr 28, 2012 at 9:26 PM, Kiuhnm
wrote:
> Your problem is that you think that copy semantics requires real copying. I
> really don't see any technical difficulty in virtualizing the all thing."Copy semantics" without "real copying" is an optimization that a
program should never need to be aware of. For instance, you could have
two 16GB strings share their buffers to avoid having to use 32GB of
memory; but to demonstrate copy semantics, they would need to
copy-on-write in some fashion. There's duplicate state but shared
memory. The trouble with duplicating state of a std::fstream is that
it's roughly impossible. You could perhaps simulate it with read-only
file access, but when you write, somehow it has to affect the disk,
and that means either you copy the file (but keep the same file name)
or have both of them affect the same file (meaning we're on Borg
semantics, not copying).
ChrisA
On Apr 28, 7:26 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote: > On 4/27/2012 19:15, Adam Skutt wrote: > > On Apr 27, 11:01 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: > >> The abstraction is this: > >> - There are primitives and objects. > >> - Primitives are not objects. The converse is also true. > >> - Primitives can become objects (boxing). > >> - Two primitives x and y are equal iff x == y. > >> - Two objects x and y are equal iff x.equals(y). > >> - Two objects are the same object iff x == y. > >> - If x is a primitive, then y = x is a deep copy. > >> - If x is an object, then y = x is a shallow copy. > >> - ... > > > This is not an abstraction at all, but merely a poor explanation of > > how things work in Java. Your last statement is totally incorrect, as > > no copying of the object occurs whatsoever. The reference is merely > > reseated to refer to the new object. If you're going to chide me for > > ignoring the difference between the reference and the referent object, > > then you shouldn't ignore it either, especially in the one case where > > it actually matters! If we try to extend this to other languages, > > then it breaks down completely. > > With shallow copy I meant exactly that. I didn't think that my using the > term with a more general meaning would cause such a reaction.It has a very strict, well-defined meaning in these contexts, especially in languages such as C++.> > So you're saying that I said that "Primitive constructs are references". > Right...No, still wrong. What I said is correct, "References are a form of primitive construct". In C, an int is a primitive but not a reference. An int* is a pointer (reference), and is also (essentially) a primitive.> > > While true, it's still a bad way > > to think about what's going on. It breaks down once we add C++ / > > Pascal reference types to the mix, for example. > > ?Assignment to a C++ reference (T&) effects the underlying object, not the reference itself. A reference can never be reseated once it is bound to an object. Comparing equality on two references directly is the same as comparing two values (it calls operator==). Comparing identity requires doing (&x == &y), like one would do with a value. However, unlike a value, the object is not destroyed when the reference goes out of scope. Most importantly, references to base classes do not slice derived class objects, so virtual calls work correctly through references. As a result, normally the right way to think about a value is as a "temporary name" for an object and not worry about any of the details about how the language makes it work.> > >> Equality or equivalence is a relation which is: > >> - reflexive > >> - symmetric > >> - transitive > >> Everything else... is something else. Call it semi-equality, > >> tricky-equality or whatever, but not equality, please. > > > Sure, but then it's illegal to allow the usage of '==' with floating > > point numbers, which will never have these properties in any usable > > implementation[1]. > > ??? >The operator == is called the equality operator. Floating-point numbers don't really obey those properties in any meaningful fashion. The result is that portions of your view contradict others. Either we must give '==' a different name, meaning what you consider equality is irrelevant, or we must use method names like 'equals', which you find objectionable.> >>> If anything, you have that backwards. Look at Python: all variables > >>> in Python have pointer semantics, not value semantics. > > >> When everything is "white", the word "white" becomes redundant. > >> So the fact that everything in Python have reference semantics means > >> that we can't stop thinking about value and reference semantics. > > > Nope. The behavior of variables is absolutely essential to writing > > correct programs. If I write a program in Python that treats > > variables as if they were values, it will be incorrect. > > You misunderstood what I said. You wouldn't treat variables as if they > were values because you wouldn't even know what that means and that > that's even a possibility.Well, one hopes that is true. I think we have a misunderstanding overlanguage: you said "value and reference semantics" when you really meant "value vs. reference semantics". > I've never heard an old C programmer talk about "value semantics" and > "reference semantics". When everything is a value, your world is pretty > simple.Except if that were true, the comp.lang.c FAQ wouldn't have this question and answer: http://c-faq.com/ptrs/passbyref.html, and several others. Much as you may not like it, most code doesn't care about a pointer's value, doesn't need to know anything about it, and would just as soon pretend that it doesn't exist. All it really wants is a controlled way to mutate objects in different scopes. Which is precisely why references are preferred over pointers in C++, as they're a better expression of programmer intent, and far safe as a result. Peaking under the covers in an attempt to simplify the definition of '==' is silly. As I've hopefully shown by now, it's pretty much a fool's errand anyway.> > >>> In imperative > >>> languages, pointers have greater utility over value types because not > >>> all types can obey the rules for value types. For example, I don't > >>> know how to give value semantics to something like a I/O object (e.g, > >>> file, C++ fstream, C FILE), since I don't know how to create > >>> independent copies. > > >> By defining a copy constructor. > > > Then write me a working one. I'll wait. To save yourself some time, > > you can start with std::fstream. > > Will you pay me for my time?No, because you'll never finish. There's a reason why std::fstream lacks one, and it isn't because the committee is lazy.> > Your problem is that you think that copy semantics requires real > copying. I really don't see any technical difficulty in virtualizing the > all thing.Then you would have written one already. They do require real copying, though. Adam
On 4/28/2012 16:18, Adam Skutt wrote: > On Apr 28, 7:26 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >> On 4/27/2012 19:15, Adam Skutt wrote: >>> On Apr 27, 11:01 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >>>> The abstraction is this: >>>> - There are primitives and objects. >>>> - Primitives are not objects. The converse is also true. >>>> - Primitives can become objects (boxing). >>>> - Two primitives x and y are equal iff x == y. >>>> - Two objects x and y are equal iff x.equals(y). >>>> - Two objects are the same object iff x == y. >>>> - If x is a primitive, then y = x is a deep copy. >>>> - If x is an object, then y = x is a shallow copy. >>>> - ... >> >>> This is not an abstraction at all, but merely a poor explanation of >>> how things work in Java. Your last statement is totally incorrect, as >>> no copying of the object occurs whatsoever. The reference is merely >>> reseated to refer to the new object. If you're going to chide me for >>> ignoring the difference between the reference and the referent object, >>> then you shouldn't ignore it either, especially in the one case where >>> it actually matters! If we try to extend this to other languages, >>> then it breaks down completely. >> >> With shallow copy I meant exactly that. I didn't think that my using the >> term with a more general meaning would cause such a reaction. > > It has a very strict, well-defined meaning in these contexts, > especially in languages such as C++.In C++ it's called "memberwise copy" (see the C++ spec), which is not equivalent to Java's "shallow copy", thanks to copy constructors.>> So you're saying that I said that "Primitive constructs are references". >> Right... > > No, still wrong.You said: "You have the first statement backwards. References are a primitive construct, not the other way around."The other way around is "Primitive construct are references", but I never said that. > What I said is correct, "References are a form of > primitive construct". In C, an int is a primitive but not a > reference. An int* is a pointer (reference), and is also > (essentially) a primitive.I never said it isn't. >>> While true, it's still a bad way >>> to think about what's going on. It breaks down once we add C++ / >>> Pascal reference types to the mix, for example. >> >> ? > > Assignment to a C++ reference (T&) effects the underlying object, not > the reference itself. A reference can never be reseated once it is > bound to an object. Comparing equality on two references directly is > the same as comparing two values (it calls operator==). Comparing > identity requires doing (&x ==&y), like one would do with a value. > However, unlike a value, the object is not destroyed when the > reference goes out of scope. Most importantly, references to base > classes do not slice derived class objects, so virtual calls work > correctly through references. > > As a result, normally the right way to think about a value is as a > "temporary name" for an object and not worry about any of the details > about how the language makes it work.If you add C++ references to Java, the abstraction breaks down too, so I don't see your point.>>>> Equality or equivalence is a relation which is: >>>> - reflexive >>>> - symmetric >>>> - transitive >>>> Everything else... is something else. Call it semi-equality, >>>> tricky-equality or whatever, but not equality, please. >> >>> Sure, but then it's illegal to allow the usage of '==' with floating >>> point numbers, which will never have these properties in any usable >>> implementation[1]. >> >> ??? >> > > The operator == is called the equality operator. Floating-point > numbers don't really obey those properties in any meaningful fashion.I say they do. Any counter-examples?> The result is that portions of your view contradict others. Either we > must give '==' a different name, meaning what you consider equality is > irrelevant, or we must use method names like 'equals', which you find > objectionable. > >>>>> If anything, you have that backwards. Look at Python: all variables >>>>> in Python have pointer semantics, not value semantics. >> >>>> When everything is "white", the word "white" becomes redundant. >>>> So the fact that everything in Python have reference semantics means >>>> that we can't stop thinking about value and reference semantics. >> >>> Nope. The behavior of variables is absolutely essential to writing >>> correct programs. If I write a program in Python that treats >>> variables as if they were values, it will be incorrect. >> >> You misunderstood what I said. You wouldn't treat variables as if they >> were values because you wouldn't even know what that means and that >> that's even a possibility. > > Well, one hopes that is true. I think we have a misunderstanding over > language: you said "value and reference semantics" when you really > meant "value vs. reference semantics".Ok.>> I've never heard an old C programmer talk about "value semantics" and >> "reference semantics". When everything is a value, your world is pretty >> simple. > > Except if that were true, the comp.lang.c FAQ wouldn't have this > question and answer: http://c-faq.com/ptrs/passbyref.html, and several > others.That's why I said "an /old/ C programmer". > Much as you may not like it, most code doesn't care about a pointer's > value, doesn't need to know anything about it, and would just as soon > pretend that it doesn't exist. All it really wants is a controlled > way to mutate objects in different scopes. Which is precisely why > references are preferred over pointers in C++, as they're a better > expression of programmer intent, and far safe as a result. > > Peaking under the covers in an attempt to simplify the definition of > '==' is silly. As I've hopefully shown by now, it's pretty much a > fool's errand anyway.You say a lot of true things as I already said, but they don't prove anything. You say "this is bad or right because <something true>". That "because" is what we don't agree on.As I already said, if the abstraction is (much) more complex than the mechanism under the hood, in my opinion, the abstraction is bad. >>>>> In imperative >>>>> languages, pointers have greater utility over value types because not >>>>> all types can obey the rules for value types. For example, I don't >>>>> know how to give value semantics to something like a I/O object (e.g, >>>>> file, C++ fstream, C FILE), since I don't know how to create >>>>> independent copies. >> >>>> By defining a copy constructor. >> >>> Then write me a working one. I'll wait. To save yourself some time, >>> you can start with std::fstream. >> >> Will you pay me for my time? > > No, because you'll never finish. There's a reason why std::fstream > lacks one, and it isn't because the committee is lazy. > >> >> Your problem is that you think that copy semantics requires real >> copying. I really don't see any technical difficulty in virtualizing the >> all thing. > > Then you would have written one already.What a solid reasoning.> They do require real > copying, though.Think what you want. Kiuhnm
On Thu, 26 Apr 2012 18:02:31 +0200, Kiuhnm wrote: > On 4/26/2012 16:00, Adam Skutt wrote: >> C# and Python do have a misfeature: '==' is identity comparison only if >> operator== / __eq__ is not overloaded. Identity comparison and value >> comparison are disjoint operations, so it's entirely inappropriate to >> combine them. > > They're not "disjoint", in fact one almost always implies the other (*). > Python's idea is that, by default, any object is equal to itself and > only itself. The fact that this is equivalent to "identity comparison" > is just a coincidence, from a conceptual point of view.Define your terms: what do you mean by "equal"? The complication is that "equal" has many meanings. Does 1/2 equal 2/4? Well, yes, numerically, but numerical equality is not the only useful sense of equality -- not even for mathematicians! Although the convention to write "1/2 = 2/4" is too strong to discard, there are areas of mathematics where 1/2 and 2/4 are not treated as equal regardless of numerical equality. http://en.wikipedia.org/wiki/Mediant_%28mathe... In Python, "equal" can have any meaning we like, because we can override __eq__. For most meaningful equality comparisons, we expect that X should always equal itself, even if it doesn't equal anything else, and so __eq__ defaulting to an identity comparison if you don't override it makes good sense. Some people (e.g. the creator of Eiffel, Bertrand Meyer) argue that identity should *always* imply equality (reflexivity). I disagree, but regardless, reflexivity is *almost always* the right thing to do. When it comes to equality, Python defaults to sensible behaviour. By default, any object supports equality. By default, "a == a" is true for any object a. If you want to define a non-reflexive type, you have to do so yourself. If you want to define a type that doesn't support equality at all, you have to do so yourself. But both use-cases are vanishingly rare, and rather troublesome to use. It would be stupid for Python to make them the default behaviour. After all, Python is a tool, not a philosophy. There's no need to force the user to start from a blank slate and define everything from first principles when you can start with the common tools you normally need, and add or subtract from it as needed.> (*) nan == nan is false, but, at least conceptually, a 'NotComparable' > exception should be raised instead. That wouldn't be very useful, > though.NANs are comparable. By definition, NAN != x for every x. They're just not reflexive.>> I don't necessarily mind if the two operations have the same symbol, as >> long as there's some other way in-context to determine which operation >> is occurring. This is the case in C and C++, for example. >> >>> Python's way is much much cleaner. >> >> Nope. Automatically substituting identity equality for value equality >> is wrong. While rare, there are legitimate reasons for the former to >> be True while the latter is False. > > There shouldn't be, to be fair.I disagree. Violating reflexivity has its uses. NANs are the classic example. Another example is if you redefine "==" to mean something other than "equals". If your class treats == as something other than equality, there is no need for a==a to necessarily return True.
On 4/27/2012 13:09, Steven D'Aprano wrote: > On Thu, 26 Apr 2012 18:02:31 +0200, Kiuhnm wrote: > >> On 4/26/2012 16:00, Adam Skutt wrote: >>> C# and Python do have a misfeature: '==' is identity comparison only if >>> operator== / __eq__ is not overloaded. Identity comparison and value >>> comparison are disjoint operations, so it's entirely inappropriate to >>> combine them. >> >> They're not "disjoint", in fact one almost always implies the other (*). >> Python's idea is that, by default, any object is equal to itself and >> only itself. The fact that this is equivalent to "identity comparison" >> is just a coincidence, from a conceptual point of view. > > Define your terms: what do you mean by "equal"?a and b are equal iff a = a a = b => b = a a = b and b = c => a = c If some of this properties are violated, we're talking of something else. The fact that you can define '==' whatever way you want is irrelevant. I call that "calling 'equality' something which shouldn't be regarded as such and making Python comply with it anyway."> The complication is that "equal" has many meanings. Does 1/2 equal 2/4? > Well, yes, numerically, but numerical equality is not the only useful > sense of equality -- not even for mathematicians! Although the convention > to write "1/2 = 2/4" is too strong to discard, there are areas of > mathematics where 1/2 and 2/4 are not treated as equal regardless of > numerical equality. > > http://en.wikipedia.org/wiki/Mediant_%28mathe... > > In Python, "equal" can have any meaning we like, because we can override > __eq__. For most meaningful equality comparisons, we expect that X should > always equal itself, even if it doesn't equal anything else, and so __eq__ > defaulting to an identity comparison if you don't override it makes good > sense. > > Some people (e.g. the creator of Eiffel, Bertrand Meyer) argue that > identity should *always* imply equality (reflexivity). I disagree, but > regardless, reflexivity is *almost always* the right thing to do. > > When it comes to equality, Python defaults to sensible behaviour. By > default, any object supports equality. By default, "a == a" is true for > any object a. If you want to define a non-reflexive type, you have to do > so yourself. If you want to define a type that doesn't support equality > at all, you have to do so yourself. But both use-cases are vanishingly > rare, and rather troublesome to use. It would be stupid for Python to > make them the default behaviour. > > After all, Python is a tool, not a philosophy. There's no need to force > the user to start from a blank slate and define everything from first > principles when you can start with the common tools you normally need, > and add or subtract from it as needed. > > > >> (*) nan == nan is false, but, at least conceptually, a 'NotComparable' >> exception should be raised instead. That wouldn't be very useful, >> though. > > NANs are comparable. By definition, NAN != x for every x. They're just > not reflexive.As I said, I know they are but I think they shouldn't, at least conceptually. >>> I don't necessarily mind if the two operations have the same symbol, as >>> long as there's some other way in-context to determine which operation >>> is occurring. This is the case in C and C++, for example. >>> >>>> Python's way is much much cleaner. >>> >>> Nope. Automatically substituting identity equality for value equality >>> is wrong. While rare, there are legitimate reasons for the former to >>> be True while the latter is False. >> >> There shouldn't be, to be fair. > > I disagree. Violating reflexivity has its uses. NANs are the classic > example.Useful... maybe, conceptually sound... no. Conceptually, NaN is the class of all elements which are not numbers, therefore NaN = NaN. The conceptually correct way would be to check for 'NaN' explicitly.> Another example is if you redefine "==" to mean something other than > "equals". If your class treats == as something other than equality, there > is no need for a==a to necessarily return True.Then it wouldn't be equality anymore. I can always call a cat 'dog'. Kiuhnm
On Fri, Apr 27, 2012 at 10:07 PM, Kiuhnm
wrote:
> Conceptually, NaN is the class of all elements which are not numbers,
> therefore NaN = NaN. The conceptually correct way would be to check for
> 'NaN' explicitly.Conceptually, "single-digit-numbers" is the class of all elements
which are integers [0,10). Does that mean that SdN = SdN, and
therefore that 2 = 5?
ChrisA
On 4/27/2012 14:07, Kiuhnm wrote: > On 4/27/2012 13:09, Steven D'Aprano wrote: >> On Thu, 26 Apr 2012 18:02:31 +0200, Kiuhnm wrote: >> >>> On 4/26/2012 16:00, Adam Skutt wrote: >>>> C# and Python do have a misfeature: '==' is identity comparison only if >>>> operator== / __eq__ is not overloaded. Identity comparison and value >>>> comparison are disjoint operations, so it's entirely inappropriate to >>>> combine them. >>> >>> They're not "disjoint", in fact one almost always implies the other (*). >>> Python's idea is that, by default, any object is equal to itself and >>> only itself. The fact that this is equivalent to "identity comparison" >>> is just a coincidence, from a conceptual point of view. >> >> Define your terms: what do you mean by "equal"? > > a and b are equal iffNope. What I meant is that we can talk of equality whenever...> a = a > a = b => b = a > a = b and b = c => a = c > If some of this properties are violated, we're talking of something else.Kiuhnm
On Fri, 27 Apr 2012 14:17:48 +0200, Kiuhnm wrote: >>> Define your terms: what do you mean by "equal"? >> >> a and b are equal iff > > Nope. What I meant is that we can talk of equality whenever... > >> a = a >> a = b => b = a >> a = b and b = c => a = c >> If some of this properties are violated, we're talking of something >> else.Sorry, that won't do it. You haven't defined equality, or given any way of deciding whether two entities are equal. What you have listed are three *properties* of equality, namely: - reflexivity (a = a) - symmetry (if a = b then b = a) - transitivity (if a = b and b = c then a = c) But those three properties apply to any equivalence relation, not just equality. Examples: "both are odd" (of integers) "have the same birthday" (of people) "is congruent to" (of triangles) "is in the same tax bracket" (of tax payers) "has the same length" (of pieces of string) "both contain chocolate" (of cakes) For example, if we define the operator "~" to mean "has the same genes" (to be precise: the same genotype), then if Fred and Barney are identical twins we have: Fred ~ Fred Fred ~ Barney and Barney ~ Fred Identical triplets are rare (at least among human beings), but if we clone Barney to get George, then we also have: Fred ~ Barney and Barney ~ George => Fred ~ George. So "have the same genes" meets all your conditions for equality, but isn't equality: the three brothers are very different. Fred lost his arm in a car crash, Barney is a hopeless alcoholic, and George is forty years younger than his two brothers.
On 4/27/2012 16:07, Steven D'Aprano wrote: > On Fri, 27 Apr 2012 14:17:48 +0200, Kiuhnm wrote: > >>>> Define your terms: what do you mean by "equal"? >>> >>> a and b are equal iff >> >> Nope. What I meant is that we can talk of equality whenever... >> >>> a = a >>> a = b => b = a >>> a = b and b = c => a = c >>> If some of this properties are violated, we're talking of something >>> else. > > Sorry, that won't do it. You haven't defined equality, or given any way > of deciding whether two entities are equal. What you have listed are > three *properties* of equality, namely: > > - reflexivity (a = a) > - symmetry (if a = b then b = a) > - transitivity (if a = b and b = c then a = c) > > But those three properties apply to any equivalence relation, not just > equality. Examples:But that's what equality is in programming languages. You choose whatever you want. Just abide to those rules.> "both are odd" (of integers) > "have the same birthday" (of people) > "is congruent to" (of triangles) > "is in the same tax bracket" (of tax payers) > "has the same length" (of pieces of string) > "both contain chocolate" (of cakes)I can very well define a class Z_2 where 1 and 3 are equal (both are odd) and equality is defined as x % 2 == y % 2 This is why I keep saying that asking what 'equal' means lead us nowhere. Equality is whatever you see fit as long as you follows some rules.> For example, if we define the operator "~" to mean "has the same > genes" (to be precise: the same genotype), then if Fred and Barney are > identical twins we have: > > Fred ~ Fred > Fred ~ Barney and Barney ~ Fred > > Identical triplets are rare (at least among human beings), but if we > clone Barney to get George, then we also have: > > Fred ~ Barney and Barney ~ George => Fred ~ George. > > So "have the same genes" meets all your conditions for equality, but > isn't equality: the three brothers are very different. Fred lost his arm > in a car crash, Barney is a hopeless alcoholic, and George is forty years > younger than his two brothers./You/ decide if '~' is a good definition for equality in your case. If it isn't, then define it another way. What I'm saying is that equality is in the eye of the beholder. To me, in some situations, all odd numbers are the same. What's wrong with that? Kiuhnm
On Apr 27, 8:07 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote:
> Useful... maybe, conceptually sound... no.
> Conceptually, NaN is the class of all elements which are not numbers,
> therefore NaN = NaN.NaN isn't really the class of all elements which aren't numbers. NaN
is the result of a few specific IEEE 754 operations that cannot be
computed, like 0/0, and for which there's no other reasonable
substitute (e.g., infinity) for practical applications .
In the real world, if we were doing the math with pen and paper, we'd
stop as soon as we hit such an error. Equality is simply not defined
for the operations that can produce NaN, because we don't know to
perform those computations. So no, it doesn't conceptually follow
that NaN = NaN, what conceptually follows is the operation is
undefined because NaN causes a halt.
This is what programming languages ought to do if NaN is compared to
anything other than a (floating-point) number: disallow the operation
in the first place or toss an exception. Any code that tries such an
operation has a logic error and must be fixed.
However, when comparing NaN against floating point numbers, I don't
see why NaN == NaN returning false is any less conceptually correct
than any other possible result. NaN's very existence implicitly
declares that we're now making up the rules as we go along, so we
might as well pick the simplest set of functional rules.
Plus, floating point numbers violate our expectations of equality
anyway, frequently in surprising ways. 0.1 + 0.1 + 0.1 == 0.3 is
true with pen and paper, but likely false on your computer. It's even
potentially possible to compare two floating point variables twice and
get different results each time[1]! As such, we'd have this problem
with defining equality even if NaN didn't exist. We must treat
floating-point numbers as a special case in order to write useful
working programs. This includes defining equality in a way that's
different from what works for nearly every other data type.
Adam
[1] Due to register spilling causing intermediate rounding. This
could happen with the x87 FPU since the registers were 80-bits wide
but values were stored in RAM as 64-bits. This behavior is less
common now, but hardly impossible.
On Fri, Apr 27, 2012 at 9:39 AM, Adam Skutt wrote: > On Apr 27, 8:07 am, Kiuhnm <kiuhnm03.4t.yahoo.it> wrote: >> Useful... maybe, conceptually sound... no. >> Conceptually, NaN is the class of all elements which are not numbers, >> therefore NaN = NaN. > > NaN isn't really the class of all elements which aren't numbers. NaN > is the result of a few specific IEEE 754 operations that cannot be > computed, like 0/0, and for which there's no other reasonable > substitute (e.g., infinity) for practical applications . > > In the real world, if we were doing the math with pen and paper, we'd > stop as soon as we hit such an error. Equality is simply not defined > for the operations that can produce NaN, because we don't know to > perform those computations. So no, it doesn't conceptually follow > that NaN = NaN, what conceptually follows is the operation is > undefined because NaN causes a halt. > > This is what programming languages ought to do if NaN is compared to > anything other than a (floating-point) number: disallow the operation > in the first place or toss an exception. Any code that tries such an > operation has a logic error and must be fixed.NaNs do not signify errors (for instance, a NaN could result simply from subtracting one Inf from another), and they do not necessarily imply that the calculation should halt. They are propagating values indicating the lack of a concrete value, and frequently they can reasonably be ignored. If you need to know about a NaN immediately, then you can trap them with fpectl. If you don't, then you let it propagate and check for it at the end of the calculation. If instead they raised exceptions by default, then we would need to wrap virtually every floating point operation in a try-except, which would quickly become a mess. See also: http://grouper.ieee.org/groups/754/faq.html#e...
Adam Skutt wrote:
[ ... ]
> In the real world, if we were doing the math with pen and paper, we'd
> stop as soon as we hit such an error. Equality is simply not defined
> for the operations that can produce NaN, because we don't know to
> perform those computations. So no, it doesn't conceptually follow
> that NaN = NaN, what conceptually follows is the operation is
> undefined because NaN causes a halt.
>
> This is what programming languages ought to do if NaN is compared to
> anything other than a (floating-point) number: disallow the operation
> in the first place or toss an exception. Any code that tries such an
> operation has a logic error and must be fixed.There was a time when subtracting 5 from 3 would have been a logic error.
Your phrase "if we were doing the math ..." lies behind most of the history
of math, esp. as it concerns arithmetic. Mathematicians kept extending the
definitions so that they wouldn't have to stop. Feynman's _Lectures on
Physics_, chapter 22, "Algebra" gives a stellar account of the whole
process.
Mel.
On 4/27/2012 17:39, Adam Skutt wrote: > On Apr 27, 8:07 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote: >> Useful... maybe, conceptually sound... no. >> Conceptually, NaN is the class of all elements which are not numbers, >> therefore NaN = NaN. > > NaN isn't really the class of all elements which aren't numbers. NaN > is the result of a few specific IEEE 754 operations that cannot be > computed, like 0/0, and for which there's no other reasonable > substitute (e.g., infinity) for practical applications . > > In the real world, if we were doing the math with pen and paper, we'd > stop as soon as we hit such an error. Equality is simply not defined > for the operations that can produce NaN, because we don't know to > perform those computations. So no, it doesn't conceptually follow > that NaN = NaN, what conceptually follows is the operation is > undefined because NaN causes a halt.Mathematics is more than arithmetics with real numbers. We can use FP too (we actually do that!). We can say that NaN = NaN but that's just an exception we're willing to make. We shouldn't say that the equivalence relation rules shouldn't be followed just because *sometimes* we break them.> This is what programming languages ought to do if NaN is compared to > anything other than a (floating-point) number: disallow the operation > in the first place or toss an exception. Any code that tries such an > operation has a logic error and must be fixed. > > However, when comparing NaN against floating point numbers, I don't > see why NaN == NaN returning false is any less conceptually correct > than any other possible result. NaN's very existence implicitly > declares that we're now making up the rules as we go along, so we > might as well pick the simplest set of functional rules. > > Plus, floating point numbers violate our expectations of equality > anyway, frequently in surprising ways. 0.1 + 0.1 + 0.1 == 0.3 is > true with pen and paper, but likely false on your computer.Maybe wrong expectations of equality, since 0.1 (the real number) is /not/ a floating point. Don't confuse the representation of floating points with the floating point themselves.> It's even > potentially possible to compare two floating point variables twice and > get different results each time[1]!We should look at the specification and not the single implementations.> As such, we'd have this problem > with defining equality even if NaN didn't exist. We must treat > floating-point numbers as a special case in order to write useful > working programs. This includes defining equality in a way that's > different from what works for nearly every other data type. > > Adam > > [1] Due to register spilling causing intermediate rounding. This > could happen with the x87 FPU since the registers were 80-bits wide > but values were stored in RAM as 64-bits. This behavior is less > common now, but hardly impossible.Kiuhnm
On 4/28/2012 4:47 AM, Kiuhnm wrote:
> On 4/27/2012 17:39, Adam Skutt wrote:
>> On Apr 27, 8:07 am, Kiuhnm<kiuhnm03.4t.yahoo.it> wrote:
>>> Useful... maybe, conceptually sound... no.
>>> Conceptually, NaN is the class of all elements which are not numbers,
>>> therefore NaN = NaN.
>>
>> NaN isn't really the class of all elements which aren't numbers. NaN
>> is the result of a few specific IEEE 754 operations that cannot be
>> computed, like 0/0, and for which there's no other reasonable
>> substitute (e.g., infinity) for practical applications .
>>
>> In the real world, if we were doing the math with pen and paper, we'd
>> stop as soon as we hit such an error. Equality is simply not defined
>> for the operations that can produce NaN, because we don't know to
>> perform those computations. So no, it doesn't conceptually follow
>> that NaN = NaN, what conceptually follows is the operation is
>> undefined because NaN causes a halt.
>
> Mathematics is more than arithmetics with real numbers. We can use FP
> too (we actually do that!). We can say that NaN = NaN but that's just an
> exception we're willing to make. We shouldn't say that the equivalence
> relation rules shouldn't be followed just because *sometimes* we break
> them.
>
>> This is what programming languages ought to do if NaN is compared to
>> anything other than a (floating-point) number: disallow the operation
>> in the first place or toss an exception.If you do a signaling floating point comparison on IEEE floating
point numbers, you do get an exception. On some FPUs, though,
signaling operations are slower. On superscalar CPUs, exact
floating point exceptions are tough to implement. They are
done right on x86 machines, mostly for backwards compatibility.
This requires an elaborate "retirement unit" to unwind the
state of the CPU after a floating point exception. DEC Alphas
didn't have that; SPARC and MIPS machines varied by model.
ARM machines in their better modes do have that.
Most game console FPUs do not have a full IEEE implementation.
Proper language support for floating point exceptions varies
with the platform. Microsoft C++ on Windows does support
getting it right. (I had to deal with this once in a physics
engine, where an overflow or a NaN merely indicated that a
shorter time step was required.) But even there, it's
an OS exception, like a signal, not a language-level
exception. Other than Ada, which requires it, few
languages handle such exceptions as language level
exceptions.
John Nagle
On 4/26/2012 4:45 AM, Adam Skutt wrote:
> On Apr 26, 1:48 am, John Nagle< wrote:
>> On 4/25/2012 5:01 PM, Steven D'Aprano wrote:
>>
>>> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote:
>>
>>>> Though, maybe it's better to use a different keyword than 'is' though,
>>>> due to the plain English
>>>> connotations of the term; I like 'sameobj' personally, for whatever
>>>> little it matters. Really, I think taking away the 'is' operator
>>>> altogether is better, so the only way to test identity is:
>>>> id(x) == id(y)
>>
>>> Four reasons why that's a bad idea:
>>
>>> 1) The "is" operator is fast, because it can be implemented directly by
>>> the interpreter as a simple pointer comparison (or equivalent).
>>
>> This assumes that everything is, internally, an object. In CPython,
>> that's the case, because Python is a naive interpreter and everything,
>> including numbers, is "boxed". That's not true of PyPy or Shed Skin.
>> So does "is" have to force the creation of a temporary boxed object?
>
> That's what C# does AFAIK. Java defines '==' as value comparison for
> primitives and '==' as identity comparison for objects, but I don't
> exactly know how one would do that in Python.I would suggest that "is" raise ValueError for the ambiguous cases.
If both operands are immutable, "is" should raise ValueError.
That's the case where the internal representation of immutables
shows through.
If this breaks a program, it was broken anyway. It will
catch bad comparisons like
if x is 1000 :
...
which is implementation dependent.
John Nagle
On Apr 26, 2:31 pm, John Nagle wrote: > On 4/26/2012 4:45 AM, Adam Skutt wrote: > > On Apr 26, 1:48 am, John Nagle< wrote: > >> On 4/25/2012 5:01 PM, Steven D'Aprano wrote: > > >>> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote: > > >>>> Though, maybe it's better to use a different keyword than 'is' though, > >>>> due to the plain English > >>>> connotations of the term; I like 'sameobj' personally, for whatever > >>>> little it matters. Really, I think taking away the 'is' operator > >>>> altogether is better, so the only way to test identity is: > >>>> id(x) == id(y) > > >>> Four reasons why that's a bad idea: > > >>> 1) The "is" operator is fast, because it can be implemented directly by > >>> the interpreter as a simple pointer comparison (or equivalent). > > >> This assumes that everything is, internally, an object. In CPython, > >> that's the case, because Python is a naive interpreter and everything, > >> including numbers, is "boxed". That's not true of PyPy or Shed Skin. > >> So does "is" have to force the creation of a temporary boxed object? > > > That's what C# does AFAIK. Java defines '==' as value comparison for > > primitives and '==' as identity comparison for objects, but I don't > > exactly know how one would do that in Python. > > I would suggest that "is" raise ValueError for the ambiguous cases. > If both operands are immutable, "is" should raise ValueError.I don't know how you would easily detect user-defined immutable types, nor do I see why such an operation should be an error. I think it would end up violating the principal of least surprise in a lot of cases, especially when talking about things like immutable sets, maps, or other complicated data structures. What I think you want is what I said above: ValueError raised when either operand is a /temporary/ object. Really, it should probably be a parse-time error, since you could (and should) make the determination at parse time.> That's the case where the internal representation of immutables > shows through.You still have this problem with mutable temporary objects, as my little snipped showed. You're still going to get a result that's inconsistent and/or "surprising" sooner or later. The problem is the temporary nature of the object, not mutability.> > If this breaks a program, it was broken anyway. It will > catch bad comparisons like > > if x is 1000 : > ... > > which is implementation dependent.Yes, I agree that a correct fix shouldn't break anything except already broken programs. Adam
On Thu, Apr 26, 2012 at 1:34 PM, Adam Skutt wrote:
> What I think you want is what I said above: ValueError raised when
> either operand is a /temporary/ object. Really, it should probably be
> a parse-time error, since you could (and should) make the
> determination at parse time.I'm not sure precisely what you mean by "temporary object", so I am
taking it to mean an object that is referenced only by the VM stack
(or something equivalent for other implementations).
In that case: no, you can't. Take "f() is g()", where the code
objects of f and g are supplied at runtime. Are the objects returned
by either of those expressions "temporary"? Without being able to do
static analysis of the code of f and g, there is no way to know.
On Fri, Apr 27, 2012 at 7:39 AM, Ian Kelly wrote:
> I'm not sure precisely what you mean by "temporary object", so I am
> taking it to mean an object that is referenced only by the VM stack
> (or something equivalent for other implementations).
>
> In that case: no, you can't. Take "f() is g()", where the code
> objects of f and g are supplied at runtime. Are the objects returned
> by either of those expressions "temporary"? Without being able to do
> static analysis of the code of f and g, there is no way to know.The expression itself will have references to all its operands (at
least conceptually). If their refcounts are precisely 1, then the
objects are temporaries and will be disposable as soon as the
expression's fully evaluated.
ChrisA
On Thu, Apr 26, 2012 at 3:51 PM, Chris Angelico wrote: > On Fri, Apr 27, 2012 at 7:39 AM, Ian Kelly wrote: >> I'm not sure precisely what you mean by "temporary object", so I am >> taking it to mean an object that is referenced only by the VM stack >> (or something equivalent for other implementations). >> >> In that case: no, you can't. Take "f() is g()", where the code >> objects of f and g are supplied at runtime. Are the objects returned >> by either of those expressions "temporary"? Without being able to do >> static analysis of the code of f and g, there is no way to know. > > The expression itself will have references to all its operands (at > least conceptually).Yes, the references on the VM stack.> If their refcounts are precisely 1, then the > objects are temporaries and will be disposable as soon as the > expression's fully evaluated.You can't check ref counts at parse time.
On Thu, Apr 26, 2012 at 5:39 PM, Ian Kelly wrote:
> On Thu, Apr 26, 2012 at 1:34 PM, Adam Skutt wrote:
>> What I think you want is what I said above: ValueError raised when
>> either operand is a /temporary/ object. Really, it should probably be
>> a parse-time error, since you could (and should) make the
>> determination at parse time.
>
> I'm not sure precisely what you mean by "temporary object", so I am
> taking it to mean an object that is referenced only by the VM stack
> (or something equivalent for other implementations).
>
> In that case: no, you can't. Take "f() is g()", where the code
> objects of f and g are supplied at runtime. Are the objects returned
> by either of those expressions "temporary"? Without being able to do
> static analysis of the code of f and g, there is no way to know.A temporary object would be anything that need not be alive longer
than the duration of the 'is' operation. I am not a Python language
expert so that definition may not be exactly correct or workable for
Python.
In the example:
>>> [1, 2] is [3, 4]
[1,2] and [3,4] don't need to exist before the 'is' operation, nor
after it, so they are temporaries.
Adam
On Thu, 26 Apr 2012 11:31:39 -0700, John Nagle wrote: > I would suggest that "is" raise ValueError for the ambiguous cases. > If both operands are immutable, "is" should raise ValueError. That's the > case where the internal representation of immutables shows through.This breaks one of the most common uses of "is", i.e. "x is None". And it doesn't prevent a programmer from consfusing "is" and "==" with mutable types.> If this breaks a program, it was broken anyway. It will > catch bad comparisons like > > if x is 1000 : > ... > > which is implementation dependent.The only way to completely eliminate bugs caused by the programmer relying upon implementation-dependent behaviour is to eliminate implementation- dependent behaviour altogether, which is throwing the baby out with the bath water, IMHO. All practical languages have some implementation-defined behaviour, often far more problematic than Python's.
Nobody writes:
> All practical languages have some implementation-defined behaviour, often
> far more problematic than Python's.The usual reason for accepting implementation-defined behavior is to
enable low-level efficiency hacks written for specific machines. C and
C++ are used for that sort of purpose, so they leave many things
implementation-defined. Python doesn't have the same goals and should
leave less up to the implementation. Java, Ada, Standard ML, etc. all
try to eliminate implementation-defined behavior in the language much
more than Python does. I don't have any idea why you consider that to
be "throwing the baby out with the bath water".
On 4/27/12 12:07 AM, Paul Rubin wrote: > Nobody< writes: >> All practical languages have some implementation-defined behaviour, often >> far more problematic than Python's. > > The usual reason for accepting implementation-defined behavior is to > enable low-level efficiency hacks written for specific machines. C and > C++ are used for that sort of purpose, so they leave many things > implementation-defined. Python doesn't have the same goals and should > leave less up to the implementation. Java, Ada, Standard ML, etc. all > try to eliminate implementation-defined behavior in the language much > more than Python does. I don't have any idea why you consider that to > be "throwing the baby out with the bath water".I think there are two implementation-defined behaviors that are being discussed in this thread. One is that some immutable objects like the empty tuple may be interned such that all instances of them are the same object and have the same identity. This is allowed for efficiency reasons. CPython has used this freedom to good effect. The optimization is not theoretical. The other is that identities may be reused by different objects that do no coexist at the same time. This is to permit implementations where the ID is the address of the object in memory, like CPython. But other implementations with different memory models (including ones where "address in memory" doesn't make any sense) may forbid reuse of IDs. This allows alternative implementations like Jython and IronPython. There are specific, deliberate, practical consequences of those two implementation-defined behaviors. These are the babies that you would be throwing out.
On Thu, 26 Apr 2012 11:31:39 -0700, John Nagle wrote: > I would suggest that "is" raise ValueError for the ambiguous cases. > If both operands are immutable, "is" should raise ValueError. That's the > case where the internal representation of immutables shows through.You've already made this suggestion before. Unfortunately you failed to think it through: it would break *nearly all Python code*, and not just "broken" code. It would break code that relies on documented language features. It would break code that applies a standard Python idiom. I count at least 638 places where your suggestion would break the standard library. [steve@ando ~]$ cd /usr/local/lib/python3.2/ [steve@ando python3.2]$ grep "if .* is None:" *.py | wc -l 638 That's an average of four breakages per module. > If this breaks a program, it was broken anyway. Incorrect. Your suggestion breaks working code for no good reason. Astonishingly, your suggestion doesn't break code that actually is broken: def spam(arg=None): if arg == None: ... actually is broken, since it doesn't correctly test for the sentinel. You can break it by passing an object which compares equal to None but isn't actually None.
On 4/26/2012 1:48 AM, John Nagle wrote: > This assumes that everything is, internally, an object. In CPython, > that's the case, because Python is a naive interpreter and everything, > including numbers, is "boxed". That's not true of PyPy or Shed Skin. > So does "is" have to force the creation of a temporary boxed object?Python Language Reference "3.1. Objects, values and types Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects. ... Every object has an identity, a type and a value. An object’s identity never changes once it has been created; ... The ‘is‘ operator compares the identity of two objects; the id()function* returns an integer representing its identity#." [notes added] * the built-in function bound to 'id' on startup. # and that integer must not change for the lifetime of the object. None of the above is CPython implementation detail. What the spec 'forces' is observable behavior. Internal shortcuts are allowed. If an interpreter 'knows' that 'a' represents anything other than None, than it can evaluate 'a is None' as False without boxing 'a' anymore than it is already. The object model above allows for an object to represent or consist of a collection of raw, non-object internal data fields. A list is a sequence of Python objects. A string is not; it is a sequence of 'characters' or 'bytes'. Structs and arrays, including numpy arrays, are similar in containing non-object values. However, when single characters, bytes, or other binary values are extracted and exposed to Python code by indexing or iteration, they must be objectivized (or at least are in CPython). Worrying about 'is' and 'id' forcing objectness is misplaced. Except for comparing an object to a pre-defined constant or sentinel, 'is' is mainly used for introspection and testing. The main use of 'id()' is to make unambiguous string representations of functions, classes, and modules. The real 'culprits' for (potentially) forcing objectness are everyday indexing and iteration. Numpy avoids boxing internal binary values by providing functions that operate on numpy arrays *without* exposing the internal values at the Python level. I believe that psycho and now pypy can analyze a function to determine whether machine ints and floats can used without being boxed,
On 4/26/2012 2:01, Steven D'Aprano wrote:
> On Wed, 25 Apr 2012 13:49:24 -0700, Adam Skutt wrote:
>
>> Though, maybe it's better to use a different keyword than 'is' though,
>> due to the plain English
>> connotations of the term; I like 'sameobj' personally, for whatever
>> little it matters. Really, I think taking away the 'is' operator
>> altogether is better, so the only way to test identity is:
>> id(x) == id(y)
>
> Four reasons why that's a bad idea:
>
> 1) The "is" operator is fast, because it can be implemented directly by
> the interpreter as a simple pointer comparison (or equivalent). The id()
> idiom is slow, because it involves two global lookups and an equality
> comparison. Inside a tight loop, that can make a big difference in speed.
>
> 2) The "is" operator always has the exact same semantics and cannot be
> overridden. The id() function can be monkey-patched.
>
> 3) The "is" idiom semantics is direct: "a is b" directly tests the thing
> you want to test, namely whether a is b. The id() idiom is indirect:
> "id(a) == id(b)" only indirectly tests whether a is b.
>
> 4) The id() idiom already breaks if you replace names a, b with
> expressions:
>
>>>> id([1,2]) == id([3,4])
> TrueYou forgot one:
5) It would be a pain to write (and read)
if id(my_obj) == id(None)
and so anyone would come up with his/her own same_as(),
identical_to(), same_inst() and so on...
Kiuhnm
In article , Paul Rubin lid> wrote: >Kiuhnm <kiuhnm03.4t.yahoo.it> writes: >> I can't think of a single case where 'is' is ill-defined. > >If I can't predict the output of > > print (20+30 is 30+20) # check whether addition is commutative > print (20*30 is 30*20) # check whether multiplication is commutative > >by just reading the language definition and the code, I'd have to say >"is" is ill-defined.The output depends whether the compiler is clever enough to realise that the outcome of the expressions is the same, such that only one object needs to be created. What is ill here is the users understanding of when it is appropriate to use "is". Asking about identity of temporary objects fully under control of the compiler is just sick. Groetjes Albert
dmitrey writes:
> I have spent some time searching for a bug in my code, it was due to
> different work of "is" with () and []:
>>>> () is ()
> True
>>>> [] is []
> False
>
> (Python 2.7.2+ (default, Oct 4 2011, 20:03:08)
> [GCC 4.6.1] )
>
> Is this what it should be or maybe yielding unified result is better?Tuples are immutable, while lists are not. Because tuples are immutable,
there is no point in having several empty tuples: one value is enough,
and "is" recognizes this. Lists are different, they can change "value"
(contents), and several names can be bound to the same list (in other
words, you can have side-effects on a list). With:
a = []
b = a
a.append(42)
print b
you get [42]. If instead you do:
a = []
b = []
a.append(42)
print b
you get the expected []. I.e., you need several distinct empty lists,
to make sure they can change value independently.
(In case you wonder: lists in Python are not linked lists of cons-cells,
they are more monolithic structures, and two lists cannot share a common
tail.)
-- Alain.
On Sat, 21 Apr 2012 04:25:36 +0100 Rotwang wrote: > On 21/04/2012 01:01, Roy Smith wrote: > > In article<, > > Alain Ketterlin< wrote: > > > >> Tuples are immutable, while lists are not. > > > > If you really want to have fun, consider this classic paradox: > > > >>>> [] is [] > > False > >>>> id([]) == id([]) > > True > > Huh. This is not what I would have expected. What gives? >I'm just guessing here, but as id is "is guaranteed to be unique among simultaneously existing objects", the list literals on each side of the comparison do not simultaneously exist; presumably the first one is created, id'ed, then garbage collected (as it is not named), then the other one is created and gets the same id just freed up by the first one. OTOH, "is" hangs on to both objects in order to compare them. c.f.:>>> l, m = [], [] >>> id(l) == id(m)False Is that right?
On 04/20/2012 11:25 PM, Rotwang wrote: > On 21/04/2012 01:01, Roy Smith wrote: >> In article<, >> Alain Ketterlin< wrote: >> >>> Tuples are immutable, while lists are not. >> >> If you really want to have fun, consider this classic paradox: >> >>>>> [] is [] >> False >>>>> id([]) == id([]) >> True > > Huh. This is not what I would have expected. What gives? >It'd be easier to respond if you would say what's confusing to you about that result. The first is exactly what I'd expect, and the second is implementation dependent. [] is [] produces two independent lists, then compares their ID, then discards them. So of course they must be different. id([]) prepares a list object and figures out its id. Then discards the object. You do it a second time, and you *might* get a new object with the same id. After all, the first one is gone now, so there's no harm in re-using the id. In particular, the CPython implementation currently uses the address of the object as the ID, so if the memory is available, the next allocation just might get the same memory. Remember that the only guarantee on id() is that it won't assign the same number to two objects that exist at the same time. Once an object is gone, its ID may be reused right away, or after a while, or never.
Am 21.04.2012 05:25, schrieb Rotwang: > On 21/04/2012 01:01, Roy Smith wrote: >> In article<, >> Alain Ketterlin< wrote: >> >>> Tuples are immutable, while lists are not. >> >> If you really want to have fun, consider this classic paradox: >> >>>>> [] is [] >> False >>>>> id([]) == id([]) >> True > > Huh. This is not what I would have expected. What gives?This happens only because the first [] gets destroyed after evaluation of id([]). The second [] then by accident gets the same id as the first one had.>>> a = [] >>> b = [] >>> id(a) == id(b)False Greetings
Le samedi 21 avril 2012 10:46:39 UTC+2, Alexander Blinne a écrit : > Am 21.04.2012 05:25, schrieb Rotwang: > > This happens only because the first [] gets destroyed after evaluation > of id([]). The second [] then by accident gets the same id as the first > one had. > > >>> a = [] > >>> b = [] > >>> id(a) == id(b) > False > > GreetingsHi, I played (python3.2) a bit on that and : case 1) Ok to me (right hand side is a tuple, whose elements are evaluated one per one and have same effect as your explanation (first [] is destroyed right before the second one is created) :>>> x, y = id([]), id([]) >>> x == yTrue >>> case 2) also ok to me:>>> x = id([]) ; y = id([]) >>> x == yTrue >>> case 3) NOT ok to me :>>> x = id([]) >>> y = id([]) >>> x == yFalse >>> case 4) ok to me : >>> def f(): x = id([]) y = id([]) return x == y >>> f() True >>> I'd have thought that cases 2&3 are totally, while 3&4 nearly, syntactically equal and that case 3 is the incorrect result.. how would you explain case 3 vs cases 2 and 4 ?? regards, gs.
On Sat, Apr 21, 2012 at 10:51 PM, gst wrote:
> case 2) also ok to me:
>
>>>> x = id([]) ; y = id([])
>>>> x == y
> True
>>>>
>
>
> case 3) NOT ok to me :
>
>>>> x = id([])
>>>> y = id([])
>>>> x == y
> False
>>>>The fact that ids get reused at all is an implementation detail ONLY.
In CPython, id() returns the object's memory address, but in other
Python implementations, it returns something else (one (Jython??)
returns a sequential number, never reused).
Here's my take on the difference here. You did all of these in a
single session, so by the time you got to case 3, x already contained
a value (the previous integer). The allocation and deallocation of
integer objects created your different behavior. I tried these from
command-line Python (3.2 on Windows, fwiw), restarting the interpreter
between, and got consistent results.
The main thing to know, though, is that the id MAY be reused once the
object is destroyed, but any reuse should be considered utterly
coincidental. A Python could be completely compliant with an id
implementation in which object type gets encoded into it somehow, but
programs written to make use of that information would be just as
vulnerable as those making use of reuse.
ChrisA
Am 21.04.2012 14:51, schrieb gst:
> Hi,
>
> I played (python3.2) a bit on that and :
>
> case 1) Ok to me (right hand side is a tuple, whose elements are evaluated one per one and have same effect as your explanation (first [] is destroyed right before the second one is created) :
>
>>>> x, y = id([]), id([])
>>>> x == y
> True
>>>>
>
>
> case 2) also ok to me:
>
>>>> x = id([]) ; y = id([])
>>>> x == y
> True
>>>>
>
>
> case 3) NOT ok to me :
>
>>>> x = id([])
>>>> y = id([])
>>>> x == y
> False
>>>>
>
>
> case 4) ok to me :
>
>>>> def f():
> x = id([])
> y = id([])
> return x == y
>
>>>> f()
> True
>>>>
>
>
> I'd have thought that cases 2&3 are totally, while 3&4 nearly, syntactically equal and that case 3 is the incorrect result..
>
> how would you explain case 3 vs cases 2 and 4 ??It is simply not defined if, after creation and destruction of an empty
list with some id, a newly created empty list should carry the same id
or not. In cases 1,2 and 4 it happens, but in case 3 it doesn't. This is
simply an implementation detail and neither behaviour is right or wrong.
Alex
On 2012-04-20, dmitrey wrote: > I have spent some time searching for a bug in my code, it was due to > different work of "is" with () and []: >>>> () is () > TrueYou should better not rely on that result. I would consider it to be an implementation detail. I may be wrong, but would an implementation that results in () is () ==> False be correct or is the result True really demanded by the language specification?>>>> [] is [] > FalseSame for that.> > (Python 2.7.2+ (default, Oct 4 2011, 20:03:08) > [GCC 4.6.1] ) > > Is this what it should be or maybe yielding unified result is better?See above. Bernd
On 04/21/2012 09:02 AM, Bernd Nawothnig wrote: > On 2012-04-20, dmitrey wrote: >> I have spent some time searching for a bug in my code, it was due to >> different work of "is" with () and []: >>>>> () is () >> True > You should better not rely on that result. I would consider it to be > an implementation detail. I may be wrong, but would an implementation > that results in > > () is () ==> False > > be correct or is the result True really demanded by the language > specification?You're correct, the behavior is undefined. An implementation may happen to produce either True or False.>>>>> [] is [] >> False > Same for that.Here I have to disagree. If an implementation reused the list object for two simultaneously-existing instances, it would violate first principles. The distinction is simply that () is immutable, so the implementation *may* choose to reuse the same one.
On 04/21/2012 09:48 AM, Bernd Nawothnig wrote: > On Sat, Apr 21, 2012 at 09:21:50AM -0400, Dave Angel wrote: >>>>>>> [] is [] >>>> False >>> Same for that. >> >> Here I have to disagree. If an implementation reused the list object >> for two simultaneously-existing instances, it would violate first >> principles. > > Hm. > > Even if there is no chance to reach any of these two lists after the > comparison because *no* references to them exist and both *must* be > identical because they are both noted as the same literals? > > If any references exist, no question, that is pretty clear and > understandable. But in that special case? >You forgot to CC the list on your two messages to me. Clearly, False has to be a valid return result. So I assume your question is why shouldn't an implementation be permitted to return True, in other words why couldn't it be ambiguous, like the immutable case. Why would you (a hypothetical compiler writer) write an optimizer to look for such a special case like this, when the code would probably never appear in a real program, and certainly not in a performance critical portion of one. And if you did write one, why would you have it produce a different result than the non-optimized version? Why not have it return 42 if that's the goal?
On Sat, Apr 21, 2012 at 03:43:03PM -0400, Dave Angel wrote: > On 04/21/2012 09:48 AM, Bernd Nawothnig wrote: > > On Sat, Apr 21, 2012 at 09:21:50AM -0400, Dave Angel wrote: > >>>>>>> [] is [] > >>>> False > >>> Same for that. > >> > >> Here I have to disagree. If an implementation reused the list object > >> for two simultaneously-existing instances, it would violate first > >> principles. > > > > Hm. > > > > Even if there is no chance to reach any of these two lists after the > > comparison because *no* references to them exist and both *must* be > > identical because they are both noted as the same literals? > > > > If any references exist, no question, that is pretty clear and > > understandable. But in that special case? > > > > You forgot to CC the list on your two messages to me.Sorry, I'm reading the list via usenet gateway. Hopefully it works now.> Clearly, False has to be a valid return result. So I assume your> question is why shouldn't an implementation be permitted to return True, > in other words why couldn't it be ambiguous, like the immutable case.Yes.> Why would you (a hypothetical compiler writer) write an optimizer to> look for such a special case like this, when the code would probably > never appear in a real program, and certainly not in a performance > critical portion of one. > And if you did write one, why would you have it produce a different > result than the non-optimized version? Why not have it return 42 if > that's the goal?My original statement was: don't rely on such behaviour, it is an implementation detail. Your argument above was: it would violate first principles. And I still don't see that point. The comparison [] is [] maybe totally useless, of course, but which first principle would be violated by a compiler that lets that expression evaluate to True? Where can I read that the result *must* be False? Otherwise it is simply an ambigious and not clearly defined expression like () is () Bernd
On 4/21/12 10:15 PM, Bernd Nawothnig wrote: > Your argument above was: it would violate first principles. And I still don't > see that point. The comparison [] is [] maybe totally useless, of course, but > which first principle would be violated by a compiler that lets that > expression evaluate to True? > > Where can I read that the result *must* be False?http://docs.python.org/reference/expressions.... "A list display yields a new list object."
On 4/21/2012 9:02 AM, Bernd Nawothnig wrote: > You should better not rely on that result. I would consider it to be > an implementation detail. I may be wrong, but would an implementation > that results in > > () is () ==> False > > be correct or is the result True really demanded by the language > specification?To make sure that the result is not due to ref counting garbage collection in the middle of the expression, one must test like so:>>> a=() >>> b=() >>> a is bTrue This is explicitly an implementation detail, something that *may* be done. I am not sure if the above has always been the case for CPython. CPython's set of pre-built ints has been expended. I think string interning has also changed.
On Sat, 21 Apr 2012 15:02:00 +0200, Bernd Nawothnig wrote: > On 2012-04-20, dmitrey wrote: >> I have spent some time searching for a bug in my code, it was due to >> different work of "is" with () and []: >>>>> () is () >> True > > You should better not rely on that result. I would consider it to be an > implementation detail. I may be wrong, but would an implementation that > results in > > () is () ==> False > > be correct or is the result True really demanded by the language > specification?It would be correct, and the result True absolutely is NOT demanded by the language. For example, Jython does not make that same optimization: steve@runes:~$ jython Jython 2.5.1+ (Release_2_5_1, Aug 4 2010, 07:18:19) [OpenJDK Client VM (Sun Microsystems Inc.)] on java1.6.0_18 Type "help", "copyright", "credits" or "license" for more information. >>> () is () False so code which relies on it is already broken. Python the language makes no promises that literals will always evaluate to the same object. The ONLY such promises that it makes are that a small handful of special constants are singletons: None NotImplemented True False and perhaps one or two others. Everything else is an accident of the implementation. The CPython interpreter is especially aggressive in optimizing multiple literals in the same line. Compare this: >>> x = 3.1; y = 3.1; x is y True with this:>>> x = 3.1 >>> y = 3.1 >>> x is yFalse Again, this is an accident of implementation, and cannot be relied on.
On Sun, Apr 22, 2012 at 1:14 PM, Steven D'Aprano
wrote:
> The CPython interpreter is especially aggressive in optimizing multiple
> literals in the same line. Compare this:
>
>>>> x = 3.1; y = 3.1; x is y
> True
>
> with this:
>
>>>> x = 3.1
>>>> y = 3.1
>>>> x is y
> False
>
>
> Again, this is an accident of implementation, and cannot be relied on.That's the interactive interpreter, which works on a basis of lines.
With .py files, both 2.6 and 3.2 on Windows appear to do the same
optimization at module level. But either way, that's optimization of
constants.
>>> x=3.1+1.0; y=3.1+1.0; x is y
False
>>> x=3.1; y=3.1; x+y is x+y
False
Which can have micro-optimization implications:
>>> x=1048576; y=1048576; x is y
True
>>> x=1<<20; y=1<<20; x is y
False
But if you're concerning yourself with this kind of optimization,
you're probably wasting your time. :)
ChrisA
On 20/04/2012 20:10, dmitrey wrote: > I have spent some time searching for a bug in my code, it was due to > different work of "is" with () and []: >>>> () is () > True >>>> [] is [] > False > > (Python 2.7.2+ (default, Oct 4 2011, 20:03:08) > [GCC 4.6.1] ) > > Is this what it should be or maybe yielding unified result is better? > D.Congratulations, you've managed to introduce the most useless thread on this ng/ml that I've come across in the ten years that I've been using (c)Python. I've never ever thought about using this weird thing called "id" in my code, (which comes later in all the responses) so why do some people think it's so important?
> > I have spent some time searching for a bug in my code, it was due to > > different work of "is" with () and []: > >>>> () is () > > True > >>>> [] is [] > > False > > > > (Python 2.7.2+ (default, Oct 4 2011, 20:03:08) > > [GCC 4.6.1] ) > > > > Is this what it should be or maybe yielding unified result is better? > > D. > > Congratulations, you've managed to introduce the most useless thread on > this ng/ml that I've come across in the ten years that I've been using > (c)Python. > > I've never ever thought about using this weird thing called "id" in my > code, (which comes later in all the responses) so why do some people > think it's so important? >Somewhere out there (possibly in an alternate universe) Bill Clinton is reading this thread and nodding vigorously about an argument around the [key]word "is". Ramit Ramit Prasad | JPMorgan Chase Investment Bank | Currencies Technology 712 Main Street | Houston, TX 77002 work phone: 713 - 216 - 5423 This email is confidential and subject to important disclaimers and conditions including on offers for the purchase or sale of securities, accuracy and completeness of information, viruses, confidentiality, legal privilege, and legal entity disclaimers, available at http://www.jpmorgan.com/pages/disclosures/ema....