@生成器generator
a=(i*2 for i in range(10)) a.__next__()#等同于next(a),基本都不用,多用for循环
a.send(m)#将m传为yield的值
@生成器实例(IO异步的雏形)可看4-11-44-14‘’
import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer('A') c2 = consumer('B') c.__next__() c2.__next__() print("老子开始准备做包子啦!") for i in range(10): time.sleep(1) print("做了2个包子!") c.send(i) c2.send(i) producer("alex")
@可以直接作用于for
循环的对象统称为可迭代对象:Iterable
@判断是否是可迭代Iterable对象
from collections import Iterator print(isinstance(m,Iterator))
@判断是否是Iterator
对象
isinstance([], Iterator)
@生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
>>> isinstance(iter([]), Iterator)
True
@这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度。for循环本质就是生成器加next()。
@装饰器实例1
def eat(func): def inner(): print('吃第一个') m=func() print('至此二哥') return m return inner @eat def time1(): m='这是第一个' print(m) time1()
如果出现'NoneType' object is not callabl'错误,表示装饰器里的func放错位置
@装饰器实例2https://www.cnblogs.com/alex3714/articles/5765046.html
user_status = False #用户登录了就把这个改成True def login(auth_type): #把要执行的模块从这里传进来 def auth(func): def inner(*args,**kwargs):#再定义一层函数 if auth_type == "qq": _username = "alex" #假装这是DB里存的用户信息 _password = "abc!23" #假装这是DB里存的用户信息 global user_status if user_status == False: username = input("user:") password = input("pasword:") if username == _username and password == _password: print("welcome login....") user_status = True else: print("wrong username or password!") if user_status == True: return func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能 else: print("only support qq ") return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数 return auth def home(): print("---首页----") @login('qq') def america(): #login() #执行前加上验证 print("----欧美专区----") def japan(): print("----日韩专区----") @login('weibo') def henan(style): ''' :param style: 喜欢看什么类型的,就传进来 :return: ''' #login() #执行前加上验证 print("----河南专区----") home() # america = login(america) #你在这里相当于把america这个函数替换了 #henan = login(henan) # #那用户调用时依然写 america() # henan("3p")
@内置方法
'abs', 'bytes', 'dict', 'dir', 'exit', 'max', 'float', 'format', 'frozenset', 'id', 'input', 'int', 'len', 'list', 'min', 'next', 'open' 'pow' 'print', 'quit' 'range' 'reversed' 'set' 'sorted' 'str' 'sum', 'tuple', 'type', 'zip' 没用: 'ascii', 'bool','bytearray'(可修改的二进制字符串), 'callable'(可调用),'compile'(执行字符串内的程序), 'divmod', 'complex','copyright', 'credits', 'delattr', , 'map'(等同于列表生成式),'help', 'memoryview', 'object','oct'(八进制), 'repr'(转成字符串), 'slice'(就是切片),'vars', 'all'所有元素都是真就返回True 'any'只要有一个元素是真就返回True 'bin'整数转2进制 'chr'数字表示的ASC码转换为字符,与'ord'相反 'classmethod' 'enumerate', 'eval' 'exec' 'filter'生成条件迭代器filter(lambda n:n>5,range(10)) 'getattr' 'globals'本文件内所有全局变量及值生成键值对的集合,'locals'返回局部的 'hasattr' 'hash'让对象转为序号,特别快速从大文件中找数据。 'hex',转16进制 'isinstance', 'issubclass', 'iter', 'license' 'property' 'round', 'setattr', 'staticmethod', 'super' __import__ ‘os’等同于import os
@序列化:字典排序
a={1:3,5:2,10:1,100:4,15:2} print(sorted(a)) print(sorted(a.items())) print(sorted(a.items(),key=lambda x:x[1]))
运行结果:
[1, 5, 10, 15, 100] [(1, 3), (5, 2), (10, 1), (15, 2), (100, 4)] [(10, 1), (5, 2), (15, 2), (1, 3), (100, 4)]
@Json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
import json m={'2':2,"3":3} n=json.dumps(m) print(n,type(n))
运行结果
{"2": 2, "3": 3} <class 'str'>
@软件项目目录
Foo/ |-- bin/ | |-- foo | |-- foo/ | |-- tests/ | | |-- __init__.py | | |-- test_main.py | | | |-- __init__.py | |-- main.py | |-- docs/ | |-- conf.py | |-- abc.rst | |-- setup.py |-- requirements.txt |-- README
简要解释一下:
bin/
: 存放项目的一些可执行文件,当然你可以起名script/
之类的也行。foo/
: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/
存放单元测试代码; (3) 程序的入口最好命名为main.py
。docs/
: 存放一些文档。setup.py
: 安装、部署、打包的脚本。requirements.txt
: 存放软件依赖的外部Python包列表。README
: 项目说明文件。
除此之外,有一些方案给出了更加多的内容。比如LICENSE.txt
,ChangeLog.txt
文件等,我没有列在这里,因为这些东西主要是项目开源的时候需要用到。如果你想写一个开源软件,目录该如何组织,可以参考这篇文章。
下面,再简单讲一下我对这些目录的理解和个人要求吧。
关于README的内容
这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。
它需要说明以下几个事项:
- 软件定位,软件的基本功能。
- 运行代码的方法: 安装环境、启动命令等。
- 简要的使用说明。
- 代码目录结构说明,更详细点可以说明软件的基本原理。
- 常见问题说明。
我觉得有以上几点是比较好的一个README
。在软件开发初期,由于开发过程中以上内容可能不明确或者发生变化,并不是一定要在一开始就将所有信息都补全。但是在项目完结的时候,是需要撰写这样的一个文档的。
可以参考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包依赖都装好了。具体格式说明: 点这里。
关于配置文件的使用方法
注意,在上面的目录结构中,没有将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
之类的。
@