34

As part of answering another question, I wrote the following code whose behaviour seems bizarre at first glance:

print True                    # outputs true
True = False;    print True   # outputs false
True = True;     print True   # outputs false
True = not True; print True   # outputs true

Can anyone explain this strange behaviour? I think it has something to do with Python's object model but I'm not sure.

It's version 2.5.2 under Cygwin.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 20
    Isn't that good old joke `#define true false` in action here? – Amarghosh Jan 13 '10 at 07:16
  • 2
    In version 3 the assigment True = False raises a sintax error, so i suppose that your question refers to version 2 – jab Jan 13 '10 at 07:17
  • 3
    what is strange about it? care to elaborate? – ghostdog74 Jan 13 '10 at 07:19
  • 14
    @Paul, how much do you know about BCPL? Or RCA1802 assembly? Or object-oriented COBOL? Or F# or Haskell or Forth? A high rep on SO is little indication of how knowledgeable someone is in a particular area. My Python knowledge is nowhere near as comprehensive as other areas and it's irrelevant, since SO is meant for *all* skill levels. On top of that, I'm not entirely certain where the difference between '=' and '==' even comes into it. Perhaps you could elucidate? – paxdiablo Jan 13 '10 at 13:17
  • 2
    @SLott, the question was more or less along the lines of: why is it even possible to assign to a constant? But @Balpha and @Ignacio cleared that up for me in that True and False aren't keywords (at least in Python 2), rather just names pointing to underlying objects (and the pointers can be changed). – paxdiablo Jan 13 '10 at 13:22
  • This is fixed in python 3, True is a keyword https://docs.python.org/3/reference/lexical_analysis.html#keywords – Andy Hayden Nov 01 '14 at 21:43

5 Answers5

78

Python has these two (among others) builtin objects. They are just objects; in the beginning, they don't have any names yet, but to know what we refer to, let's call them 0x600D and 0xBAD.

Before starting to execute a Python (2.x) script, the name True gets bound to the object 0x600D, and the name False gets bound to the object 0xBAD, so when the program refers to True, it looks at 0x600D.

Because 0x600D and 0xBAD know that they are usually used by the names True and False, that's what they output when they get printed, i.e. the __str__ method of 0x600D returns 'True' and so on.

True = False

now binds the name True to a different object. From now on, both names True and False refer to the same object 0xBAD, which, when printed, outputs False.

True = True

doesn't really do anything: It takes the object referred to by the name True, and binds the new (and old) name True to this object. Since (because of the previous step) True refers to 0xBAD before this, it still refers to 0xBAD after this. Hence, printing still outputs False.

True = not True

first takes the object that the name True is bound to, which is 0xBAD. It gives this object to the not operator. not doesn't care (or know) what name is used here to refer to 0xBAD, it just knows that when given 0xBAD it should return 0x600D. This return value is then given to the assignment operator =, binding the name True to this object.

Since the name True now once more refers to the object 0x600D, calling print True outputs True, and the world is good again.

balpha
  • 50,022
  • 18
  • 110
  • 131
45

Imagine this instead:

A = True
B = False

print A           # true
A = B;  print A   # false
A = A;  print A   # false, because A is still false from before
A = not A; print A # true, because A was false, so not A is true

The exact same thing is going on, but in your version it's confusing, because you don't expect that you can redefine True and False.

James
  • 8,512
  • 1
  • 26
  • 28
18

In 2.x, True and False are not keywords so it's possible to shadow the built-ins in this manner.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
12

You can check whether True/False is a keyword:

>>> import keyword
>>> keyword.iskeyword('True')
False

Since it's not (in my version), assigning True=False just means "True" is another "variable" name.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
0

You could easily restore the original values using simple Boolean comparisons:

True = 1==1
False = 1==0

Or by converting integer literals to bools:

True = bool(1)  # actually every number except 0 works
False = bool(0)
Byte Commander
  • 6,506
  • 6
  • 44
  • 71