Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Amy Hanlon - Investigating Python Wats

Amy Hanlon - Investigating Python Wats

Many of us have experienced a "wat" in Python - some behavior that totally mystifies us. We'll look at three areas where wats arise - identity, mutability, and scope. For each of these three topics, we'll look at some common surprising behaviors, investigate the cause of the behaviors, and cover some practical tips on how to avoid related bugs.

https://us.pycon.org/2015/schedule/presentation/384/

PyCon 2015

April 18, 2015
Tweet

More Decks by PyCon 2015

Other Decks in Programming

Transcript

  1. WAT

  2. >>>  a  =  257   >>>  b  =  257  

    >>>  a  is  b   False
  3. b a >>>  a  =  256   >>>  b  =

     256   ! -­‐5 … 255 256
  4. b a >>>  a  =  256   >>>  b  =

     256   >>>  a  is  b   True -­‐5 … 255 256
  5. a … 255 256 … 257 257 b >>>  a

     =  257   >>>  b  =  257   !
  6. a … 255 256 … 257 257 b >>>  a

     =  257   >>>  b  =  257   >>>  a  is  b   False
  7. >>>  a  =  257   >>>  b  =  257  

    >>>  a  is  b   False   ! >>>  a  =  257;  b  =  257   >>>  a  is  b   True
  8. >>>  a  =  257   >>>  source  =  "a  =

     257"   >>>  code_obj_a  =  compile(   ...          source=source,   ...          filename="",   ...          mode="exec")   !
  9. >>>  a  =  257   >>>  source  =  "a  =

     257"   >>>  code_obj_a  =  compile(   ...          source=source,   ...          filename="",   ...          mode="exec")   >>>  code_obj_a.co_consts   (257,  None)
  10. >>>  source  =  "b  =  257"   >>>  code_obj_b  =

     compile(   ...          source=source,   ...          filename="",   ...          mode="exec")   !
  11. >>>  source  =  "b  =  257"   >>>  code_obj_b  =

     compile(   ...          source=source,   ...          filename="",   ...          mode="exec")   >>>  code_obj_b.co_consts   (257,  None)
  12. >>>  source  =  "a  =  257;  b  =  257"  

    >>>  code_obj  =  compile(     ...          source=source,   ...          filename="",   ...          mode="exec")   !
  13. >>>  source  =  "a  =  257;  b  =  257"  

    >>>  code_obj  =  compile(     ...          source=source,   ...          filename="",   ...          mode="exec")   >>>  code_obj.co_consts   (257,  None)
  14. >>>  row  =  [""]  *  3   >>>  row  

    ['',  '',  '']   ! ! ! !
  15. >>>  row  =  [""]  *  3   >>>  row  

    ['',  '',  '']   >>>  board  =  [row]  *  3   ! ! !
  16. >>>  row  =  [""]  *  3   >>>  row  

    ['',  '',  '']   >>>  board  =  [row]  *  3   >>>  board   [['',  '',  ''],    ['',  '',  ''],    ['',  '',  '']]
  17. >>>  board[0][0]  =  "X"   >>>  board   [['X',  '',

     ''],    ['X',  '',  ''],    ['X',  '',  '']]
  18. def  append_cat(l=[]):          l.append(‘cat’)      

       return  l   ! >>>  append_cat()   ???   !
  19. def  append_cat(l=[]):          l.append(‘cat’)      

       return  l   ! >>>  append_cat()   ['cat']   !
  20. def  append_cat(l=[]):          l.append(‘cat’)      

       return  l   ! >>>  append_cat()   ['cat']   >>>  append_cat()   ???
  21. def  append_cat(l=[]):          l.append(‘cat’)      

       return  l   ! >>>  append_cat()   ['cat']   >>>  append_cat()   ['cat',  'cat']
  22. def  append_cat(l=[]):          l.append(‘cat’)      

       return  l   ! >>>  append_cat.__defaults__   ???   !
  23. def  append_cat(l=[]):          l.append(‘cat’)      

       return  l   ! >>>  append_cat.__defaults__   ([],)   !
  24. def  append_cat(l=[]):          l.append('cat')      

       return  l   ! >>>  append_cat()   >>>  append_cat.__defaults__   ???  
  25. def  append_cat(l=[]):          l.append('cat')      

       return  l   ! >>>  append_cat()   >>>  append_cat.__defaults__   (['cat'],)  
  26. >>>  a  =  1   >>>  def  foo():   ...

             return  a   ! !
  27. >>>  a  =  1   >>>  def  foo():   ...

             return  a   ! >>>  foo()   ???
  28. >>>  a  =  1   >>>  def  foo():   ...

             return  a   ! >>>  foo()   1
  29. >>>  a  =  1   >>>  def  foo():   ...

             return  a   >>>  foo()   ???
  30. • locals()          #  {}   !

    ! >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   ???
  31. • locals()          #  {}   !

    ! >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   ???
  32. • locals()          #  {}   •

    enclosing        #  {}   ! >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   ???
  33. • locals()          #  {}   •

    enclosing        #  {}   ! >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   ???
  34. • locals()          #  {}   •

    enclosing        #  {}   • globals()        #  {'a':  1}   >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   ???
  35. • locals()          #  {}   •

    enclosing        #  {}   ✓ globals()        #  {'a':  1}   >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   1
  36. • locals()          #  {}   •

    enclosing        #  {}   ✓ globals()        #  {'a':  1}   • builtins          #  {'True':  True,…} >>>  a  =  1   >>>  def  foo():   ...          return  a   >>>  foo()   1
  37. >>>  a  =  1   >>>  def  foo():   ...

             return  a   ! >>>  from  dis  import  dis   ! !
  38. >>>  a  =  1   >>>  def  foo():   ...

             return  a   ! >>>  from  dis  import  dis   >>>  dis(foo)      2              0  LOAD_GLOBAL                    0  (a)                        3  RETURN_VALUE      
  39. >>>  a  =  1   >>>  def  foo():   ...

             a  +=  1   ...          return  a   ! >>>  foo()   ???
  40. >>>  a  =  1   >>>  def  foo():   ...

             a  +=  1   ...          return  a   ! >>>  foo()   UnboundLocalError:  local   variable  'a'  referenced   before  assignment
  41. “When you make an assignment to a variable in a

    scope, that variable becomes local to that scope.”
  42. >>>  a  =  1   >>>  def  foo():   ...

             #  a  +=  1   ...          a  =  a  +  1   ...          return  a
  43. >>>  a  =  1   >>>  def  foo():   ...

             #  a  +=  1   ...          a  =  a  +  1   ...          return  a
  44. >>>  a  =  1   >>>  def  foo():   ...

             #  a  +=  1   ...          a  =  a  +  1   ...          return  a
  45. >>>  a  =  1   >>>  def  foo():   ...

             a  +=  1   ...          return  a   ...     >>>  dis(foo)      2              0  LOAD_FAST                        0  (a)                        3  LOAD_CONST                      1  (1)                        6  INPLACE_ADD                                          7  STORE_FAST                      0  (a)     !    3            10  LOAD_FAST                        0  (a)                      13  RETURN_VALUE            
  46. >>>  a  =  1   >>>  def  foo():   ...

             a  +=  1   ...          return  a   ...     >>>  dis(foo)      2              0  LOAD_FAST                        0  (a)                        3  LOAD_CONST                      1  (1)                        6  INPLACE_ADD                                          7  STORE_FAST                      0  (a)     !    3            10  LOAD_FAST                        0  (a)                      13  RETURN_VALUE            
  47. Links • https://www.destroyallsoftware.com/talks/wat • http://akaptur.github.io/blog/2013/10/29/a-python-puzzle/ • https://docs.python.org/3.4/c-api/long.html • https://docs.python.org/3/reference/ compound_stmts.html#function-definitions

    • http://effbot.org/zone/default-values.htm • https://docs.python.org/3/reference/ executionmodel.html#naming-and-binding • http://eli.thegreenplace.net/2011/05/15/understanding- unboundlocalerror-in-python/ • http://rose.github.io/posts/measured-in-wats/