Live data from Hacker News

Weird Python Integers

kate.io

61–70 of 150 posts

Re: Weird Python Integers

#61

Summary: Integers in python are full blown objects. Small numbers are stored in a central preallocated table where each entry represents one number. Setting a variable to a small integer makes it point to an entry in that table. Multiple variables may point to the same small integer objects in that table. Fooling around with the table leads to funny results.

The real ProTip is in the comments. :)

Re: Weird Python Integers

#62
post #59

Earlier quoted context omitted.

That's only the case if you specifically treat them as full-blown objects. If you treat them as primitives they stay as primitives. Note that the type of these local variables is `Integer` rather than `int`.

Although you are technically correct (the best kind of correct), it is still a shortcoming of objects (or maybe just their implementation in Java). It is just a bad idea to implement the '==' operator using comparison on addresses instead of on values and I consider languages pretty low-level that do that. In a sane language == is always a function adhering the attributes of an equivalence relation (reflexivity, symm…

Why is value-equality inherently more sane than identity-equality, in an object-orientated language? Value-equality takes an unbound amount of time to compute, as well.

Re: Weird Python Integers

#64

I wrote a blog post about this in the past. It's really fun going through the oddities of the language like this. It caches small integers, but also literals used in the same interpreter context (I'm probably getting that last term wrong). You'll get different results if you run these in from the shell as opposed to executing a script, try it out! Here's a fun example >>> x = 256; x is 256 True >>> x = 257; x is 257…

The Python interpreter compiles programs into bytecode first, and the bytecode includes instructions that load a constant from the constant pool. As an example,

    >>> def f():
    ...     x = 2222; y=2222
    ...     return x is y
    ...
    >>> f()
    True
    >>> f.func_code.co_consts
    (None, 2222)
This last line is showing the constant pool, which is just a standard Python tuple.

I believe the REPL compiles each input in a new toplevel module context, so each input gets its own constant pool.

Functions get their own constant pools, which explains the following behavior:

    >>> if True:
    ...     def f():return 2222
    ...     def g():return 2222
    ...
    >>> f() is g()
    False

Re: Weird Python Integers

#65
post #59

Earlier quoted context omitted.

Although you are technically correct (the best kind of correct), it is still a shortcoming of objects (or maybe just their implementation in Java). It is just a bad idea to implement the '==' operator using comparison on addresses instead of on values and I consider languages pretty low-level that do that. In a sane language == is always a function adhering the attributes of an equivalence relation (reflexivity, symm…

Why is value-equality inherently more sane than identity-equality, in an object-orientated language? Value-equality takes an unbound amount of time to compute, as well.

I agree, that's why I revised my answer (see edit). Probably comparison by-value is more useful in higher-level and comparison by reference in lower-level languages.

Re: Weird Python Integers

#66
post #56

Earlier quoted context omitted.

It's just like Fortran, where everything including a constant is passed by reference, so if you assign to your arguments you might corrupt your program's only copy of 7 unless the linker put it in a read-only page.

Could you be more specific? Fortran passes arguments by reference, but writing to an argument won't mess up the "program's only copy of 7". For example, the first output of the following program is 4, but the second is still 7. program main integer m,n m = 7 call corrupt(m) write (*,*) m n = 7 write (*,*) n stop end program main subroutine corrupt (a) integer a a = 4 return end

I think he means something like

   call corrupt(7)
which causes a segfault for me. I suppose if you were using a compiler/platform that didn't store constants in read only memory, this might actually work.

Re: Weird Python Integers

#67
post #38

Yup, 0day hunters have fun with this behavior from time to time.

Why is it interesting for "0day hunters" in particular?

I believe it's because you can find code where it does the right thing when e.g. a username is short enough, but fails in a useful way if you increase an input string length.

Re: Weird Python Integers

#68
post #64

I wrote a blog post about this in the past. It's really fun going through the oddities of the language like this. It caches small integers, but also literals used in the same interpreter context (I'm probably getting that last term wrong). You'll get different results if you run these in from the shell as opposed to executing a script, try it out! Here's a fun example >>> x = 256; x is 256 True >>> x = 257; x is 257…

The Python interpreter compiles programs into bytecode first, and the bytecode includes instructions that load a constant from the constant pool. As an example, >>> def f(): ... x = 2222; y=2222 ... return x is y ... >>> f() True >>> f.func_code.co_consts (None, 2222) This last line is showing the constant pool, which is just a standard Python tuple. I believe the REPL compiles each input in a new toplevel module con…

Yep yep, and only when no operations are done to it, otherwise it will end up storing the same constant multiple times and referring to each separately. For example

    >>> def f():
    ...   x = 2221+1; y=2221+1
    ...   return x is y
    >>> f()
    False
    >>> f.__code__.co_consts
    (None, 2221, 1, 2222, 2222)
    >>> dis.dis(f)
      2           0 LOAD_CONST               3 (2222)
                  2 STORE_FAST               0 (x)
                  4 LOAD_CONST               4 (2222)
                  6 STORE_FAST               1 (y)
    
      3           8 LOAD_FAST                0 (x)
                 10 LOAD_FAST                1 (y)
                 12 COMPARE_OP               8 (is)
                 14 RETURN_VALUE
Not too sure when stuff like this would be useful (it goes way beyond the 'avoid using is unless checking references` advice), but it sure is fun.

Re: Weird Python Integers

#69

Is there any practical reason to use "is" to compare two ints (other than demonstrating integer interning)? Should doing so produce a warning?

Yeah, this post is confusing. It reads as though I should expect `is` to be the same operator as `==`. But that's completely off. It's not that the integers are weird, it's that OP is misusing an operator. The topic could have been an explanation of what the `is` is, instead it's confusing and not helpful.

Re: Weird Python Integers

#70
post #66
post #56

Earlier quoted context omitted.

Could you be more specific? Fortran passes arguments by reference, but writing to an argument won't mess up the "program's only copy of 7". For example, the first output of the following program is 4, but the second is still 7. program main integer m,n m = 7 call corrupt(m) write (*,*) m n = 7 write (*,*) n stop end program main subroutine corrupt (a) integer a a = 4 return end

I think he means something like call corrupt(7) which causes a segfault for me. I suppose if you were using a compiler/platform that didn't store constants in read only memory, this might actually work.

By manually moving the constants from .const to .data, I got this program:

       program main
       integer m,n

       call corrupt(7)

       write (*,*), 7
       stop
       end program main

       subroutine corrupt (a)
       integer a
       a = 4
       return
       end
to output 4. Thanks for pointing this out, 'concede_pluto. Pretty weird!
Post reply on HN