讀書筆記 - The Hitchhiker's Guide to Python

網址

http://docs.python-guide.org/en/latest/
Reuqests庫的做者寫的經驗python

1. 項目須要一個好的結構

https://www.kennethreitz.org/essays/repository-structure-and-python
https://github.com/kennethreitz/samplemodgit

2. 組織糟糕的項目的一些標誌(Some signs of a poorly structured project include):

2.1 多重和混亂的循環依賴(Multiple and messy circular dependencies)

furn.pygithub

class Table():

    def isdoneby():
        pass

class Chair():

    def isdoneby():
        pass

workers.pyapp

class Carpenter():
   def whatdo():
        pass

若是table.isdoneby()依賴import Carapenter才能執行;相反地,carpenter.whatdo()又要依賴import Table和Chiar。若是一開始就import,出現循環引入。
一個能解決問題可是很差的技巧是在方法或者函數裏import。編輯器

2.2 隱藏的耦合(Hidden coupling)

若是Table內部的改變都使Carpenter的某些test cases不經過,那麼Table和Carpenter的耦合性過大,Table的內部改變影響到Carpenter。ide

2.3 重度使用全局變量、狀態、上下文(Heavy usage of global state or context)

2.4 麪條代碼(Spaghetti code)和餛飩代碼(Ravioli code)

https://blog.csdn.net/godsme_yuan/article/details/6594013
麪條代碼:冗長,控制結構複雜(例如充滿if,for),混亂而難以理解的代碼。本人以爲,在編輯器裏這些代碼有很是多的guidelines垂下來,很是形象的」麪條「。
餛飩代碼:程序由許多小的,鬆散耦合的部分(方法,類,包等)組成。沒有遵循高內聚,低耦合。書中舉了個例子:若是你不知道何時用必須用FurnitureTable,AssetTable和TableNew,那麼這些代碼就是混沌代碼一塊一塊的,很鬆散。函數

3. Module的藝術

3.1 Being able to tell immediately where a class or function comes from, as in the modu.func idiom, greatly improves code readability and understandability in all but the simplest single file projects.
3.2 In many languages, an include file directive is used by the preprocessor to take all code found in the file and‘copy’ it into the caller’s code. It is different in Python: the included code is isolated in a module namespace, whichmeans that you generally don’t have to worry that the included code could have unwanted effects, e.g. override an
existing function with the same name (即import module, 可用module.func)
3.3 不要用from module import *ui

4. package的藝術

4.1 文件夾裏有__init__.py就視做一個包
4.2 A file modu.py in the directory pack/ is imported with the statement import pack.modu. This statement will look for an __init__.py file in pack, execute all of its top-level statements. Then it will look for a file named pack/modu.py and execute all of its top-level statements. After these operations, any variable, function, or class defined in modu.py is available in the pack.modu namespace.
4.3 當項目複雜性上升的時候,可能有sub-packages或者sub-sub-packages在一個很深的目錄層次結構裏面(包裏有包,嵌套關係)。這種狀況下,import一個在很深層次的包裏的module時,會執行所有__init__.py文件(files);因此,若是包(package)和包裏的包(sub-package)不共享代碼,最好把__init__.py置空。
4.4 用import very.deep.module as mod 代替 import very.deep.modulethis

5. Pure function

Pure functions are more efficient building blocks than classes and objects for some architectures because they have no context or side-effectsspa

6. 裝飾器(Decorators)

6.1 A decorator is a function or a class that wraps (or decorates) a function or a method. The ‘decorated’ function or method will replace the original ‘undecorated’ function or method. Because functions are first-class objects in Python, this can be done ‘manually’, but using the @decorator syntax is clearer and thus preferred.

6.2 This mechanism is useful for separating concerns and avoiding external un-related logic ‘polluting’ the core logic of the function or method.
6.3 A good example of a piece of functionality that is better handled with decoration is memoization or caching: you want to store the results of an expensive function in a table and use them directly instead of recomputing them when they have already been computed. This is clearly not part of the function logic.

7. 上下文管理(Context Managers)

7.1 A context manager is a Python object that provides extra contextual information to an action. 用with語句.

with open('file.txt') as f:
    contents = f.read()

7.2 有兩種實現方法:class和generator. Since the two approaches appear the same, we should follow the Zen of Python to decide when to use which. The class approach might be better if there’s a considerable amount of logic to encapsulate. The function approach might be better for situations where we’re dealing with a simple action.

CustomOpen is first instantiated and then its __enter__ method is called and whatever __enter__ returns is assigned to f in the as f part of the statement. When the contents of the with block is finished executing, the __exit__ method is then called.

# class實現上下文管理
class CustomOpen(object):
    def __init__(self, filename):
        self.file = open(filename)

    def __enter__(self):
        return self.file

    def __exit__(self, ctx_type, ctx_value, ctx_traceback):
        self.file.close()

with CustomOpen('file') as f:
    contents = f.read()

The custom_open function executes until it reaches the yield statement. It then gives control back to the with statement, which assigns whatever was yield‘ed to f in the as f portion. The finally clause ensures that close() is called whether or not there was an exception inside the with

# generator實現上下文管理
from contextlib import contextmanager

