Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can hexadecimal python integers access properties but not regular ints?

Decimal (i.e. non-prefixed) integers in Python seem to have fewer features than prefixed integers.

If I do 1.real I get a SyntaxError: invalid decimal literal. However, if I do 0x1.real, then I get no error and 1 is the result. (Same for 0b1.real and 0o1.real, though in Python2 01.real gives a syntax error as).

like image 960
Sir Nate Avatar asked Oct 18 '25 14:10

Sir Nate


1 Answers

It's because the 1. lead-in is being treated as a floating-point literal and r is not a valid decimal digit.

Hex literals, of the form you show, are integers, so are not ambiguous in being treated as a possible floating point (there is no 0x1.1).

If you use (1).real to specify that the literal is just the 1, it works fine.

The following (annotated) transcript may help:

>>> 0x1.real     # Can only be integer, so works as expected.
1

>>> 1.real       # Treated as floating point, "r" is invalid,
  File "<stdin>", line 1
    1.real
         ^
SyntaxError: invalid syntax

>>> (1).real     # Explicitly remove ambiguity, works.
1

>>> 0x1.1        # No float hex literal in this form.
  File "<stdin>", line 1
    0x1.1
        ^
SyntaxError: invalid syntax

For completeness, the lexical tokens for numerics (integral and float) can be found in the Python docs:

integer      ::=  decinteger | bininteger | octinteger | hexinteger
decinteger   ::=  nonzerodigit (["_"] digit)* | "0"+ (["_"] "0")*
bininteger   ::=  "0" ("b" | "B") (["_"] bindigit)+
octinteger   ::=  "0" ("o" | "O") (["_"] octdigit)+
hexinteger   ::=  "0" ("x" | "X") (["_"] hexdigit)+
nonzerodigit ::=  "1"..."9"
digit        ::=  "0"..."9"
bindigit     ::=  "0" | "1"
octdigit     ::=  "0"..."7"
hexdigit     ::=  digit | "a"..."f" | "A"..."F"

floatnumber   ::=  pointfloat | exponentfloat
pointfloat    ::=  [digitpart] fraction | digitpart "."
exponentfloat ::=  (digitpart | pointfloat) exponent
digitpart     ::=  digit (["_"] digit)*
fraction      ::=  "." digitpart
exponent      ::=  ("e" | "E") ["+" | "-"] digitpart

You can see there that the floats do not allow for hexadecimal prefixes, which is why it can safely assume the . in 0x1.real is not part of the literal value.

That's not the case for 1.real, it assumes that the . is preceding a fractional part of a float.

A clever-enough lexer could, of course, detect an invalid decimal digit immediately after the . and therefore assume it's a method to call on an integer. But that introduces complexity and may have other problematic edge cases.

like image 93
paxdiablo Avatar answered Oct 21 '25 03:10

paxdiablo