python装饰器、迭代器、生成器、内置方法、Json & pickle数列序列化

1、装饰器

定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能
原则:1、不能修改被装饰函数的源代码
     2、被装饰函数的调用方式不能被修改
实现装饰器知识储备:
	 1、函数即"变量"
	 2、高阶函数:
		a:把一个函数名当做实参传给另一个函数(不修改被修饰函数源代码的情况下,添加新功能)
		b:返回值中包含函数名(不修改函数的调用方式,添加新功能)
	 3、嵌套函数
高阶函数+嵌套函数 --> 装饰器    
1.1、完整的装饰器:
import time
def timmer(func):												#装饰器
    def warpper(*args,**kwargs):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('the func run time is %s' %(stop_time-start_time))
    return warpper

@timmer
def test1():
    time.sleep(3)
    print("in the test1")

test1()
--->
in the test1
the func run time is 3.000171661376953
1.2、只做到了不修改源代码:
import time
def test(func):
    start_time=time.time()
    func()                                                 #将bar赋值给func,func()=bar()
    stop_time=time.time(
    print("the func run time is %s"%(stop_time-start_time))

def bar():                                                 #将bar这个函数传到test函数中再运行
   print("in the bar")
   time.sleep(2)

test(bar)												   #改变了调用方式
1.3、不修改调用方式:
import time
def bar():                                                 #将bar这个函数传到test函数中再运行
    time.sleep(2)
    print("in the bar")

def test2(func):
    print(func)
    return func

bar=test2(bar)
bar()														#调用方式没改变
1.4、嵌套函数
在一个函数内部,在用def声明一个新的函数
def foo():
    print("in the foo")
    def bar():
        print("in the bar")

    bar()
foo()
装饰器---高潮版:
import time
def timmer(func):                               				#timmer(test1),func=test1
    def deco():
        start_time=time.time()
        func()                                  				#test1在这里运行了
        stop_time=time.time()
        print("the func run time is %s"%(stop_time-start_time))
    return deco                                 				#返回deco的内存地址
@timmer                                         				#test1=timmer(test1)
def test1():
    time.sleep(3)
    print("in the test1")
test1()                                         				#test1执行实际在执行deco

--->
in the test1
the func run time is 3.000171661376953
装饰器---又一次高潮:
import time
def timmer(func):            		                   			#timmer(test1),func=test1
    def deco(*args,**kwargs):									#加上*args,**kwargs,适配所有
        start_time=time.time()		
        func(*args,**kwargs)                    				#test1在这里运行了
        stop_time=time.time()
        print("the func run time is %s"%(stop_time-start_time))
    return deco                                 				#返回deco的内存地址
@timmer                                         				#test1=timmer(test1)
def test1():
    time.sleep(1)
    print("in the test1")
@timmer                                         				#test1=timmer(test1)
def test2(name,age):
    print("in the test2: %s %s"%(name,age))
test1()                                         				#test1执行实际在执行deco
test2('lishang',22)

--->
in the test1
the func run time is 1.0000572204589844
in the test2: lishang 22
the func run time is 0.0
补充前戏:
user,passwd = 'lishang','22'
def auth(func):
    def wrapper(*args,**kwargs):
        username = input("Username:").strip()
        password = input("Password:").strip()
        if user == username and passwd == password:
            print('\033[32;1mUser has passwd authentication\033[0m')
            res = func(*args,**kwargs)
            return res													#加一个return
        else:
            exit('\033[31;1mInvalid username or password\033[0m')
    return wrapper

def index():
    print("welcome to this index page")

@auth
def home():
    print("welcome to this home page")
    return "from home"
@auth
def bbs():
    print("welcome to this bbs page")

index()
print(home())
bbs()

--->
welcome to this index page
Username:lishang
Password:22
User has passwd authentication
welcome to this home page
from home													#可以返回值了
Username:lishang
Password:22
User has passwd authentication
welcome to this bbs page
终极高潮版:
user,passwd = 'lishang','22'
def auth(auth_type):
    print("auth func :%s"%auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper funcar :args",*args, **kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print('\033[32;1mUser has passwd authentication\033[0m')
                    res = func(*args, **kwargs)
                    return res
                else:
                    exit('\033[31;1mInvalid username or password\033[0m')
            else:
                print("不会--------------")
        return wrapper
    return outer_wrapper

def index():
    print("welcome to this index page")
@auth(auth_type="local")
def home():                                                         #home=wrapper
    print("welcome to this home page")
    return "from home"
@auth(auth_type="ldap")
def bbs():
    print("welcome to this bbs page")

index()
print(home())
bbs()

2、迭代器

2.1、介绍:
我们已经知道,可以直接作用于for循环的数据类型有一下几种:
一类是集合数据类型,如list(列表)、tuple(元组)、dict(字典)、set(集合)、str(字符串)等;
一类是generator,包括生成器和带yield的generator  function;
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable;
可以用isinstance()判断一个对象是否为Iterable对象:
from collections.abc import Iterable
list=[1,2,3,4,5,6]
list1=set(list)

print(isinstance(list,Iterable))					#判断列表是否可迭代
print(isinstance('123',Iterable))					#判断字符串是否可迭代
print(isinstance({},Iterable))						#判断字典是否可迭代
print(isinstance((),Iterable))						#判断元组是否可迭代
print(isinstance(list1,Iterable))					#判断集合是否可迭代
print(isinstance(100,Iterable))						#判断数字是否可迭代

--->
True
True
True
True
True
False
   而生成器不但可以作用于for循环,还可以被next()函数不断调用返回下一个值,直到最后抛出StopIteration错误标识无法
返回下一个值了。

3、生成器

3.1、列表生成式
a = [ i**2 for i in range(10) ]							#列表生成式
print(a)

c = []													#上面的一行需要下面的3行生成,麻烦
for i in range(10):
    c.append(i**2)
print(c)
--->
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
3.2、生成器
生成器只有在调用时才会生成相应的数据
#b直接在内存里生成一个包含1亿个元素的列表		#不是生成器
b = [i*2 for i in range(10000000)]
b[1000] = 2000							#能直接取到

#d是列表生成器,只有当访问它的时候才会生成		
d = (i*2 for i in range(10000000))		#这个列表根本没有生成,只是保留可一个算法,需要时才生成
d[1000]									#TypeError: 'generator' object is not subscriptable
e=d.__next__()							#用__next__()取下一次数据,没有别的方法
f=d.__next__()							#没法取代前一次,它只记一次数据,取完就没了
print(e)
print(f)
--->
0
2
3.3、通过函数做一个斐波那契函数的生成器
def fib(max):               #max=10
    n, a, b = 0, 0, 1
    while n < max:
        #print(b)
        yield b											 #返回当前状态的值
        a, b = b, a+b
        n=n+1

f=fib(10)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print("start=================")
for i in f:
    print(i)

--->
1
1
2
3
start=================
5
8
13
21
34
55
def fib(max):               #max=10
    n, a, b = 0, 0, 1
    while n < max:
        yield b                                               #中断函数,返回当前状态的值
        a, b = b, a+b
        n=n+1
    return '-----------done'

g=fib(6)
while True:
    try:
        x = next(g)                                            #下一次会回到yield
        print('g:', x)
    except StopAsyncIteration as e:
        print("Generator return value:",e.value)
        break

--->
Traceback (most recent call last):
  File "D:/Python3.7.4/项目1/day10/fib.py", line 16, in <module>
    x = next(g)                                             #下一次会回到yield
StopIteration: -----------done
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
异步IO的雏形,epoll模型,速度快
import time
def consumer(name):
    print("%s准备吃包子了!"%name )
    while True:
        baozi = yield                                       #保存当前状态,将状态返回

        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

c = consumer("zhaoxiaomeng")
c.__next__()                                                #next不会给yield传值,只调用
                                               
def producer(name):
    c = consumer('A')
    c2 = consumer('B')
    c.__next__()
    c2.__next__()
    print("%s准备开始做包子了!!"%name)
    for i in range(5):
        time.sleep(1)
        print("做了一个包子,分成两半!")
        c.send(i)											#send(b1)也调用yield,同时给yield传值
        c2.send(i)
producer("Lishang")

--->
赵晓萌准备吃包子了!
A准备吃包子了!
B准备吃包子了!
李尚准备开始做包子了!!
做了一个包子,分成两半!
包子[0]来了,被[A]吃了!
包子[0]来了,被[B]吃了!
做了一个包子,分成两半!
包子[1]来了,被[A]吃了!
包子[1]来了,被[B]吃了!
做了一个包子,分成两半!
包子[2]来了,被[A]吃了!
包子[2]来了,被[B]吃了!
做了一个包子,分成两半!
包子[3]来了,被[A]吃了!
包子[3]来了,被[B]吃了!
做了一个包子,分成两半!
包子[4]来了,被[A]吃了!
包子[4]来了,被[B]吃了!

4、内置方法

python官方介绍:https://docs.python.org/zh-cn/3/library/functions.html?highlight=built#all

4.1、内置参数:
Build-in Function
abs() dict() help() min() setattr()
all() dir() hex() next() slice()
any() divmod() id() object() sorted()
ascii() enumerate() input() oct() staticmethod()
bin() eval() int() open() str()
bool() exec() isinstance() ord() sum()
bytearray() filter() issubclass() pow() super()
bytes() float() iter() print() tuple()
callable() format() len() property() type()
chr() forzenset() list() range() vars()
classmethod() getattr() locals() repr() zip()
compile globals() map() reversed() import()
complex() hasattr() max() round()
delattr hash() memoryview() set()
第一列:
abs()取绝对值
all(iterable)
	如果 iterable 的所有元素为真(或迭代器为空),返回 True 。等价于:
	def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True
any(iterable)
	如果 iterable 的任一元素为真则返回 True。 如果迭代器为空,返回 False。 等价于:
	def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
ascii(object)
	就像函数 repr(),返回一个对象可打印的字符串,但是 repr() 返回的字符串中非 ASCII 编码的字符,会使用 \x、\u 和 \U 来转义。生成的字符串和 Python 2 的 repr() 返回的结果相似。
bin(x)
	将一个整数转变为一个前缀为“0b”的二进制字符串。结果是一个合法的 Python 表达式。如果 x 不是 Python 的 int 对象,那它需要定义 __index__() 方法返回一个整数。一些例子:
	>>> bin(3)
	'0b11'
	>>> bin(-10)
	'-0b1010'
	###如果不一定需要前缀“0b”,还可以使用如下的方法。###
	>>> format(14, '#b'), format(14, 'b')
	('0b1110', '1110')
	>>> f'{14:#b}', f'{14:b}'
	('0b1110', '1110')bool([x])
	返回一个布尔值,True 或者 False。 x 使用标准的 真值测试过程 来转换。如果 x 是假的或者被省略,返回 False;其他情况返回 True。bool 类是 int 的子类(参见 数字类型 --- int, float, complex)。
	其他类不能继承自它。它只有 False 和 True 两个实例(参见 布尔值)。
	在 3.7 版更改: x 现在只能作为位置参数。
breakpoint(*args, **kws)
	此函数会在调用时将你陷入调试器中。具体来说,它调用 sys.breakpointhook() ,直接传递 args 和 kws 。默认情况下, sys.breakpointhook() 调用 pdb.set_trace() 且没有参数。
	在这种情况下,它纯粹是一个便利函数,因此您不必显式导入 pdb 且键入尽可能少的代码即可进入调试器。但是,sys.breakpointhook() 可以设置为其他一些函数并被 breakpoint() 自动调用,以允许进入你想用的调试器。
bytearray()
	bytearray([source[, encoding[, errors]]])
	返回一个新的 bytes 数组。 bytearray 类是一个可变序列,包含范围为 0 <= x < 256 的整数。它有可变序列大部分常见的方法,见 可变序列类型 的描述;同时有 bytes 类型的大部分方法,参见 bytes 和 bytearray 操作。
bytes([source[, encoding[, errors]]])
	返回一个新的“bytes”对象, 是一个不可变序列,包含范围为 0 <= x < 256 的整数。bytes 是 bytearray 的不可变版本 - 它有其中不改变序列的方法和相同的索引、切片操作。
callable(object)
	如果实参 object 是可调用的,返回 True,否则返回 False。如果返回真,调用仍可能会失败;但如果返回假,则调用 object 肯定会失败。注意类是可调用的(调用类会返回一个新的实例)。
	如果实例的类有 __call__() 方法,则它是可调用。
chr(i)
	返回 Unicode 码位为整数 i 的字符的字符串格式。例如,chr(97) 返回字符串 'a',chr(8364) 返回字符串 '€'。这是 	ord() 的逆函数。
classmethod()
	把一个方法封装成类方法。
	一个类方法把类自己作为第一个实参,就像一个实例方法把实例自己作为第一个实参。请用以下习惯来声明类方法:
	class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ...
compile()
	将 source 编译成代码或 AST 对象。代码对象可以被 exec() 或 eval() 执行。source 可以是常规的字符串、字节字符串,或者 AST 对象
complex([real[, imag]])
	返回值为 real + imag*1j 的复数,或将字符串或数字转换为复数。如果第一个形参是字符串,则它被解释为一个复数,并且函数调用时必须没有第二个形参。第二个形参不能是字符串。每个实参都可以是任意的数值类型(包括复数)。
	如果省略了 imag,则默认值为零,构造函数会像 int 和 float 一样进行数值转换。如果两个实参都省略,则返回 0j
delattr(object, name)
	setattr() 相关的函数。实参是一个对象和一个字符串。该字符串必须是对象的某个属性。如果对象允许,该函数将删除指定的属性。例如 delattr(x, 'foobar') 等价于 del x.foobar 
第二列:
dict(**kwarg)
dict(mapping, **kwarg)
dict(iterable, **kwarg)
	创建一个新的字典。dict 对象是一个字典类。参见 dict 和 映射类型 --- dict 了解这个类。
	其他容器类型,请参见内置的 list、set 和 tuple 类,以及 collections 模块。
dir([object])
	如果没有实参,则返回当前本地作用域中的名称列表。如果有实参,它会尝试返回该对象的有效属性列表。
	如果对象有一个名为 __dir__() 的方法,那么该方法将被调用,并且必须返回一个属性列表。这允许实现自定义
	__getattr__() 或 __getattribute__() 函数的对象能够自定义 dir() 来报告它们的属性。
	如果对象不提供 __dir__(),这个函数会尝试从对象已定义的 __dict__ 属性和类型对象收集信息。结果列表并不总是完整的,如果对象有自定义 __getattr__(),那结果可能不准确。
	默认的 dir() 机制对不同类型的对象行为不同,它会试图返回最相关而不是最全的信息:
	如果对象是模块对象,则列表包含模块的属性名称。
	如果对象是类型或类对象,则列表包含它们的属性名称,并且递归查找所有基类的属性。
	否则,列表包含对象的属性名称,它的类属性名称,并且递归查找它的类的所有基类的属性。
	返回的列表按字母表排序。例如:
	>>> import struct
	>>> dir()   # show the names in the module namespace  # doctest: +SKIP
	['__builtins__', '__name__', 'struct']
	>>> dir(struct)   # show the names in the struct module # doctest: +SKIP
	['Struct', '__all__', '__builtins__', '__cached__', '__doc__', '__file__',
	 '__initializing__', '__loader__', '__name__', '__package__',
	 '_clearcache', 'calcsize', 'error', 'pack', 'pack_into',
	 'unpack', 'unpack_from']
	>>> class Shape:
	...     def __dir__(self):
	...         return ['area', 'perimeter', 'location']
	>>> s = Shape()
	>>> dir(s)
	['area', 'location', 'perimeter']
divmod(a, b)
	它将两个(非复数)数字作为实参,并在执行整数除法时返回一对商和余数。对于混合操作数类型,适用双目算术运算符的规则。对于整数,结果和 
	(a // b, a % b)一致,对于浮点数,结果是 (q, a % b) ,q 通常是 math.floor(a / b) 但可能会比 1 小。在任何情况下,,q * b + a % b 和 a 基本相等;如果 a % b 非零,它的符号和 b 一样,并且 0 <= abs(a % b) < abs(b) 。
enumerate(iterable, start=0)
	返回一个枚举对象。iterable 必须是一个序列,或 iterator,或其他支持迭代的对象。 enumerate() 返回的迭代器的 __next__() 方法返回一个元组,
	里面包含一个计数值(从 start 开始,默认为 0)和通过迭代 iterable 获得的值。
	>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
	>>> list(enumerate(seasons))
	[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
	>>> list(enumerate(seasons, start=1))
	[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
	等价于:
	def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1
eval(expression[, globals[, locals]])
	实参是一个字符串,以及可选的 globals 和 locals。globals 实参必须是一个字典。locals 可以是任何映射对象
	>>> x = 1
	>>> eval('x+1')
	2
exec(object[, globals[, locals]])
	这个函数支持动态执行 Python 代码。object 必须是字符串或者代码对象。如果是字符串,那么该字符串将被解析为一系列 
	Python 语句并执行(除非发生语法错误)。1 如果是代码对象,它将被直接执行。在任何情况下,被执行的代码都需要和文件输入
	一样是有效的(见参考手册中关于文件输入的章节)。请注意即使在传递给 exec() 函数的代码的上下文中,
	return 和 yield 语句也不能在函数定义之外使用。该函数返回值是 None
filter(function, iterable)
	用 iterable 中函数 function 返回真的那些元素,构建一个新的迭代器。iterable 可以是一个序列,一个支持迭代的容器,或一个迭代器。
	如果 function 是 None ,则会假设它是一个身份函数,即 iterable 中所有返回假的元素会被移除。
	请注意, filter(function, iterable) 相当于一个生成器表达式,当 function 不是 None 的时候为 (item for item in iterable if function(item));
	function 是 None 的时候为 (item for item in iterable if item)
	
	res = filter(lambda n:n>5,range(10))
	for i in res:
    	print(i)
    --->
    6
	7
	8
	9
float([x])
	返回从数字或字符串 x 生成的浮点数。
	如果实参是字符串,则它必须是包含十进制数字的字符串,字符串前面可以有符号,之前也可以有空格。
	可选的符号有 '+' 和 '-' ; '+' 对创建的值没有影响。实参也可以是 NaN(非数字)、正负无穷大的字符串。
	确切地说,除去首尾的空格后,输入必须遵循以下语法:
	sign           ::=  "+" | "-"
	infinity       ::=  "Infinity" | "inf"
	nan            ::=  "nan"
	numeric_value  ::=  floatnumber | infinity | nan
	numeric_string ::=  [sign] numeric_value
	>>> float('+1.23')
	1.23
	>>> float('   -12345\n')
	-12345.0
	>>> float('1e-003')
	0.001
	>>> float('+1E6')
	1000000.0
	>>> float('-Infinity')
	-inf
format(value[, format_spec])
	将 value 转换为 format_spec 控制的“格式化”表示。format_spec 的解释取决于 value 实参的类型,但是大多数内置类型使用标准格式化语法:格式规格迷你语言。
	默认的 format_spec 是一个空字符串,它通常和调用 str(value) 的结果相同。
	调用 format(value, format_spec) 会转换成 type(value).__format__(value, format_spec) ,所以实例字典中的 __format__() 方法将不会调用。
	如果搜索到 object 有这个方法但 format_spec 不为空,format_spec 或返回值不是字符串,会触发 TypeError 异常。
	在 3.4 版更改: 当 format_spec 不是空字符串时, object().__format__(format_spec) 会触发 TypeError
frozenset([iterable])
	返回一个新的 frozenset 对象,它包含可选参数 iterable 中的元素。将集合锁定,不能再修改
	frozenset 是一个内置的类。有关此类的文档,请参阅 frozenset 和 集合类型 --- set, frozenset
getattr(object, name[, default])
	返回对象命名属性的值。name 必须是字符串。如果该字符串是对象的属性之一,则返回该属性的值。
	例如, getattr(x, 'foobar') 等同于 x.foobar。如果指定的属性不存在,且提供了 default 值,则返回它,否则触发 AttributeError
globals()
	返回表示当前全局符号表的字典。这总是当前模块的字典(在函数或方法中,不是调用它的模块,而是定义它的模块)。
hasattr(object, name)
	该实参是一个对象和一个字符串。如果字符串是对象的属性之一的名称,则返回 True,否则返回 False.(
	此功能是通过调用 getattr(object, name) 看是否有 AttributeError 异常来实现的。)
hash(object)
	返回该对象的哈希值(如果它有的话)。哈希值是整数。它们在字典查找元素时用来快速比较字典的键。
	相同大小的数字变量有相同的哈希值(即使它们类型不同,如 1 和 1.0)
第三列:
help([object])
	启动内置的帮助系统(此函数主要在交互式中使用)。如果没有实参,解释器控制台里会启动交互式帮助系统。
	如果实参是一个字符串,则在模块、函数、类、方法、关键字或文档主题中搜索该字符串,并在控制台上打印帮助信息。
	如果实参是其他任意对象,则会生成该对象的帮助页。
	请注意如果在函数的形参列表中出现了斜杠 (/),则它在发起调用 help() 的时候意味着斜杠之前的均为仅限位置形参。
hex(x)
	将整数转换为以“0x”为前缀的小写十六进制字符串。
	如果 x 不是 Python int 对象,则必须定义返回整数的 __index__() 方法。一些例子:
	>>> hex(255)
	'0xff'
	>>> hex(-42)
	'-0x2a'
	如果要将整数转换为大写或小写的十六进制字符串,并可选择有无“0x”前缀,则可以使用如下方法:
	>>> '%#x' % 255, '%x' % 255, '%X' % 255
	('0xff', 'ff', 'FF')
	>>> format(255, '#x'), format(255, 'x'), format(255, 'X')
	('0xff', 'ff', 'FF')
	>>> f'{255:#x}', f'{255:x}', f'{255:X}'
	('0xff', 'ff', 'FF')
id(object)
	返回对象的“标识值”。该值是一个整数,在此对象的生命周期中保证是唯一且恒定的。
	两个生命期不重叠的对象可能具有相同的 id() 值。
input([prompt])
	如果存在 prompt 实参,则将其写入标准输出,末尾不带换行符。
	接下来,该函数从输入中读取一行,将其转换为字符串(除了末尾的换行符)并返回。当读取到 EOF 时,则触发 EOFError。
	例如:
	>>>
	>>> s = input('--> ')  
	--> Monty Python's Flying Circus
	>>> s  
	"Monty Python's Flying Circus"
	如果加载了 readline 模块,input() 将使用它来提供复杂的行编辑和历史记录功能。
int([x])
int(x, base=10)
	如果 x 不是数字,或者有 base 参数,x 必须是字符串、bytes、表示进制为 base 的 整数字面值 的 bytearray 实例。
	该文字前可以有 + 或 - (中间不能有空格),前后可以有空格。一个进制为 n 的数字包含 0 到 n-1 的数,
	其中 a 到 z (或 A 到 Z )表示 10 到 35。默认的 base 为 10 ,允许的进制有 0、2-36。2、8、16 进制的数字
	可以在代码中用 0b/0B 、 0o/0O 、 0x/0X 前缀来表示。进制为 0 将安照代码的字面量来精确解释,最后的结果会是
	2、8、10、16 进制中的一个。所以 int('010', 0) 是非法的,但 int('010') 和 int('010', 8) 是合法的。
isinstance(object, classinfo)
	如果 object 实参是 classinfo 实参的实例,或者是(直接、间接或 虚拟)子类的实例,则返回 true。
	如果 object 不是给定类型的对象,函数始终返回 false。如果 classinfo 是对象类型(或多个递归元组)的元组,
	如果 object 是其中的任何一个的实例则返回 true。 如果 classinfo 既不是类型,
	也不是类型元组或类型的递归元组,那么会触发 TypeError 异常。
issubclass(class, classinfo)
	如果 class 是 classinfo 的子类(直接、间接或 虚拟 的),则返回 true。classinfo 可以是类对象的元组,
	此时 classinfo 中的每个元素都会被检查。其他情况,会触发 TypeError 异常。
iter(object[, sentinel])
	返回一个 iterator 对象。根据是否存在第二个实参,第一个实参的解释是非常不同的。
	如果没有第二个实参,object 必须是支持迭代协议(有 __iter__() 方法)的集合对象,
	或必须支持序列协议(有 __getitem__() 方法,且数字参数从 0 开始)。如果它不支持这些协议,会触发 TypeError。
	如果有第二个实参 sentinel,那么 object 必须是可调用的对象。这种情况下生成的迭代器,每次迭代调用它的 
	__next__() 方法时都会不带实参地调用 object;如果返回的结果是 sentinel 则触发 StopIteration,否则返回调用结果。
len(s)
	返回对象的长度(元素个数)。实参可以是序列(如 string、bytes、tuple、list 或 range 等)
	或集合(如 dictionary、set 或 frozen set 等)。
list([iterable])
	虽然被称为函数,list 实际上是一种可变序列类型,详情请参阅 列表 和 序列类型 --- list, tuple, range。
locals()
	更新并返回表示当前本地符号表的字典。 在函数代码块但不是类代码块中调用 locals() 时将返回自由变量。 
	请注意在模块层级上,locals() 和 globals() 是同一个字典。
map(function, iterable, ...)
	返回一个将 function 应用于 iterable 中每一项并输出其结果的迭代器。 
	如果传入了额外的 iterable 参数,function 必须接受相同个数的实参并被应用于从所有可迭代对象中并行获取的项。
	当有多个可迭代对象时,最短的可迭代对象耗尽则整个迭代就将结束。
max(iterable, *[, key, default])
max(arg1, arg2, *args[, key])
	返回可迭代对象中最大的元素,或者返回两个及以上实参中最大的。
	如果只提供了一个位置参数,它必须是非空 iterable,返回可迭代对象中最大的元素;
	如果提供了两个及以上的位置参数,则返回最大的位置参数。
	有两个可选只能用关键字的实参。key 实参指定排序函数用的参数,如传给 list.sort() 的。
	default 实参是当可迭代对象为空时返回的值。如果可迭代对象为空,并且没有给 default ,则会触发 ValueError。
	如果有多个最大元素,则此函数将返回第一个找到的。
	这和其他稳定排序工具如 sorted(iterable, key=keyfunc, reverse=True)[0] 
	和 heapq.nlargest(1, iterable, key=keyfunc) 保持一致。
memoryview(obj)
	返回由给定实参创建的“内存视图”对象。有关详细信息,请参阅 内存视图
第四列:
memoryview(obj)
	返回由给定实参创建的“内存视图”对象。有关详细信息,请参阅 内存视图。
min(iterable, *[, key, default])
min(arg1, arg2, *args[, key])
	返回可迭代对象中最小的元素,或者返回两个及以上实参中最小的。
	如果只提供了一个位置参数,它必须是 iterable,返回可迭代对象中最小的元素;
	如果提供了两个及以上的位置参数,则返回最小的位置参数。
	有两个可选只能用关键字的实参。key 实参指定排序函数用的参数,如传给 list.sort() 的。
	default 实参是当可迭代对象为空时返回的值。如果可迭代对象为空,并且没有给 default ,则会触发 ValueError。
	如果有多个最小元素,则此函数将返回第一个找到的。
	这和其他稳定排序工具如 sorted(iterable, key=keyfunc)[0] 
	和 heapq.nsmallest(1, iterable, key=keyfunc) 保持一致。
next(iterator[, default])
	通过调用 iterator 的 __next__() 方法获取下一个元素。
	如果迭代器耗尽,则返回给定的 default,如果没有默认值则触发 StopIteration。
class object
	返回一个没有特征的新对象。object 是所有类的基类。它具有所有 Python 类实例的通用方法。这个函数不接受任何实参。
	注解 由于 object 没有 __dict__,因此无法将任意属性赋给 object 的实例。
oct(x)
	将一个整数转变为一个前缀为“0o”的八进制字符串。结果是一个合法的 Python 表达式。
	如果 x 不是 Python 的 int 对象,那它需要定义 __index__() 方法返回一个整数。一些例子:
	>>> oct(8)
	'0o10'
	>>> oct(-56)
	'-0o70'
	如果要将整数转换为八进制字符串,并可选择有无“0o”前缀,则可以使用如下方法:
	>>> '%#o' % 10, '%o' % 10
	('0o12', '12')
	>>> format(10, '#o'), format(10, 'o')
	('0o12', '12')
	>>> f'{10:#o}', f'{10:o}'
	('0o12', '12')
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
	打开 file 并返回对应的 file object。如果该文件不能打开,则触发 OSError。
	mode 是一个可选字符串,用于指定打开文件的模式。默认值是 'r' ,这意味着它以文本模式打开并读取。
	其他常见模式有:写入 'w' (截断已经存在的文件);排它性创建 'x' ;
	追加写 'a' (在 一些 Unix 系统上,无论当前的文件指针在什么位置,所有 写入都会追加到文件末尾)。
	在文本模式,如果 encoding 没有指定,则根据平台来决定使用的编码:使用 locale.getpreferredencoding(False) 来获取本地编码。
	(要读取和写入原始字节,请使用二进制模式并不要指定 encoding。)可用的模式有:
	'r'	读取(默认)
	'w'	写入,并先截断文件
	'x'	排它性创建,如果文件已存在则失败
	'a'	写入,如果文件存在则在末尾追加
	'b'	二进制模式
	't'	文本模式(默认)
	'+'	更新磁盘文件(读取并写入)
	默认的模式是 'r' (打开并读取文本,同 'rt' )。
	对于二进制写入, 'w+b' 模式打开并把文件截断成 0 字节; 'r+b' 则不会截断。
	>>>
	>>> import os
	>>> dir_fd = os.open('somedir', os.O_RDONLY)
	>>> def opener(path, flags):
	...     return os.open(path, flags, dir_fd=dir_fd)
	...
	>>> with open('spamspam.txt', 'w', opener=opener) as f:
	...     print('This will be written to somedir/spamspam.txt', file=f)
	...
	>>> os.close(dir_fd)  # don't leak a file descriptor
	open() 函数所返回的 file object 类型取决于所用模式。 
	当使用 open() 以文本模式 ('w', 'r', 'wt', 'rt' 等) 打开文件时,
	它将返回 io.TextIOBase (特别是 io.TextIOWrapper) 的一个子类。 
	当使用缓冲以二进制模式打开文件时,返回的类是 io.BufferedIOBase 的一个子类。 
	具体的类会有多种:在只读的二进制模式下,它将返回 io.BufferedReader;
	在写入二进制和追加二进制模式下,它将返回 io.BufferedWriter,
	而在读/写模式下,它将返回 io.BufferedRandom。 
	当禁用缓冲时,则会返回原始流,即 io.RawIOBase 的一个子类 io.FileIO。
ord(c)
	对表示单个 Unicode 字符的字符串,返回代表它 Unicode 码点的整数。
	例如 ord('a') 返回整数 97, ord('€') (欧元符号)返回 8364 。
	这是 chr() 的逆函数。
pow(x, y[, z])
	返回 x 的 y 次幂;如果 z 存在,则对 z 取余(比直接 pow(x, y) % z 计算更高效)。
	两个参数形式的 pow(x, y) 等价于幂运算符: x**y。
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
	将 objects 打印到 file 指定的文本流,以 sep 分隔并在末尾加上 end。 
	sep, end, file 和 flush 如果存在,它们必须以关键字参数的形式给出。
	所有非关键字参数都会被转换为字符串,就像是执行了 str() 一样,并会被写入到流,以 sep 且在末尾加上 end。 
	sep 和 end 都必须为字符串;它们也可以为 None,这意味着使用默认值。 
	如果没有给出 objects,则 print() 将只写入 end。
	file 参数必须是一个具有 write(string) 方法的对象;如果参数不存在或为 None,则将使用 sys.stdout。 
	由于要打印的参数会被转换为文本字符串,因此 print() 不能用于二进制模式的文件对象。 
	对于这些对象,应改用 file.write(...)。
	一个典型的用法是定义一个托管属性 x:
	class C:
	    def __init__(self):
	        self._x = None
	    def getx(self):
	        return self._x
	    def setx(self, value):
	        self._x = value
	    def delx(self):
	        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")
	如果 c 是 C 的实例,c.x 将调用getter,c.x = value 将调用setter, del c.x 将调用deleter。
	如果给出,doc 将成为该 property 属性的文档字符串。 否则该 property 将拷贝 fget 的文档字符串(如果存在)。
	 这令使用 property() 作为 decorator 来创建只读的特征属性可以很容易地实现:
	class Parrot:
	    def __init__(self):
	        self._voltage = 100000
	
	    @property
	    def voltage(self):
	        """Get the current voltage."""
	        return self._voltage
	以上 @property 装饰器会将 voltage() 方法转化为一个具有相同名称的只读属性的 "getter",
	并将 voltage 的文档字符串设置为 "Get the current voltage."

	特征属性对象具有 getter, setter 以及 deleter 方法,它们可用作装饰器来创建该特征属性的副本,
	并将相应的访问函数设为所装饰的函数。 这最好是用一个例子来解释:
	class C:
	    def __init__(self):
	        self._x = None
	
	    @property
	    def x(self):
	        """I'm the 'x' property."""
	        return self._x
	
	    @x.setter
	    def x(self, value):
	        self._x = value
	
	    @x.deleter
	    def x(self):
	        del self._x
	上述代码与第一个例子完全等价。 注意一定要给附加函数与原始的特征属性相同的名称 (在本例中为 x。)
	返回的特征属性对象同样具有与构造器参数相对应的属性 fget, fset 和 fdel。
range(stop)
range(start, stop[, step])
	虽然被称为函数,但 range 实际上是一个不可变的序列类型,参见在 range 对象 与 序列类型
	 --- list, tuple, range 中的文档说明。
repr(object)
	返回包含一个对象的可打印表示形式的字符串。 对于许多类型来说,该函数会尝试返回的字符串将会与该对象被传递给 
	eval() 时所生成的对象具有相同的值,在其他情况下表示形式会是一个括在尖括号中的字符串,其中包含对象类型的名称
	与通常包括对象名称和地址的附加信息。 类可以通过定义 __repr__() 方法来控制此函数为它的实例所返回的内容。
reversed(seq)
	返回一个反向的 iterator。 seq 必须是一个具有 __reversed__() 方法的对象或者是支持该序列协议
	(具有从 0 开始的整数类型参数的 __len__() 方法和 __getitem__() 方法)。
round(number[, ndigits])
	返回 number 舍入到小数点后 ndigits 位精度的值。 
	如果 ndigits 被省略或为 None,则返回最接近输入值的整数。
	对于支持 round() 的内置类型,值会被舍入到最接近的 10 的负 ndigits 次幂的倍数;如果与两个倍数的距离相等,
	则选择偶数 (因此,round(0.5) 和 round(-0.5) 均为 0 而 round(1.5) 为 2)。
	任何整数值都可作为有效的ndigits(正数、零或负数)。
	如果 ndigits 被省略或为 None 则返回值将为整数。 否则返回值与 number 的类型相同。
	对于一般的 Python 对象 number, round 将委托给 number.__round__。
	注解 对浮点数执行 round() 的行为可能会令人惊讶:例如,round(2.675, 2) 将给出 2.67 而不是期望的 2.68。 
	这不算是程序错误:这一结果是由于大多数十进制小数实际上都不能以浮点数精确地表示。 
	请参阅 浮点算术:争议和限制 了解更多信息。
第五列:
set([iterable])
	返回一个新的 set 对象,可以选择带有从 iterable 获取的元素。 set 是一个内置类型。 
	请查看 set 和 集合类型 --- set, frozenset 获取关于这个类的文档。
setattr(object, name, value)
	此函数与 getattr() 两相对应。 其参数为一个对象、一个字符串和一个任意值。 
	字符串指定一个现有属性或者新增属性。 函数会将值赋给该属性,只要对象允许这种操作。 
	例如,setattr(x, 'foobar', 123) 等价于 x.foobar = 123。
class slice(stop)
class slice(start, stop[, step])
	返回一个表示由 range(start, stop, step) 所指定索引集的 slice 对象。 
	其中 start 和 step 参数默认为 None。 切片对象具有仅会返回对应参数值(或其默认值)的只读数据属性
	start, stop 和 step。 它们没有其他的显式功能;不过它们会被 NumPy 以及其他第三方扩展所使用。 
	切片对象也会在使用扩展索引语法时被生成。 例如: a[start:stop:step] 或 a[start:stop, i]。 
	请参阅 itertools.islice() 了解返回迭代器的一种替代版本。
sorted(iterable, *, key=None, reverse=False)
	a = {6:2,8:0,1:4,-5:6,99:11,4:22}
	print(sorted(a.items()))										#按照key大小来排序
	print(sorted(a.items(), key=lambda x:x[1]))						#按照值的大小来排序
	--->
	[(-5, 6), (1, 4), (4, 22), (6, 2), (8, 0), (99, 11)]
	[(8, 0), (6, 2), (1, 4), (-5, 6), (99, 11), (4, 22)]
	根据 iterable 中的项返回一个新的已排序列表。
	具有两个可选参数,它们都必须指定为关键字参数。
	key 指定带有单个参数的函数,用于从 iterable 的每个元素中提取用于比较的键 (例如 key=str.lower)。 
	默认值为 None (直接比较元素)。
	reverse 为一个布尔值。 如果设为 True,则每个列表元素将按反向顺序比较进行排序。
	使用 functools.cmp_to_key() 可将老式的 cmp 函数转换为 key 函数。
	内置的 sorted() 确保是稳定的。 如果一个排序确保不会改变比较结果相等的元素的相对顺序就称其为稳定的
	--- 这有利于进行多重排序(例如先按部门、再按薪级排序)。
class C:
    builtin_open = staticmethod(open)
class str(object='')
class str(object=b'', encoding='utf-8', errors='strict')
	返回一个 str 版本的 object 。有关详细信息,请参阅 str() 。
	str 是内置字符串 class 。更多关于字符串的信息查看 文本序列类型 --- str。
sum(iterable[, start])
	对某些用例来说,存在 sum() 的更好替代。 拼接字符串序列的更好更快方式是调用 ''.join(sequence)。 
	要以扩展精度对浮点值求和,请参阅 math.fsum()。 要拼接一系列可迭代对象,请考虑使用 itertools.chain()。
super([type[, object-or-type]])
	如果省略第二个参数,则返回的超类对象是未绑定的。 如果第二个参数为一个对象,则 isinstance(obj, type) 必须为真值。 
	如果第二个参数为一个类型,则 issubclass(type2, type) 必须为真值(这适用于类方法)。
	super 有两个典型用例。 
	在具有单继承的类层级结构中,super 可用来引用父类而不必显式地指定它们的名称,从而令代码更易维护。 
	这种用法与其他编程语言中 super 的用法非常相似。
	第二个用例是在动态执行环境中支持协作多重继承。 
	此用例为 Python 所独有,在静态编译语言或仅支持单继承的语言中是不存在的。 
	这使得实现“菱形图”成为可能,在这时会有多个基类实现相同的方法。 好的设计强制要求这种方法在每个情况下具有相同的
	调用签名(因为调用顺序是在运行时确定的,也因为该顺序要适应类层级结构的更改,
	还因为该顺序可能包含在运行时之前未知的兄弟类)。
tuple([iterable])
	虽然被称为函数,但 tuple 实际上是一个不可变的序列类型,参见在 元组 与 序列类型
	 --- list, tuple, range 中的文档说明。
class type(object)
class type(name, bases, dict)
	传入一个参数时,返回 object 的类型。 返回值是一个 type 对象,通常与 object.__class__ 所返回的对象相同。
	推荐使用 isinstance() 内置函数来检测对象的类型,因为它会考虑子类的情况。
	传入三个参数时,返回一个新的 type 对象。 这在本质上是 class 语句的一种动态形式。 
	name 字符串即类名并且会成为 __name__ 属性;bases 元组列出基类并且会成为 __bases__ 属性;
	而 dict 字典为包含类主体定义的命名空间并且会被复制到一个标准字典成为 __dict__ 属性。 
	例如,下面两条语句会创建相同的 type 对象:
	>>> class X:
	...     a = 1
	...
	>>> X = type('X', (object,), dict(a=1))
vars([object])
	返回模块、类、实例或任何其它具有 __dict__ 属性的对象的 __dict__ 属性。
	模块和实例这样的对象具有可更新的 __dict__ 属性;但是,其它对象的 __dict__ 属性可能会设为限制写入
	(例如,类会使用 types.MappingProxyType 来防止直接更新字典)。
	不带参数时,vars() 的行为类似 locals()。
	请注意,locals 字典仅对于读取起作用,因为对 locals 字典的更新会被忽略。
zip(*iterables)
	创建一个聚合了来自每个可迭代对象中的元素的迭代器。
	返回一个元组的迭代器,其中的第 i 个元组包含来自每个参数序列或可迭代对象的第 i 个元素。 
	当所输入可迭代对象中最短的一个被耗尽时,迭代器将停止迭代。 
	当只有一个可迭代对象参数时,它将返回一个单元组的迭代器。 
	不带参数时,它将返回一个空迭代器。 相当于:
	def zip(*iterables):
	    # zip('ABCD', 'xy') --> Ax By
	    sentinel = object()
	    iterators = [iter(it) for it in iterables]
	    while iterators:
	        result = []
	        for it in iterators:
	            elem = next(it, sentinel)
	            if elem is sentinel:
	                return
	            result.append(elem)
	        yield tuple(result)
	函数会保证可迭代对象按从左至右的顺序被求值。
	使得可以通过 zip(*[iter(s)]*n) 这样的惯用形式将一系列数据聚类为长度为 n 的分组。 
	这将重复 同样的 迭代器 n 次,以便每个输出的元组具有第 n 次调用该迭代器的结果。 
	它的作用效果就是将输入拆分为长度为 n 的数据块。
	当你不用关心较长可迭代对象末尾不匹配的值时,则 zip() 只须使用长度不相等的输入即可。 
	如果那些值很重要,则应改用 itertools.zip_longest()。
	zip() 与 * 运算符相结合可以用来拆解一个列表:
	>>>
	>>> x = [1, 2, 3]
	>>> y = [4, 5, 6]
	>>> zipped = zip(x, y)
	>>> list(zipped)
	[(1, 4), (2, 5), (3, 6)]
	>>> x2, y2 = zip(*zip(x, y))
	>>> x == list(x2) and y == list(y2)
	True

5、Json & pickle数列序列化

json用法只能处理简单的数据类型,字典、列表、元组、字符串等,json是所有语言通用的用法

参考https://www.cnblogs.com/alex3714/article/5161349.html

序列化:
import json
info = {
    'name' : 'lishang',
    'age' : '22'
}

f = open("test.txt",'w')
f.write(json.dumps(info))									#序列化用json.dumps
f.close()
反序列化:
import json

f = open("test.txt",'r')
data = json.loads(f.read())									#反序列化用json.loads
print(data['age'])
f.close()

--->
22
想处理复杂的类型,用 import pickle 用法与json基本一致,只有python里有
序列化:
import pickle
def foo(name):
    print('name',name)
info = {
    'name' : 'lishang',
    'age' : '22',
    'func' : foo
}
f = open("test.txt",'wb')
f.write(pickle.dumps(info))							#pickle.dump(info,f)---两句一模一样
f.close()
反序列化:
import pickle

f = open("test.txt",'rb')
def foo(name):
    print('name1111111111',name)

data = pickle.loads(f.read())						#data = pickle.load(f)---与前面一模一样
print(data['func']('lishang'))
f.close()

--->
name1111111111 lishang
None
json.dumps多次
import json
def foo(name):
    print('name',name)
info = {
    'name' : 'lishang',
    'age' : '22',
}

f = open("test.txt",'w')
f.write(json.dumps(info))

info['age']=21
f.write(json.dumps(info))
f.close()

--->cat test.txt
{"name": "lishang", "age": "22"}{"name": "lishang", "age": 21}
但不能loads好多次,因此实际工作中,值dump一次,只loads一次

6、软件目录结构规范

6.1、为什么要设计好目录结构?
"设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题。对于这种风格上的规范,一直都存在两种态度:

	1、一类同学认为,这种个人风格问题"无关紧要"。理由是能让程序work就好,风格问题根本不是问题。
	2、另一类同学认为,规范化能更好的控制程序结构,让程序具有更高的可读性。
	
我是比较偏向于后者的,因为我是前一类同学思想行为下的直接受害者。我曾经维护过一个非常不好读的项目,其实现的逻辑并不复
杂,但是却耗费了我非常长的时间去理解它想表达的意思。从此我个人对于提高项目可读性、可维护性的要求就很高了。

"项目目录结构"其实也是属于"可读性和可维护性"的范畴,我们设计一个层次清晰的目录结构,就是为了达到以下两点:

可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。
可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。
这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。
所以,我认为,保持一个层次清晰的目录结构是有必要的。更何况组织一个良好的工程目录,其实是一件很简单的事儿。
6.2、目录组织方式

关于如何组织一个较好的Python工程目录结构,已经有一些得到了共识的目录结构。
Stackoverflow的这个问题上,能看到大家对Python目录结构的讨论。
这里面说的已经很好了,我也不打算重新造轮子列举各种不同的方式,这里面我说一下我的理解和体会。
假设你的项目名为foo, 我比较建议的最方便快捷目录结构这样就足够了:

Foo/
|-- bin/
|   |-- foo
|
|-- foo/
|   |-- tests/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py
|   |-- main.py
|
|-- conf/
|	|-- sql
|	|-- *.conf
|
|-- docs/
|   |-- conf.py
|   |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README

简要解释一下:

	1、bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
	2、foo/: 存放项目的所有源代码。
		(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。
		(2) 其子目录tests/存放单元测试代码; 
		(3) 程序的入口最好命名为main.py。
	3、conf/:存放一些配置文件
	4、docs/: 存放一些文档。
	5、setup.py: 安装、部署、打包的脚本。
	6、requirements.txt: 存放软件依赖的外部Python包列表。
	7、README: 项目说明文件。

关于README的内容
这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。
它需要说明以下几个事项:
	1、软件定位,软件的基本功能。
	2、运行代码的方法: 安装环境、启动命令等。
	3、简要的使用说明。
	4、代码目录结构说明,更详细点可以说明软件的基本原理。
	5、常见问题说明。
我觉得有以上几点是比较好的一个README。在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,
并不是一定要在一开始就将所有信息都补全。但是在项目完结的时候,是需要撰写这样的一个文档的。

可以参考Redis(https://github.com/antirez/redis#what-is-redis)源码中Readme的写法,
这里面简洁但是清晰的描述了Redis功能和源码结构。

关于requirements.txt和setup.py
setup.py
一般来说,用setup.py来管理代码的打包、安装、部署问题。
业界标准的写法是用Python流行的打包工具setuptools来管理这些事情。这种方式普遍应用于开源项目中。
不过这里的核心思想不是用标准化的工具来解决这些问题,而是说,一个项目一定要有一个安装部署工具,
能快速便捷的在一台新机器上将环境装好、代码部署好和将程序运行起来。

这个我是踩过坑的。

我刚开始接触Python写项目的时候,安装环境、部署代码、运行程序这个过程全是手动完成,遇到过以下问题:

安装环境时经常忘了最近又添加了一个新的Python包,结果一到线上运行,程序就出错了。
Python包的版本依赖问题,有时候我们程序中使用的是一个版本的Python包,
但是官方的已经是最新的包了,通过手动安装就可能装错了。
如果依赖的包很多的话,一个一个安装这些依赖是很费时的事情。
新同学开始写项目的时候,将程序跑起来非常麻烦,因为可能经常忘了要怎么安装各种依赖。
setup.py可以将这些事情自动化起来,提高效率、减少出错的概率。
"复杂的东西自动化,能自动化的东西一定要自动化。"是一个非常好的习惯。

setuptools的文档比较庞大,刚接触的话,可能不太好找到切入点。学习技术的方式就是看他人是怎么用的,可以参考一下Python的一个Web框架,flask是如何写的: setup.py

当然,简单点自己写个安装脚本(deploy.sh)替代setup.py也未尝不可。

requirements.txt
这个文件存在的目的是:

方便开发者维护软件的包依赖。将开发过程中新增的包添加进这个列表中,避免在setup.py安装依赖时漏掉软件包。
方便读者明确项目使用了哪些Python包。
这个文件的格式是每一行包含一个包依赖的说明,通常是flask>=0.10这种格式,要求是这个格式能被pip识别,
这样就可以简单的通过 pip install -r requirements.txt来把所有Python包依赖都装好了。
具体格式说明: 点这里:	https://pip.readthedocs.io/en/1.1/requirements.html

关于配置文件的使用方法
注意,在上面的目录结构中,没有将conf.py放在源码目录下,而是放在docs/目录下。
很多项目对配置文件的使用做法是:

配置文件写在一个或多个python文件中,比如此处的conf.py。
项目中哪个模块用到这个配置文件就直接通过import conf这种形式来在代码中使用配置。
这种做法我不太赞同:

这让单元测试变得困难(因为模块内部依赖了外部配置)
另一方面配置文件作为用户控制程序的接口,应当可以由用户自由指定该文件的路径。
程序组件可复用性太差,因为这种贯穿所有模块的代码硬编码方式,使得大部分模块都依赖conf.py这个文件。
所以,我认为配置的使用,更好的方式是,

模块的配置都是可以灵活配置的,不受外部配置文件的影响。
程序的配置也是可以灵活控制的。
能够佐证这个思想的是,用过nginx和mysql的同学都知道,nginx、mysql这些程序都可以自由的指定用户配置。

所以,不应当在代码中直接import conf来使用配置文件。
上面目录结构中的conf.py,是给出的一个配置样例,不是在写死在程序中直接引用的配置文件。
可以通过给main.py启动参数指定配置路径的方式来让程序读取配置内容。
当然,这里的conf.py你可以换个类似的名字,比如settings.py。
或者你也可以使用其他格式的内容来编写配置文件,比如settings.yaml之类的。
6.2、跨目录调用
###bin/atm.py
import os
import sys
print(__file__)												#绝对路径
# print(os.path.abspath(__file__))							#相对路径
# print(os.path.dirname(os.path.abspath(__file__)))     	#os.path.dirname()返回目录名,不要最后一项
#往上两级目录
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

from conf import settings
from core import main

main.foo()
		###---core/main.py---###
		def foo():
    		print("Welcome to core's house")
    	###################
--->
D:/Python3.7.4/项目1/day10/Atm/bin/atm.py
D:\Python3.7.4\项目1\day10\Atm
Welcome to core's house									#在atm.py里调用别的目录下的文件
posted @ 2022-02-18 14:18  中國颜值的半壁江山  阅读(40)  评论(0)    收藏  举报