目录
7. 关于yield,生成器,__next__, __send__
18 被坑过的open函数, 及其read(), readline(), readlines()
三 垃圾的装饰器和@wraps(要引入),@property,@classmethod,@staticmethod
9 @classmethod和@staticmethod (mmp)
四. 驻留 python内存管理机制 1)引用计数,2)垃圾回收 3)内存池
一 内置模块
1. os模块 和 sys.platform
1. 查看当前路径下文件
os.getcwd() # 查看当前所在路径,不需要参数
os.listdir(path) # 返回目录下的的所有文件的列表
a = os.walk('C:\\Users\\Administrator\\Desktop\\mian')
for root, dirs, files in a:
print(files)
'''
root 所有文件地址,包括自身所在的文件夹, 和子子孙孙文件夹
dirs 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录)
'''
2. 绝对路径
os.path.abspath(path) # 返回path 的绝对路径
os.path.isabs(path) # 判断path 是否为绝对路径
os.path.relpath(path[, start]) # 从start开始计算相对路径
3. 查看路径的文件夹部分和文件名部分
os.path.split(path) # 将路径分解为(文件夹, 文件名), 返回的元组
os.path.join(path1, path2) # 拼接路径
# 例如
# os.path.join('//helo/', 'desky//aki')
# 返回 ‘hello/desky/aki’
os.path.split() if若路径最后时\ 即只是个路径,则只有文件夹有值;elif若是没有\, 则是个文件名,只有文件名;else 返回文件夹和文件名
os.path.join() 其中有绝对路径,则之前的path 被删除
例如:
os.path.join('D:\\desky\\aki', 'ok.py')
# D:\desky\aki\ok.py
a = os.path.join('D:\\deskyaki\\aki', 'D:\\desky\\enako')
# D:\deskyaki\enako
os.path.dirname(path) # 返回path的文件夹部分
os.path.basename(path) # 返回path的文件名
4 .查看系统时间 返回从新纪元年到现在的秒数
# 返回的1557198070.1615767是这样时间戳
a = os.path.getmtime(os.getcwd()) # 文件加最后修改的时间
datetime.datetime.fromtimestamp(a) # import datetime 时间戳转化2019-05-07 11:01:10.161577
os.path.getatime(os.getcwd()) # 最后访问时间
os.path.getctime(os.getcwd()) # 创建时间
5. 查看文件大小
os.path.getsize(os.getcwd()) # 查看文件大小
# 若是文件路径 则返回0
6 . 几个是否判断
os.path.exists(path) # 文件路径是否存在
os.path.isfile(path) # 是否为文件
os.path.isdir(path) # 是否为文件夹
os.path.isabs(path) # 是否为绝对路径
os.path.samefile(path1, path2) # 判断目录或文件是否相同
7. 创建文件夹 只是文件夹
os.makedirs('C:\\Users\\Administrator\\Desktop\\desky\\aki')
# 递归的创建, 就说可以新建好几层
# 默认有个参数 exist_ok=False,如果文件存在则报错, 可以改成False则不会报错
os.mkdir('C:\\Users\\Administrator\\Desktop\\aki')
# 只能建一层
注意: 默认情况下, 如果创建的文件夹已经存在,则会报错
8 . 其他的一些操作 sys.platform
os.rename('C:\\Users\\Administrator\\Desktop\\aki\\a.txt','C:\\Users\\Administrator\\Desktop\\aki\\b.txt')
# 重命名
os.remove('C:\\Users\\Administrator\\Desktop\\a\\a.txt')
# 删除的是文件,找不到报错 只在Unix, Windows 有效
os.unlink()
# 同上 可能不限定操作系统
os.rmdir('C:\\Users\\Administrator\\Desktop\\desky\\aki')
# 删除目录 该文件必须为空, 其中不能有任何的文件或文件夹
os.removedirs('C:\\Users\\Administrator\\Desktop\\desky\\aki')
# 递归的删除目录
os.chdir('C:\\Users\\Administrator\\Desktop\\美图啊')
# 修改当前操作目录
os.system("dir")
# 执行dir 相当于cmd 执行dir
ret = os.popen("dir").read()
# 执行dir, 不过它是有返回值的, 可以打印出来
os.name() # 查看当前的操作系统 nt--> windows posix--> linux
sys.platform:看操作系统的系统,因为返回值不一定,用startwith来识别
import sys
if sys.platform.startswith('win32'):
print('ok')
# window ’win32‘
# linux ’linux‘
# Windows/Cygwin ‘cygwin’
# Mac OS X ‘darwin’
9. 查看状态
os.stat('path/filename') 获取文件/目录信息 的结构说明
# st_mode: inode 保护模式
# st_ino: inode 节点号。
# st_dev: inode 驻留的设备。
# st_nlink: inode 的链接数。
# st_uid: 所有者的用户ID。
# st_gid: 所有者的组ID。
# st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
# st_atime: 上次访问的时间。
# st_mtime: 最后一次修改的时间。
# st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上# # (如Windows)是创建时间(详细信息参见平台的文档)。
2.shutil 模块
import os
import shutil
os.chdir('C:\\Users\\Administrator\\Desktop')
# 指定工作目录, 哟吼写路径直接写相对路径
a = shutil.copy('a\\a.txt', 'Desktop.txt')
# 有返回值,返回文件复制的路径
b = shutil.move('aki\\a.txt', 'b.txt')
# 有返回值, 返回文件移动的路径
shutil.rmtree('aki')
# 无返回值, 删除文件, 包括里面的文件,文件夹
3. 牛逼的zipfile
import zipfile
import os
# 查看压缩包
os.chdir('C:\\Users\\Administrator\\Desktop') # 指定工作目录
exampleZip = zipfile.ZipFile('a.zip') # 打开压缩文件
nameList = exampleZip.namelist() # 返回包里所有的文件, 文件夹列表
print(nameList) # ['a.txt', 'wenjain1/', 'wenjain1/b.txt']
info = exampleZip.getinfo('a.txt') # 选择单个文件, a.txt是压缩包里的
preSize = info.file_size # 返回a.txt 压缩前大小, pycharm没提示
print(preSize) # 压缩后 文件大小
aftSize = info.compress_size
print(aftSize)
exampleZip.close()
# 解压压缩包
exampleZip = zipfile.ZipFile('a.zip') # 打开压缩文件
exampleZip.extractall('c') # 解压当前全部文件, 也可以指定路径, 路径不存在则自动创建, 默认当前路径解压
exampleZip.extract('a.txt') # 解压指定单个文件, 也可指定路径,路径不存在自动创建, 默认当前路径
exampleZip.close()
# 创建压缩包
newZip = zipfile.ZipFile('new.zip', 'w') # 先创建再以也写入方式打开压缩包
newZip.write('h.txt', compress_type=zipfile.ZIP_DEFLATED) # 写入文件, 指定压缩方式,这里为默认
newZip.close()
4. random 模块 import random
import random
random.random() # 没参数 随机返回0 - 1 的浮点数
random.uniform(a, b) # 随机返回a - b 的浮点数, 包括a. b
random.randint(a, b) # 随机返回a - b 的整数, 包括a, b
d = random.randrange(start, stop, step) # 随机返回从指定开始到结束指定步长的一个值
# randrange
random.choice([1, 3, 4, 5]) # 随机返回 列表或字符串 中的一个
random.sample([1, 3, 4, 5], 3) # 随机返回 列表或字符串 中的指定个数·
l = [1, 3, 4, 5] # 打乱列表, 只能对列表操作, 字符串不可以
random.shuffle(l) # 本身应该没有返回值,
print(l)
5. 烦人的time和更烦人的datetime模块
time模块
import time
time.time()
# 1557320470.694915
time.ctime()
# ctime 还可以接受时间戳参数, 转换成相应时间
# Wed May 8 21:01:10 2019
time.strftime("%Y-%m-%d %X")
# 2019-05-08 21:01:10
# time 运算
t1 = time.time()
t2 = t1 + 10
1. time.time()方法
没啥说的, 一般把它放在程序的最前面和最后面,相减实现测试代码的运行时间,但是time.sleep()的话,时间也会被算进去的。
2. time.perf_counter()方法
perf_counter()会包含sleep()休眠时间,适用测量短持续时间。 跟time.time()没啥区别, 不过它是计算的系统时间差。
3 ime.process_time()方法
process_time()不包括sleep()休眠时间期间经过的时间
datetime模块
from datetime import time
import datetime
import time
a = datetime.datetime.now() # 返回现在的时间
# 2019-05-08 21:06:20.060533
y = a.year # 返回年
m = a.month # 返回月
d = a.day # 返回天
h = a.hour # 返回小时
a.date() # 返回日期
a.time() # 返回几点
# 很重要
datetime.datetime.fromtimestamp(time.time())
# 将时间戳 转为正常时间 2019-05-08 21:13:40.842107
"""
timedelta() 接受 weeks, days, hours, minutes, seconds, milliseconds, microseconds,
但是 没有 month 和 year
"""
delta = datetime.timedelta(days=11, hours=10, minutes=9, seconds=46)
# 11 days, 10:09:46
delta.days # 返回天数
delta.seconds # 返回的是 hours + minutes + seconds 的总计秒数
delta.microseconds # 微秒??
delta.total_seconds() # 返回总共的秒数
str(delta) # 转为字符串
# timedelta # timedelta 计算一段时间
aki = datetime.timedelta(days=100)
desky = aki + datetime.datetime.now()
# 2019-08-16 21:27:03.787900
"""
strftime() 格式化时间, 将datetime对象转化为str
"""
datetime.datetime.now().strftime('%Y/%m/%d')
# 2019/05/08
ed = datetime.datetime.now().strftime('%H:%m %p')
# 21:05 PM
"""
strptime() 将str 转为 datetime 对象
"""
ed1 = datetime.datetime.strptime('Sep-21-09 16:34', '%b-%d-%y %H:%M')
# <class 'datetime.datetime'> 将str 转为 datetime 对象 后面的参数是前面参数的 格式
- %y 两位数的年份表示(00-99)
- %Y 四位数的年份表示(000-9999)
- %m 月份(01-12)
- %d 月内中的一天(0-31)
- %H 24小时制小时数(0-23)
- %I 12小时制小时数(01-12)
- %M 分钟数(00=59)
- %S 秒(00-59)
- %a 本地简化星期名称
- %A 本地完整星期名称
- %b 本地简化的月份名称
- %B 本地完整的月份名称
- %c 本地相应的日期表示和时间表示
- %j 年内的一天(001-366)
- %p 本地A.M.或P.M.的等价符
- %U 一年中的星期数(00-53)星期天为星期的开始
- %w 星期(0-6),星期天为星期的开始
- %W 一年中的星期数(00-53)星期一为星期的开始
- %x 本地相应的日期表示
- %X 本地相应的时间表示
- %Z 当前时区的名称
- %% %号本身
6 .re 从正则表达式开始的复杂模块
先说经常弄混的groups
-
group() 不传或传入0 查看正则式匹配的所有内容
-
group(1) 传入1, 查看正则式中第一个括号里的内容, 默认从1开始
-
group(2) 传入2, 查看括号里的的二个内容
-
groups() 将正则里的括号内容组成元组
-
groupdict() 返回分组匹配的字典, 后面举例子
-
不用group 返回的使这种东西<re.Match object; span=(0, 23), match='寂しさに耐えられるものこそ、かみになれるのだ\u3000'>
然后再说说,match和search 的区别, mach是从一开始查找,也就是说从匹配文本一开始匹配成功接着匹配,开始没匹配就返回none
,search 浏览全部字符串,匹配第一个符合规则的字符串,非匹配成功返回none。
如re.match(‘super’, ‘insuperable’),一开始就匹配不成功,返回none。 re.search(‘super’, ‘insuperable’)成功。
search只会匹配第一个符合规则的,而findall匹配全部
还有 r 只是告诉python 不要转义, \\n 就匹配\\n 而不是 \n
还有我决定了, 以后尽量用re.compile 进行预编译,虽然并不是不用不行,以此为为证
(到目前为止你根本没用直接到过正则 2019.8.25 20:16)
# group 们
import re
content = '寂しさに耐えられるものこそ、かみになれるのだ'
a = re.compile(r'(?P<k>寂しさ.*)')
o = re.match(a, content)
print(o.group())
print(o.groups())
print(o.groupdict())
# 要返回字典得有 ?P<>
# 寂しさに耐えられるものこそ、かみになれるのだ
# ('寂しさに耐えられるものこそ、かみになれるのだ',)
# {'k': '寂しさに耐えられるものこそ、かみになれるのだ'}
# match
import re
content = '寂しさに耐えられるものこそ、かみになれるのだ \n aki'
a = re.compile('寂しさ.*', re.S)
p = re.match(a, content)
print(p.group())
# 加re.S 忽略\n
# 寂しさに耐えられるものこそ、かみになれるのだ
# aki
# 否则
# 只匹配到
# 寂しさに耐えられるものこそ、かみになれるのだ
# sub (正则表达式, 要替换正则文本, 要被操作的文本)
import re
content = '寂しさに耐えられるものこそ、かみになれるのだ'
a = re.compile('の.')
k = re.sub(a, '陈日天', content, 1)
# 后面的1 是替换一次
# 还可以直接用编译的结果操作
h = a.sub('陈日天', content)
print(k)
# 寂しさに耐えられるも陈日天そ、かみになれるのだ
print(h)
# 寂しさに耐えられるも陈日天そ、かみになれる陈日天
# subn
import re
origin = "寂しさに耐えられるものこそ、かみになれるのだ"
a, b = re.subn("の", "的", origin, 1) # 替换匹配成功的指定位置字符串,并且返回替换次数,可以用两个变量分别接受
# 可以指定匹配次数
print(a)
print(b)
# 寂しさに耐えられるも的こそ、かみになれるのだ
# 1
# split
import re
origin = "hello alex bcd alex lge alex 2acd 19"
r = re.split("a\w+", origin) #根据正则匹配分割字符串
print(r)
# ['hello ', ' bcd ', ' lge ', ' 2', ' 19']
# findall
import re
"""
findall 找到所有匹配成功的,返回一个列表,
下面的例子说明,匹配成功的,则不参与下次匹配,
匹配到 2b3 ,然后3c4也符合规则, 不过3参与了上一次匹配, 所以3c4不匹配
"""
a = re.compile(r'\d+\w\d+')
r = re.findall(a, "a2b3c4d5")
print(r)
# ['2b3', '4d5']
# 分组
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("a(\w+)", origin) # 一个括号是一个分组
print(r)
# ['lex', 'lex', 'lex', 'cd']
# 多个分组
origin = "hello alex bcd alex lge alex acd 19"
r = re.findall("(a)(\w+)", origin) # 两个括号, 两个分组
print(r)
# [('a', 'lex'), ('a', 'lex'), ('a', 'lex'), ('a', 'cd')]
贪婪非贪婪
import re
origin = "akiiiiii"
a = re.compile('aki+?')
# ? 非贪婪 aki
# a = re.compile('ak+')
# 贪婪 akiiiiii
c = re.match(a, origin)
print(c.group())
findall 和 split 的特殊用法(优先查询)
import re
# findall
c = re.compile('君のことが(好き|嫌い)です')
ret = c.findall('君のことが好きです')
# ['好き'] 只会匹配括号里的
rc = re.compile('君のことが(?:好き|嫌い)です')
rret = rc.findall('君のことが好きです')
# ['君のことが好きです']
# split
ret1 = re.split(r"\d+", "desky1aki2kimi3")
# ['desky', 'aki', 'kimi', '']
# 加分组会把分割的也匹配上
ret2 = re.split(r'(\d+)', ' "desky1aki2kimi3"')
# [' "desky', '1', 'aki', '2', 'kimi', '3', '"']
其他的一些东西:
正则匹配的分组和 应用分组
ret = re.search(r"<(?P<tag_name>\w+)>\w+</(?P=tag_name)>", "<h1>ojbk</h1>")
ret1 = re.search(r"<(\w+)>\w+</\1>", "<h1>ojbk</h1>")
print(ret.group('tag_name'))
# h1
print(ret1.group())
# <h1>ojbk</h1>
2019年9月7日 20点27分 紧急插入
import re
c = "asd123asdfa123aasd121233dcacasaw1"
a = re.compile(r"(\d{3,4})")
k = re.findall(a, c)
print(k)
# ['123', '123', '1212']
当输入正则(\d{3,空格4}), 就多了个空格,就也匹配不到了什么。。。。。
. | 可以是任何一个字符,匹配任意除换行符"\n"外的字符(在DOTALL模式中也能匹配换行符) | |
\ | 转义 | |
* | 0个或多个 | |
+ | 1个或多个 | |
? | 1个或0个, 防止贪婪匹配 | |
| | 或 abc|d | |
{} | 需要字符串里完全符合,匹配规则,就匹配,(规则里的 {} 元字符)前面的一个字符,是自定义字符数,位数的原本字符,{m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次,{0,}匹配前一个字符0或多次,等同于*元字符,{+,}匹配前一个字符1次或无限次,等同于+元字符,{0,1}匹配前一个字符0次或1次,等同于?元字 | |
[] |
| |
() | 也就是分组匹配,()里面的为一个组也可以理解成一个整体,如果()后面跟的是特殊元字符如 (adc)* 那么*控制的前导字符就是()里的整体内容,不再是前导一个字符 | |
^ | 略 | |
$ | 略 |
\d | \d匹配任何十进制数,它相当于类[0-9],\d+如果需要匹配一位或者多位数的数字时用 |
\D | \D匹配任何非数字字符,它相当于类[^0-9] |
\s | \s匹配任何空白字符,它相当于类[\t\n\r\f\v] |
\S | \S匹配任何非空白字符,它相当于类[^\t\n\r\f\v] |
\w | \w匹配包括下划线在内任何字母数字字符,它相当于类[a-zA-Z0-9_] |
\W | \W匹配非任何字母数字字符包括下划线在内,它相当于类[^a-zA-Z0-9_] |
\A | 仅匹配字符串开头,同^ |
\Z | 仅匹配字符串结尾,同$ |
\b | b匹配一个单词边界,也就是指单词和空格间的位置 |
\B | [^\b] |
re.S | 使.匹配包括换行在内的所有字符 |
re.I | 使匹配对大小写不敏感 大写 i |
re.L | 做本地化识别(locale-aware)匹配,法语等 |
re.M | 多行匹配,影响^和$ |
re.X | 该标志通过给予更灵活的格式以便将正则表达式写得更易于理解 |
re.U | 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B |
7. collection 模块
1. 不知道有什么用的namedruple()
from collections import namedtuple
point = namedtuple('point', ['x', 'y']) # 第一个参数是随意起的,
p = point(1, 2)
print(p) # point(x=1, y=2)
print(p.x) # 1
print(p.y) # 2
2. 不是collections 的queue()
import queue
q = queue.Queue()
q.put(1)
q.put(2)
q.put(3)
q.put(4)
print(q.get()) # 拿到第一个进队的1
print(q.qsize()) # 拿到队的大小3, 一共四个, 前面拿走一个
3. 双边队列deque(), 可插队
# 双边队列, 能插队
from collections import deque
dq = deque()
dq.append(2) # 右侧添加 deque([2])
dq.append(4) # 右侧添加 deque([2, 4])
dq.appendleft(1) # 左侧添加 deque([1, 2, 4])
dq.insert(2, 3) # 在索引2置插入,只有双边队列才能插入 deque([1, 2, 3, 4])
print(dq) # deque([1, 2, 3, 4])
print(dq.pop()) # 从右侧 拿出4
print(dq.popleft()) # 从左侧 拿出1
print(dq) # deque([2, 3])
4. defaulttict()
from collections import defaultdict
values = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90]
my_dict = defaultdict(list)
# defaultdict 参数必须是可迭代的, list, set,str, int
# 如果想向指定返回值指定值
# defaultdict(lambda: 5)
for value in values:
if value > 66:
my_dict['k1'].append(value)
else:
my_dict['k2'].append(value)
print(my_dict)
# defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99, 90]})
如果不用defaultdict,那么先创建空字典,再执行my_dict['k1'].append(value)时,会报错, 因为空字典没有任何值。
from collections import defaultdict
dic = defaultdict(lambda: 0)
dic['ss']
print(dic)
defaultdict(<function <lambda> at 0x000002613B98C1E0>, {'ss': 0})
5. 插入 setdefault
setdefault和get功能类似,都是可以拿到字典的键的值,setdefault, 当拿到字典不存在的键时, 会自动创建键值。
>>> y = d.setdefault("y", 4)
>>> y
4
>>> d
{'y': 4, 'x': 3}
6. 没啥用的计数conter()
from collections import Counter
c = Counter('abcdeabcdabcaba')
print(c)
# Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})
8 序列化模块 json, pickle
①json.dumps(), json.loads() (可以序列化嵌套的对象)
import json
list_dic = [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
print(type(list_dic[1]))
# <class 'list'>
list_str = json.dumps(list_dic)
print(type(list_str[1]))
# <class 'str'>
list_first = json.loads(list_str)
print(list_first)
# [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
② json.dump(), json.load() (不同于上面的, 上面是将数据写入内存, 而这个是写入文件)
import json
f = open('json_file', 'w')
dic = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
json.dump(dic, f) # dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()
f = open('json_file', 'r')
dic2 = json.load(f) # load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
f.close()
print(type(dic2), dic2)
注意:如果系列化出现中文,可能不会正常显示 在dumps, 或 dump 里加个参数 ensure_ascii=False
③ pickle.dumps(), pickle.loads() dumps将数据转化成二进制,loads将数据转化成原内容
import pickle
dic = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
str_dic = pickle.dumps(dic)
print(str_dic) # 一串二进制内容
print(type(str_dic)) # <class 'bytes'>
dic2 = pickle.loads(str_dic)
print(dic2) # 字典
④pickle.dump(), pikle.load() 写入文件,不过不管写入还是读取都要以二进制方式
import pickle
import time
struct_time = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file', 'wb')
pickle.dump(struct_time, f)
f = open('pickle_file', 'rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)
f.close()
几点说明:
关于json, 它只能序列化python数据类型,字符串,字典, 列表 元组,对于djano对象就不OK了。。而且,它的load,dump只能一次性写入,在一次性读取,不能分着写,分着多。不过json格式,兼容好, 编程语言的大伙们都认识。
而pikle,用于python特有的类型 和 python的数据类型, 它的load,dump可以分着写,分着读。 不过序列化转成二进制,就没几个人能看懂了。
9. 日志logging 模块
1. 第一种,简单,能做的事少
import logging
# 不能同时保存文件和打印到屏幕
logging.basicConfig(level=logging.DEBUG, # 输入级别,级别之上才打印
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='test.log',
filemode='w')
logging.debug('debug message') # 低级别
logging.info('info message') # 正常信息
logging.warning('waring message') # 警告信息
logging.error('error message') # 错误信息
logging.critical('critical message') # 严重的错误信息
2. 第二种,相对复杂,但比较强悍
import logging
logger = logging.getLogger()
# 创建输出到文件的对象
fh = logging.FileHandler('log.log', encoding='utf-8')
# 创建输出到屏幕的对象
sh = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
formatter1 = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
# 文件操作符 和 格式关联
fh.setFormatter(formatter)
# 屏幕和 格式关联
sh.setFormatter(formatter1)
# logger 对象 和 文件操作符 关联
logger.addHandler(fh) # 输出到文件
logger.addHandler(sh) # 打印到屏幕
logging.debug('debug message') # 低级别
logging.info('info message') # 正常信息
logging.warning('waring message') # 警告信息
logging.error('error message') # 错误信息
logging.critical('critical message') # 严重的错误信息
10. 不明觉厉的 unittest 模块
test case :一个完整的测试单元,执行该测试单元可以完成对某一个问题的验证,完整体现在:
测试前环境准备(setUp),执行测试代码(run),以及测试后环境还原(tearDown);
test suite :多个测试用例的集合,测试套件或测试计划;
testLoader :加载TestCase到TestSuite中的,其中loadTestsFrom__()方法用于寻找TestCase,
并创建它们的实例,然后添加到TestSuite中,返回TestSuite实例;
test runner :执行测试用例,并将测试结果保存到TextTestResult实例中,包括运行了多少测试用例,
成功了多少,失败了多少等信息;
test fixture:一个测试用例的初始化准备及环境还原,主要是setUp() 和 setDown()方法
原理:
编写TestCase,由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,
最后将运行的结果保存在TextTestResult中。
结果:
- .:代表测试通过。
- F:代表测试失败,F 代表 failure。
- E:代表测试出错,E 代表 error。
- s:代表跳过该测试,s 代表 skip。
一些断言方法:
# assertEqual(a, b) a == b
# assertNotEqual(a, b) a != b
# assertTrue(x) bool(x) is True
# assertFalse(x) bool(x) is False
# assertIsNone(x) x is None
# assertIsNotNone(x) x is not None
# assertIn(a, b) a in b
# assertNotIn(a, b) a not in b
# assertIs(a, b) a is b
# assertIsNot(a, b) a is not b
# assertlsInstance(a, b) isinstance(a, b)
# assertNotIsInstance(a, b) not isinstance(a, b)
import unittest
class MyTest(unittest.TestCase):
def tearDown(self):
print('测试之后执行的操作')
def setUp(self):
print('测试之前执行的操作')
@classmethod
def tearDownClass(cls):
# 必须使用 @classmethod装饰器, 所有test运行完后运行一次
print('所有test运行后运行一次')
@classmethod
def setUpClass(cls):
print('所有test运行前运行一次')
def test_b_run(self):
self.assertEqual(2, 5)
def test_a_run(self):
self.assertEqual(1, 8)
if __name__ == "__main__":
unittest.main()
要说的东西很多mmp:
1.首先这个类一定要继承unittest。
2.测试函数一定要以test开头。
3.tearDowm()在每个测试函数之后执行一遍,而setUp()在每个测试函数执行前执行一遍。
4.tearDownClass()在所有测试函数后只执行一遍,而setUpClass()在所有测试函数之前只执行一遍,而且两个函数必须用@classmethod 装饰。
5.测试函数断言为False则抛出异常。
6.而且而且测试函数的执行顺序不是自上而下执行的,是按测设函数名称顺序执行的,比如此例先执行的是test_a_run。
7. 测试函数没返回值,没有参数。
如果要按代码顺序执行测试函数可以如下:即先执行a再执行b。
if __name__ == "__main__":
tests = [MyTest('test_b_run'), MyTest('test_a_run')]
suite = unittest.TestSuite() # 创建一个TestSuite实例
suite.addTests(tests) # 添加
runner = unittest.TextTestRunner()
runner.run(suite)
下一段代码是用makeSuite,将一个类的测试方法加入,还是按名称的执行顺序
if __name__ == "__main__":
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(MyTest)) # 添加一个类的测试函数,按名称顺序
runner = unittest.TextTestRunner()
runner.run(test_suite)
下一段代码将错误信息写入文件,要装xmlrunner
import unittest
import xmlrunner
class MyTest(unittest.TestCase):
def tearDown(self):
print('测试之后执行的操作')
def setUp(self):
print('测试之前执行的操作')
@classmethod
def tearDownClass(cls):
# 必须使用 @classmethod装饰器, 所有test运行完后运行一次
print('所有test运行后运行一次')
@classmethod
def setUpClass(cls):
print('所有test运行前运行一次')
def test_b_run(self):
self.assertEqual(2, 5)
def test_a_run(self):
self.assertEqual(1, 8)
if __name__ == "__main__":
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(MyTest)) # 添加一个类的测试函数
runner = xmlrunner.XMLTestRunner(output='report') # 指定报告放的目录
runner.run(test_suite)
跳过测试函数 skip:
unittest.skip(reason) 直接跳过
unittest.skipIf(condition,reason)、 满足条件跳过,True则跳过
unittest.skipUnless(condition,reason), 不满条件跳过, False跳过
即在满足condition条件下跳过该用例,reason用于描述跳过的原因
import unittest
import sys
class MyTest(unittest.TestCase):
def tearDown(self):
print('测试之后执行的操作')
def setUp(self):
print('测试之前执行的操作')
@classmethod
def tearDownClass(cls):
# 必须使用 @classmethod装饰器, 所有test运行完后运行一次
print('所有test运行后运行一次')
@classmethod
def setUpClass(cls):
print('所有test运行前运行一次')
def test_b_run(self):
self.assertEqual(2, 5)
# 因为是windows系统,unless里的条件为False,所以跳过这个测试函数
@unittest.skipUnless(sys.platform.startswith('linux'), '跳过的原因, 操作系统不是linux')
def test_a_run(self):
self.assertEqual(1, 8)
if __name__ == "__main__":
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(MyTest)) # 添加一个类的测试函数
runner = unittest.TextTestRunner()
runner.run(test_suite)
11 itertools
1 .zip_longest
因为itertools里东西太多了,只写了这个可能用到的。
zip_longest()可以传多个可迭代对象,还可以传个fillvalue个参数, 整个函数和map相似, 不过zip以最短的为基准,长的后面都不要了,而zip_longest()如其名, 以最长为基准, 不足的默认用None补上, 也可以指定fillvalue。
from itertools import zip_longest
m1 = [[1, -2, 1], [2, -1, 1]]
m2 = [[1, 1, 1]]
m3 = [[1, 2, 3, 4]]
a = [i for i in zip_longest(m1, m2, m3)]
b = [i for i in zip_longest(m1, m2, m3, fillvalue="何もないよ。")]
# a [([1, -2, 1], [1, 1, 1], [1, 2, 3, 4]), ([2, -1, 1], None, None)]
# b [([1, -2, 1], [1, 1, 1], [1, 2, 3, 4]), ([2, -1, 1], '何もないよ。', '何もないよ。')]
2. combinations (返回可迭代对象的全部组合,不区分顺序)
combinations(可迭代对象,一组几个元素),combinations(list1, 1) 每组一个元素[[(1,), (2,), (3,)]]
from itertools import combinations
list1 = [1, 2, 3]
list2 = []
for i in range(1, len(list1)+1):
p = combinations(list1, i)
list2.append(list(p))
print(list2)
# [[(1,), (2,), (3,)], [(1, 2), (1, 3), (2, 3)], [(1, 2, 3)]]
2. permutations 把顺序不同的看成不同组
from itertools import permutations
list1 = [1, 2, 3]
list2 = []
for i in range(1, len(list1)+1):
p = permutations(list1, i)
list2.append(list(p))
print(list2)
# [[(1,), (2,), (3,)], [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)],
# [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]]
13 测试用的cProfileN
- 第一行:N个函数调用被监控,其中n个是原生调用(不涉及递归)
- ncalls:函数被调用的次数。如果这一列有两个值,就表示有递归调用,第二个值是原生调用次数,第一个值是总调用次数。
- tottime:函数内部消耗的总时间。(可以帮助优化)
- percall:是tottime除以ncalls,一个函数每次调用平均消耗时间。
- cumtime:之前所有子函数消费时间的累计和。
- filename:lineno(function):被分析函数所在文件名、行号、函数名。N
import cProfile
import random
def f2(l1):
l1 = [i for i in l1 if i < 0.5]
l2 = sorted(l1)
return {i*i for i in l2}
l1 = [random.random() for i in range(100000)]
cProfile.run('f2(l1)')
"""
7 function calls in 0.030 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.003 0.003 0.030 0.030 <string>:1(<module>)
1 0.000 0.000 0.028 0.028 tests.py:5(f2)
1 0.004 0.004 0.004 0.004 tests.py:6(<listcomp>)
1 0.013 0.013 0.013 0.013 tests.py:9(<setcomp>)
1 0.000 0.000 0.030 0.030 {built-in method builtins.exec}
1 0.011 0.011 0.011 0.011 {built-in method builtins.sorted}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
"""
cProfile.run("test()", filename="result.out") # 增加排序方式
cProfile.run("test()", filename="result.out", sort="cumulative")
# 直接把分析结果打印到控制台 python -m cProfile test.py
# 把分析结果保存到文件中 python -m cProfile -o result.out test.py
# 增加排序方式 python -m cProfile -o result.out -s cumulative test.py
二 比较吊的内置函数
1.reduce()
函数会对参数序列中元素进行累积,用传给reduce中的函数function(只有两个参数)先对集合中的第一第二个元素进行操作,得到的结果与第三个数据用functio进行计算。
from functools import reduce
def add(x, y):
return x + y
a = reduce(add, [1, 3, 4, 5, 6])
print(a)
# reduce(function, iterable, initializer)
# 1.函数有两个参数 2.可迭代对象 3.和旋, 初始参数
2.partial()
偏函数时间所用承载的函数作为partial()函数的第一个参数, 原函数各个参数以此作为partial的后续参数,除非使用关键字
from functools import partial
def mod(n, m):
return n % m
# 取余数
f = partial(mod, 50)
# 传第一个参数
a = f(6)
# 传第二个参数
3. enumerate()
用于将一个可遍历的数据对象组合为一个索引序列
a = ['desky', 'aki', 'enako', '陈日天']
c = list(enumerate(a, start=1))
# start 默认不写为0
# [(1, 'desky'), (2, 'aki'), (3, 'enako'), (4, '陈日
4. 判断函数
isalpha() # 如果字符串只包含字母,并且非空
isalnum() # 只包含字母数字, 并且非空
isdecimal() # 只有数字字符, 并且非空
isspace() # 只用空格, 制表符, 换行,非空
istitle() # 只以大写字母开头, 后面都为小写
5 . 总忘记用法的 join() split()
# join str, list 转为 str
'_'.join(['a', 'b', 'c', 'd'])
# a_b_c_d
'_'.join('abcd')
# a_b_c_d
# 只有str 转为 list
'a_b_c_d'.split('_')
# ['a', 'b', 'c', 'd']
6. zip()
# zip
days = ['周一', '周二', '周三', '周四', '周五'] # 'xx', 'xxx', 'xxx' 这样的字符串也是可以的
do = ['a', 'b', 'c', 'd']
eat = ['あ', 'い', 'う', 'え']
print(zip(days, do))
# <zip object at 0x000001BFA8C8ACC8>
print(list(zip(days, do, eat)))
# [('周一', 'a', 'あ'), ('周二', 'b', 'い'), ('周三', 'c', 'う'), ('周四', 'd', 'え')]
print(dict(zip(days, do)))
# {'周一': 'a', '周二': 'b', '周三': 'c', '周四': 'd'}
# zip的解压
a = [1, 2, 3]
b = [4, 5, 6, 7]
c = zip(a, b)
print(list(c))
a1, a2 = zip(*zip(a, b)) # 解压, 当然b长度变成3
# zip(*c) 居然tm不行
7. 关于yield,生成器,__next__, __send__
"""
实现每传入一个值, 打印平均值
"""
def average():
sum = 0
count = 0
avg = 0
while True:
num = yield avg
print('n', num)
sum += num
print('s', sum)
count += 1
avg = sum/count
print(avg)
# send 和next 都在到yield hold 住
avg_g = average() # 生成一个生成器
avg_g.__next__() # 第一次执行到yield, 不执行yield 后面的程序
avg_g1 = avg_g.send(10) # 把10 赋给num, 接着执行第yield 后面的程序,
avg_g2 = avg_g.send(20)
avg_g3 = avg_g.send(30)
print(avg_g1) # 10.0
print(avg_g2) # 15.0
print(avg_g3) # 20.0
8. filter() 和 map()
# filter
def aki(x):
return x % 2 == 1
ret = filter(aki, [1, 2, 3, 4, 5, 6])
# filter(func, 可迭代对象)
print(ret) # <filter object at 0x000001F647AE0518>
for i in ret:
print(i)
# 1 \n 3 \n 5\n
#map
ret = map(abs, [1, -4, 6, 8])
# map(func, 可迭代对象
print(ret)
# <map object at 0x000001C51B031898>
for i in ret:
print(i)
# 1 \n 4\n 6 \n 8 \n
filter()和map(): filter执行之后结果集合小于等于执行之前的个数,只是筛选, 不会改变值。
而 map执行前后元素个数不变, 值可能变
9.exec() 和 eval()
# 都可以执行字符串程序
exec('print("objk")')
eval('print("ojbk")')
print(eval('1+2+3'))
# 执行了, 有返回值 6
print(exec('1+2+3'))
# 执行了, 但没返回值, 返回值None
第二部分它们都执行了, 只不过eval() 有返回值,exec() 没返回值
10.pow() 和 divmod()
# divmod 7/2 得到商和余数
a, b = divmod(7, 2)
print(a, b)
# 3, 1
# pow 2的3次幂
c = pow(2, 3)
print(c)
# 8
# 2的3次幂 再取到余数
d = pow(2, 3, 5)
print(d)
# 3
11.uuid和hashlib
uuid获取随机字符串, hashlib对字符串加密
# uuid
import uuid
v = uuid.uuid4()
print(type(v)) # <class 'uuid.UUID'>
print(str(v)) # 787db7fb-ffc6-4101-b10a-f611f9a69654 获取随机字符串
# hashlib
def md5(arg):
hash = hashlib.md5(b'aki')
hash.update(bytes(arg, encoding='utf-8'))
return hash.hexdigest()
print(md5('123')) # 只能对字符串加密
12. 总TM忘记的encode和decode
str ----> bytes
s1 = s.encode('utf-8)
s1 = bytes(s, 'utf-8')
bytes ----> str
b1 = b.decode('utf-8')
b1 = str(b, 'utf-8')
13.作用域的 global 和nonlocal
1.对于不可变数据类型,在局部可以查看,这里可以打印,但是不能直接修改, a+=1, 会报错
a = 1
def func():
print(a)
a += 1 # 提示有错误
2.global(), 加个它就可以解决
a = 1
def func():
global a
a += 1 # 价格global 就行了
3.nonlocal(), 不会找到外部的全局变量, 而是找到最近的内部局部变量
a = 1
def outer():
a = 1 # 找的是这个a
def inner():
b = 2
def inner2():
nonlocal a
a += 1
......
14 不知道有什么用的register
注意: 先注册的后运行
from atexit import register
def reg_1():
print('I`m reg1 ')
def reg_2(name):
print('I`m reg2 %s' % name)
def reg_3(name1, name2, name3):
print('I`m reg3 %s %s %s' % (name1, name2, name3))
register(reg_1)
register(reg_2, 'reg2')
register(reg_3, 'reg3', 'reg3', 'reg3')
@register
def reg_4():
print('I`m reg4')
# I`m reg4
# I`m reg3 reg3 reg3 reg3
# I`m reg2 reg2
# I`m reg1
# 先注册后运行
15 list方法 append和extend 区别
extend
lia = ['aa', 1, 'bb', None]
lib = ['cc', 2, {'dd': 3}]
lic = {'dd': 3, "ff": 4}
lia.extend(lib)
print(lia)
# ['aa', 1, 'bb', None, 'cc', 2, {'dd': 3}]
lib.extend(lic)
print(lib)
# ['cc', 2, {'dd': 3}, 'dd', 'ff']
extend的若为列表将列表直接加入, 若为字典,只将字典的键加入
append
lia = ['aa', 1, 'bb', None]
lib = ['cc', 2, {'dd': 3}]
lia.append(lib)
print(lia)
# ['aa', 1, 'bb', None, ['cc', 2, {'dd': 3}]]
append直接加个列表
16 最新认识的any()和all()
any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,全部为 False则返回 False,如果有一个为 True,则返回 True。
元素除了是 0、空、FALSE 外都算 TRUE。
all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。
元素除了是 0、空、None、False 外都算 True。
通俗的说就是, any有一个True, 就为True, 只有都为False才返回False。alld都为True才为True, 有一个为False, 就为False.
>>>any(['a', 'b', 'c', 'd']) # 列表list,元素都不为空或0
True
>>> any(['a', 'b', '', 'd']) # 列表list,存在一个为空的元素
True
>>> any([0, '', False]) # 列表list,元素全为0,'',false
False
>>> any(('a', 'b', 'c', 'd')) # 元组tuple,元素都不为空或0
True
>>> any(('a', 'b', '', 'd')) # 元组tuple,存在一个为空的元素
True
>>> any((0, '', False)) # 元组tuple,元素全为0,'',false
False
>>> any([]) # 空列表
False
>>> any(()) # 空元组
False
>>> all(['a', 'b', 'c', 'd']) # 列表list,元素都不为空或0
True
>>> all(['a', 'b', '', 'd']) # 列表list,存在一个为空的元素
False
>>> all([0, 1,2, 3]) # 列表list,存在一个为0的元素
False
>>> all(('a', 'b', 'c', 'd')) # 元组tuple,元素都不为空或0
True
>>> all(('a', 'b', '', 'd')) # 元组tuple,存在一个为空的元素
False
>>> all((0, 1, 2, 3)) # 元组tuple,存在一个为0的元素
False
>>> all([]) # 空列表
True
>>> all(()) # 空元组
True
17 异常处理
1. raise 和 raise/from 的使用方法https://blog.csdn.net/jpch89/article/details/84315444#commentBox
感觉没什么区别,,,,
2.try except except
s = [1]
try:
print(123)
s[233]
print(456)
# except Exception:
# print("春、恋、花以外の")
# 多数异常可以直接写这个,触发异常, 不用指定是什么异常
except IndexError:
print("何もない")
"""
123
何もない
"""
可以看出,try最先被执行,知道出现异常直接跳到except,try后面的代码不会被执行。
3 try finally
s = [1]
try:
print(123)
s[233]
print(456)
except IndexError:
print("何もない")
finally:
print("君のことが大好きです")
"""
s[0] s[233]
123 123
456 何もない
君のことが大好きです 君のことが大好きです
"""
finally不管会不会触发异常都会被执行,
s = [1]
try:
print(123)
s[233]
print(456)
finally:
print("君のことが大好きです")
print("拉拉")
这个是说当只有try, finally 没有except时,try触发异常,再执行完成fIanlly,然后整个程序终端。这里当异常时,只打印123和君のことが大好きです, 后面的拉拉不打印
4 异常可以带参数 py3和py2 写法不同, 下面肯定是py3
def temp_convert(var):
try:
return int(var)
except ValueError as Argument:
print("参数没有包含数字\n", Argument)
# 调用函数
temp_convert("xyz")
"""
参数没有包含数字
invalid literal for int() with base 10: 'xyz'
"""
5 不要再try里用return
18 被坑过的open函数, 及其read(), readline(), readlines()
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
read 读取整个文件
with open('a.txt', 'rb') as f:
data = f.read()
print(data.decode()) # 要转码
# print(data.decode("utf-8")) 不加参数一样
readline 只读取一行文件
with open('a.txt', 'rb') as f:
for i in range(3):
data = f.readline()
print(data.decode('utf-8'))
# f.readline(3) 限制字节数, 3个字节一个汉字
readlines 读取每一行文件,返回一个列表
with open('a.txt', 'rb') as f:
data = f.readlines()
for i in data:
print(i.decode('utf-8'))
19 一看就会一用就忘的format
3.1415926 | {:.2f} | 3.14 | 保留小数点后两位 |
3.1415926 | {:+.2f} | +3.14 | 带符号保留小数点后两位 |
-1 | {:+.2f} | -1.00 | 带符号保留小数点后两位 |
2.71828 | {:.0f} | 3 | 不带小数 |
5 | {:0>2d} | 05 | 数字补零 (填充左边, 宽度为2) |
5 | {:x<4d} | 5xxx | 数字补x (填充右边, 宽度为4) |
10 | {:x<4d} | 10xx | 数字补x (填充右边, 宽度为4) |
1000000 | {:,} | 1,000,000 | 以逗号分隔的数字格式 |
0.25 | {:.2%} | 25.00% | 百分比格式 |
1000000000 | {:.2e} | 1.00e+09 | 指数记法 |
13 | {:>10d} | 13 | 右对齐 (默认, 宽度为10) |
13 | {:<10d} | 13 | 左对齐 (宽度为10) |
13 | {:^10d} | 13 | 中间对齐 (宽度为10) |
^, <, > 分别是居中、左对齐、右对齐,后面带宽度, : 号后面带填充的字符,只能是一个字符,不指定则默认是用空格填充。
+ 表示在正数前显示 +,负数前显示 -; (空格)表示在正数前加空格
b、d、o、x 分别是二进制、十进制、八进制、十六进制
print("{}{}".format(xx, xx))
三 垃圾的装饰器和@wraps(要引入),@property,@classmethod,@staticmethod
1. 原理实现
# 被装饰的函数
def func():
time.sleep(0.1)
print('ojbk')
# 装饰函数
def timmer(func):
def inner():
a = time.time()
func()
# 执行传入的函数
b = time.time()
print(a-b)
return inner
func = timmer(func)
# 给timmer函数传入func函数
func()
2. 带语法糖, 模板 (Foo变成了inner)
def wrapper(func):
def inner(*args, **kwargs):
"""装饰之前能干的事"""
ret = func(*args, **kwargs)
"""装饰之后能干的事"""
return ret
return inner
@wrapper # 这就是语法糖
def Foo(a, b):
print(a+b)
Foo(a=1, b=2)
print(Foo.__name__)
>>> inner
Foo函数实际变成了inner函数
3. 装饰器修复技术,@wrapper
from functools import wraps
def wrapper(func):
@wraps(func)
def inner(*args, **kwargs):
"""装饰之前能干的事"""
ret = func(*args, **kwargs)
"""装饰之后能干的事"""
return ret
return inner
@wrapper # 这就是语法糖
def Foo(a, b):
print(a+b)
# Foo(a=1, b=2)
print(Foo.__name__)
>>> Foo
外部导入, 注意@wraps(func) 要传参数, 不然报错
4. 多个装饰器修饰一个函数
@wrapper2
@wrapper1
def f():
pass
f()
@wrapper2 修饰之前做的事
@wrapper1 修饰之前做的事
f()
@wrapper1 修饰之后做的事
@wrapper2 修饰之后做的事
5. 三层装饰器
实现功能: 可以给多个函数使用一个装饰器,通过外部的全部变量flag, 来控制装饰器是运行x
flag = False
def out_timmer(flag):
def timmer(func):
def inner(*args, **kwargs):
if flag:
a = time.time()
ret = func(*args, **kwargs)
b = time.time()
print(a-b)
return ret
else:
ret = func(*args, **kwargs)
return ret
return inner
return timmer
@out_timmer(flag)
def Foo():
time.sleep(0.1)
print('ojbk')
Foo()
6.@property
class A(object):
def __init__(self, name):
self.name = name
@property
def get_name(self):
return self.name + 'abc'
@get_name.setter # 三个get_name必须同名
def get_name(self, new_name): # 只能多传一个参数
self.name = new_name # 还会接着执行上面的函数, 在原字符串上加abc
@get_name.deleter
def get_name(self):
del self.name
k = A('aki')
print(k.get_name) # 有了这个装饰器,可以直接k.get_name,而不是k.get_name()
# akiabc
k.get_name = 'desky'
# AttributeError: can't set attribute
print(k.get_name)
# deskyabc
有了@property可不加括号的调用,但是问题来了,没括号不能加参数,用了这个装饰器不能随意更改删除属性,所以借助了setter和deleter, 注意函数名必须一致。如果没有后面的同名函数,当试图更改属性时就会AttributeError: can't set attribut。
7.@classmethod
class A(object):
__discount = 0.8
def __init__(self, kind, price):
self.kind = kind
self.__price = price
@property
def price(self):
return self.__price * A.__discount
@classmethod # 把一个方法, 变成类的方法, 直接可以被类调用, 而不是先实例化,再用实例调用
def change_discount(cls, new_dis): # 当方法操作只涉及静态属性的时候,就是外面定义的discount, 就该用classmethod
cls.__discount = new_dis
apple = A('リンゴ', 5)
print(apple.price)
A.change_discount(0.5)
print(apple.price)
@classmethod可以把一个方法,变成类的方法, 直接用类调用方法, 而不用实例化,在调用。但方法操作只涉及静态属性的时候,类似外面的__dicount,就应该用classmethod
8. @staticmethod
class Login(object):
def __init__(self, name, password):
self.name = name
self.password = password
def login(self): pass
@staticmethod
# 静态方法, 在完全面向对象的程序中,如果一个函数 既和对象对象没关系, 也和类没关系, 就用它变成静态方法
def get_user_pwd():
user = input('用户名')
pwd = input('密码')
Login(user, pwd)
Login.get_user_pwd()
@staticmethod 在完全面向对象的程序中,例如这里的登陆模块,在登陆前需要获取用户名和密码,但是这个获取用户名的函数和登陆没关系,但是又想给它放在类中,就可以使用@staticmethod, 注意这个get_user_pwd和类没什么关系,所以也不用传self。
9 @classmethod和@staticmethod (mmp)
class Foo(object):
"""类三种方法语法形式"""
# 俺叫实例方法
def instance_method(self):
print("是类{}的实例方法,只能被实例对象调用".format(Foo))
# 俺叫静态方法,不需要传参数
@staticmethod
def static_method():
print("是静态方法")
# わたしの名前は类方法, 可以传参数,一般叫cls
@classmethod
def class_method(cls):
print("是类方法")
foo = Foo()
foo.instance_method()
foo.static_method()
foo.class_method()
print('----------------')
Foo.static_method()
Foo.class_method()
类的实例化方法, 可以调用实例方法,类方法, 静态方法。类只可以调用类方法和静态方法,Foo.static_method()Foo.class_method()
class Foo(object):
X = 1
Y = 2
@staticmethod
def averag(*mixes):
return sum(mixes) / len(mixes)
@staticmethod
def static_method():
return Foo.averag(Foo.X, Foo.Y)
@classmethod
def class_method(cls):
return cls.averag(cls.X, cls.Y)
foo = Foo()
print(foo.static_method())
print(foo.class_method())
这个例子说明静态方法可以调用静态方法, 类方法也可以调用静态方法。
平気だ平気だ
class Foo(object):
X = 1
Y = 2
@staticmethod
def averag(*mixes):
return sum(mixes) / len(mixes)
@staticmethod
def static_method():
return Foo.averag(Foo.X, Foo.Y)
@classmethod
def class_method(cls):
return cls.averag(cls.X, cls.Y)
class Son(Foo):
X = 3
Y = 5
@staticmethod
def averag(*mixes):
return sum(mixes) / 3
p = Son()
print(p.static_method())
print(p.class_method())
# 1.5
# 2.6666666666666665
这个例子说明它们的不同,继承类中的区别 从下面代码可以看出,如果子类继承父类的方法,子类覆盖了父类的静态方法,
子类的实例继承了父类的static_method静态方法,调用该方法,还是调用的父类的方法和类属性。
子类的实例继承了父类的class_method类方法,调用该方法,调用的是子类的方法和子类的类属性。
通俗的说就是,这种继承关系,子类的实例调用父类的static_method,static_method还是用父类它自己的东西,
而,子类的实例调用父类的class_method,class_method用的是子类的东西
在看来类方法和静态方法,就是调用的区别。。。。
10 类装饰器
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print('class decorator runing')
self._func()
print('class decorator ending')
@Foo
def bar():
print('bar')
bar()
四. 驻留 python内存管理机制 1)引用计数,2)垃圾回收 3)内存池
这里只说说内存池,的驻留机制, 而只有数字和字符串类型有驻留机制
>>> l1 = [1, 2, 3]
>>> l2 = [1, 2, 3]
>>> l2 is l1
False
>>> a = 'qwe'
>>> b = 'qwe'
>>> a is b
True
>>> a = 1
>>> b = 1
>>> a is b
True
这里说明,只有数字和字符串有这种机制,这种机制是有限制的
- 对于字符串, 这能包含[a-z][A-Z][1-9]和 下划线
-
对于数字乘数超过21就不会这种机制
-
s1 = 'a'*21
s2 = 'a'*21
s1 is s2
False
五 .一些注意
a = [1, 2, 3]
b = [2, 3, 1]
a = b
False