• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
 Be pythonic @StudyArea 台南 2012 10月份
 

Be pythonic @StudyArea 台南 2012 10月份

on

  • 3,175 views

 

Statistics

Views

Total Views
3,175
Views on SlideShare
1,253
Embed Views
1,922

Actions

Likes
6
Downloads
33
Comments
0

9 Embeds 1,922

http://hychen.wuweig.org 1851
http://feeds.feedburner.com 29
http://localhost 25
https://twitter.com 6
http://127.0.0.1 6
http://www.linkedin.com 2
http://www.plurk.com 1
http://webcache.googleusercontent.com 1
http://translate.googleusercontent.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

     Be pythonic @StudyArea 台南 2012 10月份 Be pythonic @StudyArea 台南 2012 10月份 Presentation Transcript

    • BePythonic 陳信屹 (Chen Hsin-Yi) StudyArea 2012, 10/20, Taiwan
    • Me?陳信屹 (Chen Hsin-Yi) a.k.a HychenPython 用大概 6 年 ,主要是寫些系統整合的工作
    • This talk is based on Christopher Arndts talk How to Write "Pythonic" Code and David Goodgers talkCode Like a Pythonista: Idiomatic Python
    • Outline● Zen of Python● Coding Style● General idioms● Functions are first-class object● Duck Typing● Class are also first-class obejct● Organized your code
    • import this
    • Zen of PythonSimple is better than complex. … Flat is better than nested. … Readability counts.
    • Coding Style程式碼是寫給人看的,特別是未來的你
    • PEP8:Style Guide for Python Code http://www.python.org/dev/peps/pep-0008/ PEP = Python Enhancement Proposal
    • Whitespace (1)● 4 spaces per indentation level.● No hard tabs.● Never mix tabs and spaces.● Two blank line between functions.● Two blank lines between classes.
    • Whitespace (2)● Add a space after "," in dicts, lists, tuples, & argument lists, and after ":" in dicts, but not before.● Put spaces around assignments & comparisons (except in argument lists).● No spaces just inside parentheses or just before argument lists.● No spaces just inside docstrings.● def make_squares(key, value=0): """Return a dictionary and a list...""" d = {key: value} l = [key, value] return d, l
    • Naming Convention● joined_lower for functions, methods, attributes● joined_lower or ALL_CAPS for constants● StudlyCaps for classes● Attributes: interface, _internal, __private●
    • More than PEP8...● Google Python Style Guide ● Google Python Style for vim ● For Emacs, the default settings should be fine.● Launchpad Python Style Guide ● Also provide configuration hit for vim class MyClass: class MyClass: def edit_number(self): def editNumber(self): pass pass
    • Long Lines & Continuations● Keep lines below 80 characters in length.● Use implied line continuation inside parentheses/brackets/braces: def __init__(self, first, second, third, fourth, fifth, sixth): output = (first + second + third + fourth + fifth + sixth)● Use backslashes as a last resort: VeryLong().left_hand_side = even_longer.right_hand_side()
    • Lone Strings>>> print o n "e"onetext = (Long strings can be made up of several shorter strings.)"""Tripledoublequotes"""Triplesinglequotes
    • Compound Statements (1)Good:if foo == blah: do_something()do_one()do_two()do_three()Bad:if foo == blah: do_something()do_one(); do_two(); do_three()
    • Docstrings & Comments● Docstrings = How to use code● Comments = Why (rationale) & how code works
    • pep8 in Emacspep8 - A tool to check your Python code against some of the styleconventions in PEP 8.
    • Pyflakes in Emacspyflakes - simple Python source checker
    • More powerful tool...● pychecker ● http://pychecker.sourceforge.net/ ● $ apt-get install pychecker● pylint ● http://www.logilab.org/857 ● $ apt-get install pylint
    • Generalidioms
    • Interactive "_">>> 1 + 12>>> _2_ stores the last printed expression.>>> import math>>> math.pi / 31.0471975511965976>>> angle = _>>> math.cos(angle)0.50000000000000011>>> _0.50000000000000011
    • Swap ValuesIn other languages:temp = aa=bb = tempIn Python:b, a = a, b- The comma is the tuple constructor syntax.- A tuple is created on the right (tuple packing).- A tuple is the target on the left (tuple unpacking).
    • Parallel assignmentvariable_one, variable_two = 10, 20variable_one, variable_two = [10, 20]variable_one, variable_two = (10, 20)
    • Return multiple values in function (1)def get_name(): # you code return first_name, last_namedef get_name(): # you code return (first_name, last_name)fst_name, lst_name = get_name()
    • Return multiple values in function (2)The return value is actually a tuple.def get_name(): # you code return first_name, last_nameprint get_name() # (first_name, last_name)print list(get_name()) # [first_name, last_name]
    • More about Tuple>>> 1,(1,)>>> (1,)(1,)>>> (1)1>>> ()()>>> tuple()()
    • Building Strings from Substringscolors = [red, blue, green, yellow]Dont do this:result = for s in colors: result += sInstead, do this:result = .join(colors)
    • Building Strings, Variations 1if you want spaces between your substrings:result = .join(colors)Or commas and spaces:result = , .join(colors)Heres a common case:colors = [red, blue, green, yellow]print Choose, , .join(colors[:-1]), or, colors[-1]Choose red, blue, green or yellow
    • Building Strings, Variations 2If you need to apply a function to generate the substrings:result = .join(map(fn, items)If function need more than 1 argumentresult = .join(fn(i, other_arg) for i in items)
    • Building Strings, Variations 3If you need to compute the substringsincrementally, accumulate them in a list first:Items = [first line, second line]...items.append(item) # many times...
    • Slicing StringString is like a immutable list.>>> s = "this is a string, a">>> s[0]t>>> s[0:4], s[-1](this, a)>>> s = u“Unicode 也行”>>> s[-1]“ 行”
    • String formation (1/2)str.format = format(...) S.format(*args, **kwargs) -> string Return a formatted version of S, usingsubstitutions from args and kwargs. The substitutions are identified by braces ({and }).
    • String formation (2/2)● “{0} {1} {2}”.format(1,2,3)● “{1} {0} {2}”.format(1,2,3)● “{a} {b} {c}”.format(a=1, b=2, c=3)● % is not recommend ● “%s %s” % (1,2)
    • String Template (1/2) ● string.Template ● Delimiter can not be changed in runtime, because string.Template is created by meta class.import string import stringtpl=string.Template("#a") class MyTpl(string.Template):tpl.delimiter = # delimiter = #print tpl.substitute(a=1)#a tpl = MyTpl("#a") print tpl.substitute(a=1) NG Work
    • String Template (2/2)Create a Template Class in runtime if we want to change delimiter.import stringdef templates(content, **tpl_config): if tpl_config: tplcls = type(CustomTemplate, (string.Template,), tpl_config) else: tplcls = string.Template return tplcls(content) Meta Class , we will talk it later if we havetpl = templates("#abcd", delimiter=#) timeprint tpl.substitute(abcd=1)
    • Pragmatic Unicode● Slide: http://farmdev.com/talks/unicode/● Video: http://pyvideo.org/video/948/pragmatic-unicode- or-how-do-i-stop-the-pain● Unicode Sand-watch ● Bytes on the outside, unicode on the inside ● Encode/decode at the edges● Know what you have ● Bytes or Unicode?● Test...
    • Unicode Sand-watch>>> chr=u" 我 ">>> chruu6211>>> chr.encode(utf-8)xe6x88x91>>> chr.encode(utf-8).decode(utf-8)uu6211>>> repr(chr)>>> print chr.encode(utf-8)我>>> print repr(chr.encode(utf-8))xe6x88x91
    • Notes for i18n● Use double quote for interface display message ● u“please input your password:”● Use single quote for internal debug, information message. ● uDEUBG: database is disconnected
    • Use in where possible (1)string = “this is a string”# do this:if “c” in string: ...do something# not this:if string.count(“c”): ...do something
    • Use in where possible (2)d = {key1: val1, key2: val2}# do this:if key in d: ...do something with d[key]# not this:if d.has_key(key): ...do something with d[key]
    • Use in where possible (3)● in is generally faster.● This pattern also works for items in arbitrary containers (such as lists, tuples, and sets).● Less typing ● chr in string v.s string.count(chr) ● key in d v.s d.has_key(key) ● No () !!!
    • Merge Dict>>> d={a: 1, b: 2, c: 3}>>> d.update({d: 4})>>> d{a: 1, c: 3, b: 2, d: 4}>>> d={a: 1, b: 2, c: 3}>>> d.update({a: 4})>>> d{a: 4, c: 3, b: 2}
    • Python has names (1/2)● a = [1,2,3] ● type(a) # list● a=1 ● type(a) # int
    • Python has names (2/2)● a = [1,2,3]● a, b = [1, 2, 3] ● a is b # True ● id(a) == id(b) # True● c=a ● c is a # True ● id(c) == id(b)
    • Dictionary setdefault Method (1)Initializing mutable dictionary values:equities = {}for (portfolio, equity) in data: if portfolio in equities: equities[portfolio].append(equity) else: equities[portfolio] = [equity]dict.setdefault(key, default) does the job muchmore efficiently:
    • Testing for NoneGood:if foo is None: do_something()Maybe:if not foo: do_someting()Bad:if foo == None: do_something()
    • Iterating a list without counterGood:for item in mylist: print itemBad:for i in range(len(mylist)): print mylist[i]
    • Iterating a list with indexGood:for i, item in enumerate(mylist): if i >= 1: print mylist[i-1] + itemBad:for i in range(len(mylist)): if i >= 1: print mylist[i-1] + itemAlso bad:i=0for item in mylist: if i >= 1: print mylist[i-1] + item i += 1
    • Use Generator instead of Iterator def items(self): ret = [] for pkgname, ctrlinfo in self.metadata.items(): if ctrlinfo and self._run_checker(ctrlinfo.get(checker)): ret.append(pkgname, ctrlinfo.get(require)) else: ret.append(pkgname, None)use generator instead...def items(self): for pkgname, ctrlinfo in self.metadata.items(): if ctrlinfo and self._run_checker(ctrlinfo.get(checker)): yield (pkgname, ctrlinfo.get(require)) else: yield (pkgname, None)
    • Another useful generatordef walkfiles(startdir, pattern=None): """Return generator for full paths of all files belowstartdir. Optionally filters out files not matching pattern. """ for dir, dirlist, filelist in os.walk(startdir): for fname in filelist: if pattern and not fnmatch.fnmatch(fname,pattern): continue yield os.path.join(dir, fname)
    • Read File (1)Open file with `with statment`, and always pass filedescriptor to functionGoodwith open(path, r) as fd: handle_fd(fd)Badhandle_fd(open(path, r).read())
    • Read File (2/2)with open(path, r) as fd: handle_fd(fd)好處* 只要是 file-like object, handle_fd 可以處理 * open * StringIO.StringIO * codec.open * any object has read interface* 另一個好處是惰性執行
    • Functions are first-class object
    • Function is First Class Object● 可當成參數傳給另一個 function● 可以在 funciton 中 return 一個 function● 也可以 assign function 給一個變數● Function 是 Singleton
    • A trickdef countme(): passprint countme() # 1print countme() # 2print countme() # 3 Can not use global variable Can not use closure Can not import any module
    • A trickdef countme(): try: countme._count += 1 except AttributeError: countme._count = 1 return countme._countprint countme() #1print countme() #2print countme() #
    • Default Parameter Values (1)This is a common mistake that beginners often make. Even more advancedprogrammers make this mistake if they dont understand Python names.def bad_append(new_item, a_list=[]): a_list.append(new_item) return a_list>>> print bad_append(one)[one]>>> print bad_append(two)[one, two]
    • Default Parameter Values (2)def bad_append(b=1, a_list=[]): passprint dir(bad_append)print bad_append.func_defaultsprint bad_append.func_dict
    • Unpack arguments● tuple->arguments● dict->keyword parameters● Arguements->tuple● Keyword parameters->arguments
    • tuple->argumentsdef f(a, b, c): print a, b, cargs = (1, 2, 3)f(*args)List also worksf(*[1, 2,3])
    • dict->keyword argumentsdef f(a, b, c, d=None, e=None): print a, b, cargs = (1, 2, 3)kwargs = {A: 0, B:1}f(*args, **kwargs)
    • Arguments → tupledef f(*args): print argsf(1, 2, 3, 4)
    • Keyword Arguments → Dictdef f(*args, **kwargs): print args, kwargsf(D=1, E=2)
    • Unpack argument exampleimport sysdef f1(a, b): print a, beval(sys.argv[1])(*sys.argv[2:]) f1 *[1, 2]
    • Closure 函式物件在建立時,綁定了當時作用範圍( Scope )下有效的自由變數( Free variable )
    • Pure Lambda calculus● A formal system in mathematical logic ● failed● Without Type● Fundamental of functional programming languages● Lambda calculus v.s. Turing machines λx. x Function body Function variable
    • Pure Lambda Calculus→ : evaluated In Pythonλx. 5 → 5 # lambda x : 5(λx. x+1) 2 → 3 # (lambda x : x + 1)(2)
    • Pure Lambda CalculusAssume we have a lambda functionλy λx. x+y x,y are freeand we assign 1 to y(λyλx. x+y)(1)So we now have a lambda function that y is bound to 1λx. 1+x x is free, y is boundThen if we assign 2 to x, function returns3 x, y are bound
    • As Python lambda functionAssume we have a lambda function(lambda y : lambda x : x + y) x,y are freeand we assign 1 to y(lambda y : lambda x : x + y)(1)So we now have a lambda function that y is bound to 1lambda x : x + 1 x is free, y is boundThen if we assign 2 to x, function returns3 x, y are bound# we can write `print (lambda y : lambda x : x + y)(1)(2)` in short
    • As Python nested functiondef f1(y): def f2(x): return x + y return f2print f1(1)(2) # 3print f1(2)(2) # 4
    • Closure Exampledef progress(self, *args, **kwds): kwds[stdin] = subprocess.PIPE p = self.__call(progress, as_process=True, *args, **kwds) def update(percent, message=): if type(percent) == float: percent = int(percent * 100) try: p.stdin.write(str(percent) + n) if message: p.stdin.write(# %sn % message) except IOError: exit() return p.returncode up = zenity.progress(text=hi) return update sleep(1) up(50, doing” sleep(1) up(100, “done”
    • Useful Function tools● map ● map(function, sequence[, sequence, ...]) -> list● filter ● filter(function or None, sequence) -> list, tuple, or string● reduce ● reduce(function, sequence[, initial]) -> value
    • Decorator without argumentsdef deprecated(func): print “warring” return func@deprecateddef oldfn(a, b): return aoldfn(1)
    • Decorator with arguments (1/3) func is bound to func in deprecated functoindef deprecated(func): print “warring” return lambda *args, **kwargs: func(*args, **kwargs)@deprecateddef oldfn(a, b): return aoldfn(1)
    • Decorator with arguments (2/3)def deprecated(func): print “warring” def wrapper(*args, **kwargs): return lambda *args, **kwargs: func(*args,**kwargs) return wrapper@deprecated() ← A decorator to create a decoratordef oldfn(a, b): return a
    • Decorator with Arguments (3/3)Lets see more detailed code!http://hychen.wuweig.org/blog/2011/12/18/recipe-deprecated-function-in-python/
    • Duck Typing
    • Strong Type? Weak Type?● Strong Type● ex. Python ● 1 + “a” → TypeError ● 1 + True → TypeError● Weak Type● ex. JavaScript ● 1 + “a” → “1a” ● 1 + 2rue → 2
    • Operator : +class A(object): passclass B(int): passA() + 1 # TypeError, because As type is objectB() + 1 # work, because Bs type is int
    • Duck Typingclass B(object): def __add__(self, x): return "x:" + str(x)print B is int # Falseprint B() + 1 # Workprint 1 + B() # TypeError
    • Support specified interface...● __call__ - make object callable● __iter__ for for..in● __add__ for + operator● __str__ for print● __sub__ for – operator● ….
    • 用 Duck Typing 去除不必要的繼承● 減少 Class 之間的耦合度● 專心在界面的實作,而非 Class 的歸類● 使繼承樹變得更扁平 ● 更容易理解 ● 更容易維護
    • Class are also first- class object
    • Getter/Setter (1/2)class Foo: def __init__(self, spamm, eggs): self.spamm = spamm self.eggs = eggs def get_spamm(self): return self.spamm def set_spamm(self, value): self.spamm = valuef = Foo(bar, baz)myspamm = f.get_spamm() Bad
    • Getter/Setter (2/2)class Foo: def __init__(self, spamm, eggs): self.spamm = spamm self.eggs = eggsf = Foo(bar, baz)myspamm = f.spamm Good
    • Getter/Setter (3/5)class Foo: # ... def get_spamm(self): return make_spamm()f = Foo()myspamm = f.get_spamm() Bad
    • Getter/Setter (4/5)class Foo: # ... @property def spamm(self): return make_spamm()f = Foo()myspamm = f.spamm Good
    • Getter/Setter (5/5)class Foo: def set_spamm(self, value): if is_valid(value): self.spamm = value else: raise ValueError(I want my spamm!)f = Foo()f.set_spamm = "Eggs" Bad
    • Property| property(fget=None, fset=None, fdel=None, doc=None) -> propertyattribute|| fget is a function to be used for getting an attribute value, and likewise| fset is a function for setting, and fdel a function for deling, an| attribute. Typical use is to define a managed attribute x:|class C(object): | def getx(self): return self._x | def setx(self, value): self._x = value | def delx(self): del self._x | x = property(getx, setx, delx, "Im the x property.")
    • Property (2/2)|| Decorators make defining new properties or modifying existing oneseasy:| class C(object):| @property| def x(self): return self._x| @x.setter| def x(self, value): self._x = value| @x.deleter| def x(self): del self._x
    • Decorators for Class● property ● property.fset ● property.fget ● property.fdel● staticmethod ● Convert a function to be a static method.● slassmethod ● Convert a function to be a class method
    • 組織你的程式碼
    • Project Layout● 常用的 code 寫成 Function● 把 function 用 Module 分類● Module 太大再改成 Package● 需要多個 instance 才用 class
    • Program Structure● (Shebang) ● Use /usr/bin/env python instend of /usr/bin/python● Source encoding declaration● Module docstring● __all__● Imports (stdlib, third-party, private modules)● Global constants and initialization code● Exceptions● Module-level functions● Classes● main function
    • __main____main__ is Top-level script environmentSometimes we dont want our code be executed while file is importedso we write a conditional script.if __name__ == __main__: import sys main(sys.argv)or testing codes of current moduleif __name__ == __main__: run_func1_test() run_func2_test()
    • Summary● Follow PEP 8● Read the standard library reference● Know built-in data structure● 少做多玩 :)
    • References● Yukuans Blog: Be Pythonic -- 字正腔圓說Python● How to Write "Pythonic" Code, Arndts talk● Code Like a Pythonista: Idiomatic Python, David Goodger● What can meta class do for you, Chen Hsin-Yi● Python Type and Object, Chen Hsin-Yi
    • Thanks謝謝聆聽