注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

[轻书快读] Effective Python - 59 Specific Ways to Write Better Python (7)  

2016-03-21 09:49:22|  分类: lang_python |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
7. Collaboration

Item 49: Write Docstrings for Every Function, Class, and Module

教你怎么写函数、类、模块的注释/文档。

函数的文档
def foo():
  """hello foo"""
  return 10

print foo.__doc__

>>>
'hello foo'

模块(module)的文档,写在模块最开头
# words.py
#!/usr/bin/env python3
"""Library for testing words for various linguistic patterns.
   ...
"""

类(class)的说明,写在类的最开始
class Player(object):
  """I'm a player
     ...
  """

生成 doc
http://www.sphinx-doc.org/

免费的 doc hosting
https://readthedocs.org/


Item 50: Use Packages to Organize Modules and Provide Stable APIs

可以将相对独立的一个功能块,封装成一个 package。比如:
main.py
mypackage/__init__.py
mypackage/models.py
mypackage/utils.py

# main.py
from mypackage import utils

如果两个 package 内有重名咋办?
from analysis.utils import inspect
from frontend.utils import inspect # 覆盖前者

可以用 as 嘛。
from analysis.utils import inspect as analysis_inspect
from frontend.utils import inspect as frontend_inspect

通过 __all__ 我们可以指定一个 module 允许暴露哪些内容。
# models.py
__all__ = ['Projectile']

class Projectile(object):
  def __init__(self, mass, velcoity):
    self.mass = mass
    self.velocity = velocity

# utils.py
from .models import Projectile

__all__ = ['simulate_collision']

def _dot_product(a, b):
  # ...

def simulate_collision(a, b):
  # ...

如果我们希望通过
from mypackage import *
来获得 package 中所有允许可见(exported)的内容,可以通过 __init__.py。
# __init__.py
from .models import *
from .utils import *

__all__ = []
__all__ += models.__all__
__all__ += utils.__all__

# main.py
from mypackage import *

a = Projectile(1.5, 3)
b = Projectile(4, 1.7)
after_a, after_b = simulate_collision(a, b)

你一定会问,位啥 from .models import * 这里,models 前面有个 . 呢?
这保证了,如果你把 mypackage/ 目录改名,也不需要修改代码。


Item 51: Define a Root Exception to Insulate Callers from APIs

# my_module.py
class Error(Exception):
  """Base-class for all exceptions raised by this module."""

class FooError(Error):
  """Some Error type"""

class BarError(Error):
  """Another Error type"""

# user.py
try:
  xx = my_module.some_func()
except my_module.Error as e:
  ...

给你自己的模块,定义专属的异常。方便与系统的异常分开,用户一看便知,
此异常来自于你的模块。


Item 52: Know How to Break Circular Dependencies

dialog.py 中,创建 save_dialog 时,需要读取 app.prefs。
而 app.py 中又需要调用 dialog.show()。
引起了 dialog.py 和 app.py 之间的循环引用。

# ==== dialog.py ====
import app

class Dialog(object):
  def __init__(self, save_dir):
    self.save_dir = save_dir

  # ...

save_dialog = Dialog(app.prefs.get('save_dir'))

def show():
  # ...

# ==== app.py ====
import dialog

class Prefs(object):
  def get(self, name):
    # ...

prefs = Prefs()
dialog.show()

>>>
Traceback (most recent call last):
  File "main.py", line 4, in <module>
    import app
  File "app.py", line 4, in <module>
    import dialog
  File "dialog.py", line 16, in <module>
    save_dialog = Dialog(app.prefs.get(‘save_dir’))
AttributeError: 'module' object has no attribute 'prefs'

<1> Searches for your module in locations from sys.path
<2> Loads the code from the module and ensures that it compiles
<3> Creates a corresponding empty module object
<4> Inserts the module into sys.modules
<5> Runs the code in the module object to define its contents

碰到循坏引用的问题,基本都是设计问题,需要重构代码。


解法一
# ==== app.py ====
class Prefs(object):
  # ...

prefs = Prefs()

import dialog
dialog.show()

将 import dialog 挪一下位置,可以运行通过。但不符合 PEP8 建议的(import放在文件头部)。


解法二
# ==== dialog.py ====
import app

class Dialog(object):
  # ...

save_dialog = Dialog()

def show():
  # ...

def configure():
  save_dialog.save_dir = app.prefs.get('save_dir')

# ==== app.py ====
import dialog

class Prefs(object):
  # ...

prefs = Prefs()

def configure():
  # ...

# ==== main.py ====
import app
import dialog

app.configure()
dialog.configure()

dialog.show()

将对外部模块引用的部分,独立成 def configure() 去初始化。


解法三
# ==== dialog.py ====
class Dialog(object):
  # ...

save_dialog = Dialog()

def show():
  import app    # Dynamic import
  save_dialog.save_dir = app.prefs.get('save_dir')

# ==== app.py ====
import dialog

class Prefs(object):
  # ...

prefs = Prefs()
dialog.show()

和解法二类似,但不需要修改 app.py,改动比较小。


Item 53: Use Virtual Environments for Isolated and Reproducible Dependencies

NOTE: python3 only, 且不支持Windows

python 运行环境是全局的,安装了越来越多的 package 会导致 dependency 混乱。
比如:

$ pip3 show Sphinx
Name: Sphinx
Version: 1.2.2
Location: /usr/local/lib/python3.4/site-packages
Requires: docutils, Jinja2, Pygments

$ pip3 show flask
Name: Flask
Version: 0.10.1
Location: /usr/local/lib/python3.4/site-packages
Requires: Werkzeug, Jinja2, itsdangerous

如果 Sphinx 和 Flask 对 Jinja2 的版本要求不一致,就容易产生混乱。

如何解决呢?阿哈,Python 3.4 开始,python 默认支持 pyenv,允许随意创建多个 python environment。

创建过程。
D:\> c:\python35\python.exe -m venv
D:\> mkdir tmp
D:\> mkdir tmp\pyblob
D:\> c:\python35\python.exe -m venv tmp\pyblob
D:\> cd tmp\pyblob
D:\tmp\pyblob> Scripts\activate.bat

(pyblob) D:\tmp\pyblob>python
Python 3.5.1 (...)
>>> ^Z
(pyblob) D:\tmp\pyblob> Scripts\deactivate.bat


将一个 environment 的所有 package 复制到另一个 environment
(pyblob) D:\tmp\pyblob> python -m pip freeze > requirements.txt
(pyblob) D:\tmp\pyblob> type requirements.txt
pytz==2015.7

D:\> mkdir tmp\pyblob2
D:\> c:\python35\python.exe -m venv tmp\pyblob2

(pyblob2) D:\tmp\pyblob2>python -m pip list
pip (8.1.0)
setuptools (18.2)

(pyblob2) D:\tmp\pyblob2>python -m pip install -r d:\tmp\pyblob\requirements.txt
...

(pyblob2) D:\tmp\pyblob2>python -m pip list
pip (8.1.0)
pytz (2015.7)
setuptools (18.2)
  评论这张
 
阅读(210)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017