8

This isn't a practical problem - I'm just curious about some strange behavior I've observed, and wondering if I understand the "is" operator correctly.

Here's some predictable Python interpreter output:

>>> True is True
True
>>> (1==1) is True
True

Now let's define a variable called True:

>>> True = 'abc'
>>> True == 'abc'
True
>>> True is 'abc'
True

The interpreter will still return "True" for boolean operations, but the results of boolean operations are considered equivalent to neither 'abc' nor True.

>>> (1==1)
True
>>> (1==1) is 'abc'
False
>>> (1==1) is True
False

Can anyone explain this strange behavior?

5
  • Odd that True isn't a reserved keyword (or whatever Python has). Commented Oct 28, 2013 at 15:56
  • 4
    @iamnotmaynard: True has become a keyword in Python 3, and the assignment would have raised SyntaxError: assignment to keyword. Commented Oct 28, 2013 at 15:57
  • most built-ins aren't keywords... you can reassign values to almost all of them. thankfully they are usually local, so when an unwary programmer does it in their function, it stays localized there. Commented Oct 28, 2013 at 16:59
  • @CorleyBrigman: for example id is a built in function, yet it's very often reassigned as object property (eg. in Django ORM). I'm not sure if it's "unwary", given that id() builtin isn't much useful for anything other than debugging. Commented Oct 28, 2013 at 17:21
  • as an object property, it's OK - this isn't C, you will always access it as self.id or object.id (in a namespace). though I have seen (and even written) code like for id in list_of_uids: etc., so it's a very good point that it's easy to do. Commented Oct 28, 2013 at 17:26

3 Answers 3

6

As often happens on here, I think I figured out the answer while I was typing up the question.

There are two "True"s: one is a boolean value, and the other is the variable called True; initially, they're equal to each other. This is why boolean operations like (1==1) can still return True even when the variable called True has been changed - they're returning the boolean value True. Yet they're not equal to the new value of the "True" variable, which is a string.

Sign up to request clarification or add additional context in comments.

5 Comments

I love it when that happens!
@JMK Yea, I was like, Imma be the first to answer this :P I didn't see his answer.
True is basically 1 with a fancy repr(). :-)
True is not 1 with a fancy repr(). It's a singleton, builtin value that happens to evaluate to 1 in a condition. As far as the interpreter is concerned, there's nothing wrong with using a different singleton value that also has this behavior such as if Ellipsis: print 3, apart from the obvious obscurity that makes this stylistically egregious. As you can tell from the Ellipsis example (where ellipsis also has a different slicing function in numpy arrays that nobody uses), the value of a singleton can't be thought of as like 1. For instance, compare True+1 and Ellipsis+1
isinstance(True, int)
5

That's happening is namespaceing and interactive console hiding it.

Initially you have normal True, which is part of __builtin__ module.

When you redefine True, you're actually defining it in a current module, which in that case is just default one __main__.

Thus you actually have two different objects. __builtin__.True and __main__.True.

In [1]: import __builtin__, __main__

In [2]: True = "a bad idea"

In [3]: __main__.True
Out[3]: 'a bad idea'

In [4]: __builtin__.True
Out[4]: True

3 Comments

that explains how you can hide it... but you can also do __builtin__.True = 4 if you want. so this doesn't completely answer the original question.
@CorleyBrigman: I'm explaining what is happening in the code in the original question.
i'm just saying that it's deeper than that... you can do __builtin__.True = 4, and 1==1 will still return True. something even below the builtin points to the real object, or it's handled in the python core.
2

To add a bit more to your own answer (should be a comment, but, long and needs formatting):

python2.7
...
>>> import __builtin__
>>> id(True)
7744528
>>> id(__builtin__.True)
7744528
>>> True = 'abc'
>>> id(True)
34386540544

The value from id is (essentially) the internal identity, or "true name" if you like, of an object in Python. (It's literally a C pointer turned into an integer.) The is test compares object-identity.

>>> 1==1
True
>>> id(1==1)
7744528

This shows that the boolean-result of a comparison is the "old" True, the one still available as __builtin__.True.

You re-bound the name __main__.True (your current module at the interpreter >>> prompt is __main__):

>>> True
'abc'
>>> __builtin__.True
True

and:

>>> import __main__
>>> id(__main__.True)
34386540544
>>> __main__.True
'abc'
>>> 

This same thing happens quite often in beginners' Python programs when they write functions like:

def foo(list):
    ...

list is a built-in function, but inside function foo, the name has been re-bound to the argument. Then somewhere in the ... part they get a surprise:

    x = list(y)

They expect this to invoke __builtin__.list, but it tries to call their local variable as a function instead.

(It's possible, but not generally good style, to import __builtin__ and call things through those names instead. It's also possible to re-bind the __builtin__ names, but that's an even worse idea. :-) )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.