读书笔记

网址

http://docs.python-guide.org/en/latest/
Reuqests库的作者写的经验

1. 项目需要一个好的结构

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

2. 组织糟糕的项目的一些标志(Some signs of a poorly structured project include):

2.1 多重和混乱的循环依赖(Multiple and messy circular dependencies)

furn.py

class Table():

    def isdoneby():
        pass

class Chair():

    def isdoneby():
        pass

workers.py

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。

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 *

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.module

5. Pure function

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

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.

原文地址:https://www.cnblogs.com/allen2333/p/9132683.html