Learn Python, the Right Way (Workshop)

Learn Python, the Right Way (Workshop)

Python Workshop for tutorial track at PyCon APAC 2015. Should be used in conjunction with the Learn Python, the Right Way presentation.

(Contact me if you’re interested in using this for your presentation. The source of the presentation contains some extra useful notes.)

9dafad54b5b4f360b7aae5f482bc1c91?s=128

Tzu-ping Chung

May 31, 2015
Tweet

Transcript

  1. Let’s Build!

  2. Demo: accountant.py This is what we plan to build.

  3. $  python3  -­‐m  venv  accountant_venv   $  .  accountant_venv/bin/activate  

    (accountant_venv)  $ >  python3  -­‐m  venv  accountant_venv   >  accountant_venv\Scripts\activate   (accountant_venv)  >
  4. project   !""  accountant_venv   #      !""  bin

      #      !""  include   #      !""  lib   #      $""  pyvenv.cfg   $""  accountant.py
  5. Function to add an item

  6. def  _show_items(items):          template  =  '{name:20}{price:>5}'  

           if  not  items:                  print("No  items.  Use  'add'  to  add  some!")                  return          print()          print(template.format(name='Item',  price='Price'))          print('-­‐'  *  25)          for  item  in  items:                  print(template.format(                          name=item['name'],  price=item['price'],                  ))          print()
  7. def  add():          items  =  []  

           name  =  input('Item  name:  ')          price  =  input('Price:  ')          if  name  and  price:                  items.append({                          'name':  name,  'price':  price,                  })                  _show_items(items)   if  __name__  ==  '__main__':          add()
  8. http://d.pr/1c5Bv

  9. $  python  accountant.py   What  to  buy:  Apples   Price:

     10   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10
  10. Persistence

  11. File Objects • For reading files and file-like interfaces •

    Built-in type file • close(), read(), readline(), seek(), etc.
  12. Path Objects • import  pathlib • pathlib.Path • Path.cwd() •

    desktop  =  home  /  'Desktop' • home  =  desktop.parent
  13. import  pathlib   path  =  pathlib.Path('input.txt')   f  =  path.open()

      line  =  f.readline()   while  line:          print(line)          line  =  f.readline()   f.close()   print('ALL  DONE')
  14. import  pathlib   path  =  pathlib.Path('input.txt')   f  =  path.open()

      for  line  in  f:          print(line)   f.close()   print('ALL  DONE')
  15. import  pathlib   pathlib.Path('input.txt')   with  path.open()  as  f:  

           for  line  in  f:                  print(line)   print('ALL  DONE') File closed automatically on exiting.
  16. import  pathlib   path  =  pathlib.Path('output.txt')   with  path.open('w')  as

     f:          f.write('Hello  world!\n')   print('ALL  DONE')
  17. The json module • import  json • json.dump(obj,  f) •

    json.load(f) 㶷Ⰵ 隟⳿
  18. Python types JSON types int, float Number str String list,

    tuple, etc. Array dict, etc. Object None null
  19. import  pathlib   def  _get_save_path():          my_path

     =  pathlib.Path(__file__)          save_path  =  my_path.parent  /  'items.json'          return  save_path
  20. import  json   def  _load_items():          path

     =  _get_save_path()          try:                  with  path.open()  as  f:                          items  =  json.load(f)          except  (FileNotFoundError,  ValueError):                  items  =  []          return  items
  21. def  _save_items(items):        #  Your  turn!!  Implement  this.

      def  add():          items  =  _load_items()          #  Original  implementation.          _save_items(items)
  22. project   !""  accountant_venv   #      !""  ...

      !""  accountant.py   $""  items.json
  23. $  python  accountant.py   What  to  buy:  Apples   Price:

     10   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   $  python  accountant.py   What  to  buy:  Oranges   Price:  15   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Oranges                                15
  24. Command Line Interface

  25. $  python  accountant.py  add   What  to  buy:  Bananas  

    Price:  7   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Oranges                                15   Bananas                                  7   $  python  accountant.py  show   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Oranges                                15   Bananas                                  7
  26. $  pip  install  click   Collecting  click      Downloading

     click-­‐4.0-­‐py2.py3-­‐none-­‐any.whl  (62kB)          100%  |████████████████████████|  65kB  1.1MB/s     Installing  collected  packages:  click   Successfully  installed  click-­‐4.0   $
  27. pip • Python’s package manager • Manages packages on PyPI

    (Python Package Index) • pip  install, uninstall, freeze
  28. https://pypi.python.org/

  29. http://click.pocoo.org/

  30. $  pip  freeze  >  requirements.txt

  31. project   !""  accountant_venv   #      !""  ...

      !""  accountant.py   !""  items.json   $""  requirements.txt
  32. Decorators • Modify a callable without re-defining • “Wraps” the

    target • How is that even possible!?
  33. def  hi(name):          print('Hi'  +  name  +'!')

      greet  =  hi   greet('Mosky') First-class Functions
  34. def  get_name():          return  'TP'   def

     hi(name_getter,  words):          def  make_message(words):                  return  '  '.join(words)          print('{msg},  {name}!'.format(                  msg=make_message(words),                  name=name_getter(),          ))   hi(get_name,  ['Hello',  'there'])
  35. def  hi(name,  words):          def  make_message():  

                   return  '  '.join(words)          print('{msg},  {name}!'.format(                  msg=make_message(words),                  name=name,          ))   #  Prints  "Hello  there,  people!"   hi('people',  ['Hello',  'there'])
  36. def  outer_wrapper(func)          def  inner_wrapper(name):    

                 print('Before')                  func(name)                  print('After')          return  inner_wrapper   def  hi(name):          print('Hi,  {}!'.format(name))   #  What  happens  here?   outer_wrapper(hi)('Marty')
  37. def  outer_wrapper(func)          def  inner_wrapper(name):    

                 print('Before')                  func(name)                  print('After')          return  inner_wrapper   def  hi(name):          print('Hi,  {}!'.format(name))   #  What  happens  here?   outer_wrapper(hi)('Marty')
  38. def  outer_wrapper(func)          def  inner_wrapper(name):    

                 print('Before')                  func(name)                  print('After')          return  inner_wrapper   def  hi(name):          print('Hi,  {}!'.format(name))   #  What  happens  here?   outer_wrapper(hi)('Marty')
  39. def  outer_wrapper(func)          def  inner_wrapper(name):    

                 print('Before')                  func(name)                  print('After')          return  inner_wrapper   def  hi(name):          print('Hi,  {}!'.format(name))   #  What  happens  here?   outer_wrapper(hi)('Marty')
  40. def  outer_wrapper(func)          def  inner_wrapper(name):    

                 print('Before')                  func(name)                  print('After')          return  inner_wrapper   def  hi(name):          print('Hi,  {}!'.format(name))   hi  =  outer_wrapper(hi)   hi('Marty')
  41. def  outer_wrapper(func)          def  inner_wrapper(name):    

                 print('Before')                  func(name)                  print('After')          return  inner_wrapper   @outer_wrapper   def  hi(name):          print('Hi,  {}!'.format(name))   hi('Marty')
  42. import  click   @click.group()   def  cli():      

       pass   @cli.add_command   @click.command()   def  add():          #  Implementation  does  not  change.   if  __name__  ==  '__main__':          cli()
  43. $  python  accountant.py  add   What  to  buy:  Bananas  

    Price:  7   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Oranges                                15   Bananas                                  7
  44. $  python  accountant.py   #  What  happens  here?   $

     python  accountant.py  -­‐-­‐help   #  What  happens  here?
  45. $  python  accountant.py  show   Item        

                           Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Oranges                                15   Bananas                                  7
  46. Item Removal

  47. $  python  accountant.py  show   Item        

                           Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Oranges                                15   Bananas                                  7   $  python  accountant.py  remove  1   Item                                Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐   Apples                                  10   Bananas                                  7
  48. #  remove_test.py   def  remove(index):          items

     =  [                  {'name':  'Apples',  'price':  10},                  {'name':  'Oranges',  'price':  15},          ]          #  㻛⡲...          #  㹀纏湫䱸䖰  accountant.py  䭠頺麓⢵կ          _show_items(items)   if  __name__  =  '__main__':          index  =  input('Index:  ')          remove(index)
  49. Keyword Arguments • print('Hello',  'World',  sep=',  ') • Named and

    unnamed arguments • Most arguments can be either
  50. def  hi(name,  message):          print('{},  {}!'.format(message,  name))

      #  Calling  with  positional  arguments.   hi('Doc',  'Hello')   #  Calling  with  keyword  arguments.   hi(message='Hello',  name='Doc')
  51. import  sys   print('Hello',  'World',          

       sep=',  ',  file=sys.stderr) Variable-length positional arguments keyword-only arguments
  52. https://docs.python.org/3/ tutorial/controlflow.html#more- on-defining-functions

  53. @cli.add_command   @click.command()   @click.argument('index')   def  remove(index):    

         items  =  _load_items()          #  ...          #  Before  _show_items(items)          _save_items(items)
  54. $  python  accountant.py   #  What  happens  now?   $

     python  accountant.py  remove  0   #  What  happens?   $  python  accountant.py  remove   #  What  happens?
  55. Refining UI

  56. $  python  accountant.py  show      #    Item  

                                 Price             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐      0    Apples                                  10      1    Oranges                                15      2    Bananas                                  7
  57. def  _show_items(items):          template  =  '{i:>3}  

     {name:20}{price:>5}'          #  ...          for  i  in  range(len(items)):                  item  =  items[i]                  print(template.format(                          i=i,  name=item['name'],                            price=item['price'],                  ))          print()
  58. “There must be a better way.”

  59. def  _show_items(items):          template  =  '{i:>3}  

     {name:20}{price:>5}'          #  ...          i  =  0          for  item  in  items:                  print(template.format(                          i=i,  name=item['name'],                            price=item['price'],                  ))                  i  +=  1          print()
  60. “There must be a better way.”

  61. def  _show_items(items):          template  =  '{i:>3}  

     {name:20}{price:>5}'          #  ...        for  i,  item  in  zip(range(len(items)),  items):                  print(template.format(                          i=i,  name=item['name'],                            price=item['price'],                  ))        print()
  62. def  _show_items(items):          template  =  '{i:>3}  

     {name:20}{price:>5}'          #  ...        for  i,  item  in  enumerate(items):                  print(template.format(                          i=i,  name=item['name'],                            price=item['price'],                  ))        print()
  63. “There must be a better way.”

  64. Next Steps • Configurable _get_save_path • configparser module • Save

    as SQLite and other formats • sqlite3 module • Even more CLI improvements • colorama and palpatine • Any other ideas? Ask!