@contextmanager
def custom_open(filename):
    f = open(filename)
    try:
        yield f 
    finally:
        f.close()

with custom_open('file') as f:
    contents = f.read()

8. 動態類型(Dynamic typing)

  1. Python是動態類型語言,也便是variables沒有fixed type,只是引用;
  2. The dynamic typing of Python is often considered to be a weakness, and indeed it can lead to complexities and hardto-debug code. 例如變量a能夠是任何object.

1. Avoid using the same variable name for different things.

# Bad
a = 1
a = 'a string'
def a():
    pass # Do something
# Good
count = 1
msg = 'a string'
def func():
    pass # Do something

2. it may be a good discipline to avoid assigning to a variable more than once

#Bad
items = 'a b c d'    # This is a string
items = items.split(' ')    # ..becoming a list
items = set(items)    # ... and then a set

9. 可變類型和不可變類型(Mutable and immutable types)

9.1 像list這些可變對象,list.append()和list.pop()是原地修改list
9.2 像6這個integer,是不可變對象, x = x + 1會是x指向另外一個對象。

my_list = [1, 2, 3]
my_list[0] = 4
print(my_list)    # [4, 2, 3]  <-  The same list has changed

x = 6
x = x + 1    # The new x is another object

9.3 string在Python中是不變的

#Bad
# create a concatenated string from 0 to 19 (e.g. "012..1819")
nums = ""
for n in range(20):
    nums += str(n)  # slow and inefficient
print(nums)
#Good
# create a concatenated string from 0 to 19 (e.g. "012..1819")
nums = []
for n in range(20):
    nums.append(str(n))
print("".join(nums))    # much more efficient
# Better
# create a concatenated string from 0 to 19 (e.g. "012..1819")
# list comprehension
nums = [str(n) for n in range(20)]
print("".join(nums))
# Best
# create a concatenated string from 0 to 19 (e.g. "012..1819")
# map
nums = map(str, range(20))
print("".join(nums))

One final thing to mention about strings is that using join() is not always best. In the instances where you are creating a new string from a pre-determined number of strings, using the addition operator is actually faster, but in cases like above or in cases where you are adding to an existing string, using join() should be your preferred method.

foo = 'foo'
bar = 'bar'
foobar = foo + bar  # This is good
foo += 'ooo'  # This is bad, instead you should do:
foo = ''.join([foo, 'ooo'])
foo = 'foo'
bar = 'bar'
foobar = '%s%s' % (foo, bar)  # It is OK
foobar = '{0}{1}'.format(foo, bar)  # It is better
foobar = '{foo}{bar}'.format(foo=foo, bar=bar)  # It is best

10. General concepts

不要炫技, Explicit code. While any kind of black magic is possible with Python, the most explicit and straightforward manner is preferred.

Avoid the magical wand. it is always better to use the most straightforward way to achieve your goal.

Like a kung fu master, a Pythonista knows how to kill with a single finger, and never to actually do it.

#Bad
def make_complex(*args):
    x, y = args
    return dict(**locals())

#Good
def make_complex(x, y):
    return {'x':x, 'y':y}

第二個Good的寫法,可讀性強

it is bad practice to have two disjointed statements on the same line of code.

#Bad
print('one');    print('two')
if x == 1: print('one')
if <complex comparison> and < other complex comparison>:
    # Do Something

#Good
print('one')
print('two')

if x == 1:
    print('one')

cond1 = <complex comparison>
cond2 = <other complex comparision>
if cond1 and cond2:
    # Do Something

Function arguments(函數參數)

關於Positional arguments(位置參數).
Positional arguments are mandatory and have no default values.

def send(message, recipient):
    pass

def point(x, y):
    pass

# Bad, 雖然能夠這樣作
send(recipient='World', message='Hello')
point(y=2, x=1)

# Good, straightforward而且可讀性高
send('Hello', 'World') and point(1, 2)

關於Keyword arguments(關鍵字參數)
Keyword arguments are not mandatory and have default values. for optional parameters sent to the function.

def send(message, to, cc=None, bcc=None)

arbitrary argument list
In the function body, args will be a tuple of all the remaining positional arguments.

def send(message, *args):
    pass

# send('a', 'b', 'c' ,'d'), args就等於('b', 'c', 'd')

arbitrary keyword argument dictionary
kwargs will be a dictionary of all the passed named arguments that have not been caught by
other keyword arguments in the function signature.

We are all responsible users.

Python裏沒有真正的private屬性, 因此開發者和用戶有責任去遵照約定(conventions).

Rather, instead of relying on concrete walls erected by the developers between their code and other’s, the Python community prefers to rely on a set of conventions indicating that these elements should not be accessed directly.

返回值Returning values

保持單一return就好

def complex_function(a, b, c):
    if not a:
        return None     # Raising an exception might be better
    if not b:
        return None     # Raising an exception might be better
    if not c:
        return None     # Raising an exception might be better
    
    # Plan A: Compute X from a, b and c
    x = a + b + c
    if not x:
    # Plan B for computing x
    return x    # One single exit point for the returned value x will help when maintaining the code.

11. Idioms(習慣用法)

A programming idiom, put simply, is a way to write code.

相關文章
相關標籤/搜索