Live data from Hacker News

Python Oddities

twitter.com

21–30 of 38 posts

Re: Python Oddities

#21
post #17
post #2

I’ve got one! First, some context: This is normal assignment unpacking: >>> a, b = 1, 2 >>> [a, b] [1, 2] (Mismatch gives errors) >>> a, b = [1] ValueError >>> a, b = [1, 2, 3] ValueError Extra parentheses works too: >>> (a, b) = [2, 3] >>> [a, b] [2, 3] Unpacking a single value: >>> (a,) = [4] >>> a 4 Works without parentheses too: >>> a, = [5] >>> a 5 No values does not work: >>> , = [] SyntaxError Neither does emp…

This appears to no longer be the case in Python 3: $ python2 Python 2.7.13 (default, Jul 21 2017, 03:24:34) >>> () = [] File " ", line 1 SyntaxError: can't assign to () >>> $ python3 Python 3.6.2 (default, Jul 20 2017, 03:52:27) >>> () = [] >>>

> This appears to no longer be the case in Python 3:

It was fixed in Python 3.6 specifically, the error still occurs under 3.5.

Re: Python Oddities

#22
post #14

Vast majority of these are not actually oddities. A few stuck out at me, though: - since True/False are aliased to 1/0, you cannot have both 1 and True as dictionary keys. This could plausibly happen in real code. - there's no distinction between args/kwargs, at least when it comes to a function's __defaults__ attr. An arg can be turned into a kwarg by messing with the __defaults__ attr. - you cannot have a nested tu…

> there's no distinction between args/kwargs

At the python level, all named args[0] are kwargs. The reverse is not true in Python 3, although it is in Python 2. At the C level, arguments can be exclusively one or the other even in Python 2.

> at least when it comes to a function's __defaults__ attr. An arg can be turned into a kwarg by messing with the __defaults__ attr.

No, an arg can be turned optional by manipulating __defaults__, that is a different (though not quite orthogonal) axis, especially in Python 3 (which supports python-level required keyword arguments).

> - since True/False are aliased to 1/0, you cannot have both 1 and True as dictionary keys.

They are not aliased (`True is 1` will return False) but they are equal and hash identically.

[0] *args notwithstanding

Re: Python Oddities

#23
post #19

cpython oddity: The following program (call it "20.py") takes a minute to run, but the reported time for the "x=..." line is a few microseconds. import time if 1: t1 = time.time() x = ((((((((0,)*20,)*20,)*20,)*20,)*20,)*20,)*20,)*20 t2 = time.time() print(t2-t1) % /usr/bin/time python 20.py 3.09944152832e-06 70.91 real 70.91 user 0.24 sys

That's probably ARC.

Re: Python Oddities

#24
post #8

Earlier quoted context omitted.

This isn't doing what you think though. There are container objects underneath. For instance "(a, b)" is "a tuple object containing two things, the values named "a" and "b" so you can still refer to them separately. "[a, b]" is a new list with those same elements, not an alias for "(a, b)".

The oddity is that unpacking an empty iterable works when the target is a "list", but not when it's a "tuple". In fact it so doesn't work it actually fails during parsing. And there are no collections underneath the LHS, it compiles to an UNPACK_SEQUENCE opcode in both cases: >>> @dis.dis ... def foo(): ... (a, b) = bar() ... 3 0 LOAD_GLOBAL 0 (bar) 3 CALL_FUNCTION 0 (0 positional, 0 keyword pair) 6 UNPACK_SEQUENCE 2…

It’s a good point that Python is smart enough not to build a mutable list on the right-hand side when not needed (e.g. for "x = [1, 2]" it will and for "y, z = [1, 2]" it will not). A right-hand tuple however is still a separate type, and if you disassemble the original cases you can see that "[] = []" is more wasteful than "[] = ()".

  >>> import dis
  >>> 
  >>> @dis.dis
  ... def f():
  ...     a, b = 1, 2
  ...     (a, b) = [2, 3]
  ...     (a,) = [4]
  ...     a, = [5]
  ...     [] = []
  ...     [] = ()
  ... 
    3           0 LOAD_CONST               6 ((1, 2))
                3 UNPACK_SEQUENCE          2
                6 STORE_FAST               0 (a)
                9 STORE_FAST               1 (b)
  
    4          12 LOAD_CONST               2 (2)
               15 LOAD_CONST               3 (3)
               18 ROT_TWO             
               19 STORE_FAST               0 (a)
               22 STORE_FAST               1 (b)
  
    5          25 LOAD_CONST               4 (4)
               28 STORE_FAST               0 (a)
  
    6          31 LOAD_CONST               5 (5)
               34 STORE_FAST               0 (a)
  
    7          37 BUILD_LIST               0
               40 UNPACK_SEQUENCE          0
  
    8          43 LOAD_CONST               7 (())
               46 UNPACK_SEQUENCE          0
               49 LOAD_CONST               0 (None)
               52 RETURN_VALUE

Re: Python Oddities

#25
post #23
post #19

cpython oddity: The following program (call it "20.py") takes a minute to run, but the reported time for the "x=..." line is a few microseconds. import time if 1: t1 = time.time() x = ((((((((0,)*20,)*20,)*20,)*20,)*20,)*20,)*20,)*20 t2 = time.time() print(t2-t1) % /usr/bin/time python 20.py 3.09944152832e-06 70.91 real 70.91 user 0.24 sys

That's probably ARC.

ARC? The GIS language? Age Restricted Content? Advance Reading Copy? Activity-Regulated Cytoskeleton-associated protein?

Re: Python Oddities

#26
post #25
post #23

Earlier quoted context omitted.

That's probably ARC.

ARC? The GIS language? Age Restricted Content? Advance Reading Copy? Activity-Regulated Cytoskeleton-associated protein?

Automatic Reference Counting, i.e. garbage collection. That's why the timer wouldn't attribute the CPU time to the line you mention; the GC cleans the structure up when the "x" goes out of scope, so after the timing has ended.

Re: Python Oddities

#27
post #26
post #25

Earlier quoted context omitted.

ARC? The GIS language? Age Restricted Content? Advance Reading Copy? Activity-Regulated Cytoskeleton-associated protein?

Automatic Reference Counting, i.e. garbage collection. That's why the timer wouldn't attribute the CPU time to the line you mention; the GC cleans the structure up when the "x" goes out of scope, so after the timing has ended.

Thanks. No, it's not due to reference counting, at least, not according to the tracker issue which reports this oddity.

Add:

  import os
  os._exit(0)
to the bottom of the code and it still takes a minute to run, even though there's no final garbage collection.

Also, that code creates only a few hundred objects, and the 'x' data structure is acyclic.

Post reply on HN