标签:属性绑定 面向过程 重用 lse case star 开始 超过 形参与实参
1. 先启动文本编辑器
原则:先定义后引用
name = ‘Stephane‘ # 定义
print(name) # 引用
Python的值传递都是引用传递(内存地址的传递)
内存管理:垃圾回收机制
垃圾:当一个变量值被绑定的变量名的个数为0,该变量值无法被访问到,称之为垃圾
引用数:变量绑定变量名的个数
x = 10 # 10的引用次数为1
y = x # 10的引用次数为2
z = y # 10的引用次数为3
del解除绑定
2. 变量名指向另一个变量
x = 10 # 10的引用次数为1
y = x # 10的引用次数为2
z = y # 10的引用次数为3
del x # 10的引用次数为2
del y # 10的引用次数为1
直接引用与间接引用:
变量组成部分:
赋值符号: = 将变量值的内存地址绑定给变量名
变量值:代表记录的事物的状态
变量命名:
命名基础
原则: 变量名应该见名知意
不能用数字开头
ps:尽量不要用拼音,不要用中文,在见名知意的情况下,尽可能短
命名风格
纯小写加下划线的方式
驼峰体
变量值的三个重要特征
id:反映的是变量值的内存地址(id不是地址),内存地址不同,则id不同
name = stephane
print(id(name))
2. type:不同类型的值表示记录不同的状态
name = stephane
print(type(name))
3. value:值本身
name = stephane
print(name)
5. is 与 ==
is:比较左右两值身份id是否相等
一般来说,两个值相等的整数变量id相同([-5, 256]),因为python解释器在启动之时就会在内存中事先申请一部分空间放入一系列常用的整数,被称为小整数池,事实上其他变量也有类似优化
==:比较左右两个值是否相等
数字类型
int 整型
作用:记录年龄、号码、个数等的整数变量
定义:变量名 = 整数
age = 18
print(type(age)) # int
float 浮点型
作用:记录小数、身高体重等的小数变量
定义:变量名 = 小数
height = 1.78
print(type(height)) # float
数字类型的其他使用
字符串类型
作用:记录描述性质的状态、名字、一段话等
定义:用(‘ ‘," ",‘‘‘ ‘‘‘,""" """)包含的一串字符
str1 = ‘19‘
str2 = "Hi"
str3 = ‘Do you know "Python"?‘
str4 = "I‘m a engineer!"
str5 = ‘I\‘m a engineer‘
# str6和str7赋值的字符串可换行,输出形式包括换行
str6 = ‘‘‘Hello world‘‘‘
str7 = """Hello
everyone!"""
其他使用:
列表类型:索引对应值,从0开始
作用:记录多个值,并且按照索引取指定位置的值
定义:在中括号内用逗号分隔开多个任意类型值的元素,一个值称为一个元素
list1 = [0, 0, 2, 6]
print(list1[3]) # 2
其他使用:
在内存中的存储方式:
字典类型:key对应值,其中key通常为字符串类型,key对值有描述性的功能
作用:存多个值,每个值都有唯一一个key与之对应,key对值有描述作用
定义:在{}中由逗号分开为多个key: value,key必须是不可变类型,value可以是任意类型
dict1 = {"Name": "Stephane", "Age": 19, "height": 178}
print(dict["Name"]) # Stephane
其他使用:
布尔类型
作用:用来记录真假两种状态
定义:True False
a = True
b = False
其他使用:用来当作判断条件
可变不可变类型
定义:垃圾回收机制(GC)是Python解释器的一种机制,专门用来回收不可用的变量值所占用的空间
引用计数
x = 10
print(x) # 10
l = [10, x] # 间接引用
dict1 = {"age": x} # 间接引用
z = x # 直接引用
# id相同
print(id(x)) # 直接引用
print(id(l[1]))
标记清除
分代回收
背景:每次回收内存,都需要把所有对象的引用计数都遍历一遍,浪费时间,于是引入分代回收,采用“空间换时间”的策略
分代回收的核心思想:在历经多次扫描的情况下,都没有被回收的变量,GC机制认为其比较常用,于是降低对此类变量的扫描频率,依据权重,将其分为多个代,权重越高,扫描频率越低
接受用户输入
字符串格式化输出
print()
%
name = ‘Stephane‘
age = 18
print(‘My name is %s, I am %d‘ % (name, age))
print(‘My name is %(name)s, I am %(age)d‘ % {‘name‘: ‘Ste‘, ‘age‘: 18}) # 字典形式
(重要)str.format()(python2.6及之后可用)
用来进行字符串格式化的函数(非输出函数)
在()中写变量名,在str中变量名由{}代替{}中数字代表变量序号
可以用字典的键值对传值
在str中可用一些格式使某个特定字符对字符串进行填充
若变量之前已赋值有变量值,则可以使用索引进行标记,否则需要使用变量名进行标记
# 使用索引标记
name = ‘Stephane‘
age = 18
print("My name is {0}, I am {1}".format(name, age))
# 使用变量名标记
print("Today is {week}.".format(week=‘Mon‘))
最后的数字表示字符串总长度(若无附加字符时,就已超过总长度,则不会出现附加字符)
变量后可跟:.精度+f,表示精确到某位小数
f‘ ‘(python3.5及以后可用但效率低)
f‘格式化字符串‘
变量由{}括起
{}内的字符串可以被当作表达式运行
name = ‘Stephane‘
age = 18
print("My name is {0}, I am {0}".format(name, age))
res = ("My age is {age}, I am {name}".format(name=‘ZHS‘, age=18))
print(res)
print(‘{x:=<10}‘.format(x=‘Start‘)) # Start=====
print(‘{x:=^10}‘.format(x=‘Start‘)) # ==Start===
print(‘{x:.3f}‘.format(x=1020.363520)) # 1020.364
name = ‘Stephane‘
age = 18
print(f‘My name is {name}, and I am {age} years old.‘)
print(f‘10+3‘) # 10+3
print(f‘{10+3}‘) # 13
f"{print(‘abcd‘)}" # abcd
?
算术运算符
赋值运算符
=
增量赋值
链式赋值
交叉赋值(交换变量值)
a = 10
b = 20
a, b = b, a
print(a, b) # 20 10
解压赋值(若解压字典,默认解压key)
list1 = [1, 2, 3, 4]
a, b, c, d = list1
print(a, b, c, d) # 1 2 3 4
print(list1) # [1, 2, 3, 4]
list1 = [1, 2, 3, 4]
a, b, c, *_ = list1
print(a, b, c) # 1 2 3
print(_) # [4]
比较运算符
逻辑运算符:用来连接多个条件加以判断的运算符
成员运算符:
身份运算符:is
什么是if判断:判断条件是否成立的语句
为什么要有if判断:增强程序的流程多样性
怎么用:
"""
语法1:
if 条件:
语句1
语句2
...
语法2:
if 条件:
语句1
...
else
语句1
...
语法3:
if 条件1:
语句1
...
elif 条件2:
语句1
...
...
else:
语句1
...
"""
while循环的语法与基本使用
"""
while 条件:
代码1
代码2
...
"""
死循环与效率问题
循环的应用
退出循环的两种方式
while循环嵌套
"""
while 条件1:
语句1
...
while 条件2:
语句2
...
或
while 条件1:
语句1
...
while 条件2:
语句2
...
break
break
"""
while + continue:结束本次循环
while + else
"""
while 条件:
语句1
...
else:
语句2
...
"""
for循环的语法与基本使用
什么是for循环:for循环shipython提供的第二种循环机制
为什么要用for循环:
如何用for循环:
"""
for 变量名 in 可迭代对象:
# 可迭代对象可以是list,string,dictionary,set,tuple
语句1
语句2
...
"""
for循环应用
for循环嵌套
"""
for 变量名1 in 可迭代对象1:
语句1
for 变量名2 in 可迭代对象2:
语句2
...
"""
for + break
for + continue
for + else
while循环与for循环的异同:
导入copy模块
使用函数copy.deepcopy()
在两个变量中对于任意不可变类型变量,指向的地址都相同,对于任意可变类型,指向地址都不同
![](引用图像\批注 2020-07-30 000912.png)
作用
定义:
类型转换:str() 把任意其他类型转换成字符串
使用:
优先掌握
按索引取值
# 正向取
str = "Hello"
print(str[1]) # e
# 反向取
print(str[-1]) # o
# 只能取
str[1] = ‘a‘ # 错误,字符串不可修改
切片(从一个大的字符串中拷贝出一个子字符串)
# 左闭右开区间
str = "HelloWorld"
print(str[0:3]) # Hel
# 步长
print(str[0:5:2]) # Hlo (start <= stop)
# 反向步长
print(str[5:0:-2]) # Wle (start >= stop)
长度(len)
str = "Hello"
print(len(str)) # 5
成员运算符 in 和 not in
# 判断一个子字符串是否在大字符串中
str = "hello"
print(‘h‘ in str) # True
print(‘a‘ not in str) # True
移除空白 strip
# strip只去两边,不去中间
str = " hello World "
print(str.strip()) # "hello world"
print(str.lstrip()) # "hello world "
print(str.rstrip()) # " hello world"
str1 = "********hello********"
print(str1.strip(‘*‘)) # "hello"
切分split
# 把一个字符串按照某种分隔符(默认为空格)进行切分,得到一个列表(不包括分隔符)
# split(sep, maxsplit)
str = "a:b:c:d"
list1 = str.split(‘:‘)
print(list1) # [‘a‘, ‘b‘, ‘c‘, ‘d‘]
list1 = str.split(‘:‘, 2)
print(list1) # [‘a‘, ‘b‘, ‘c:d‘]
循环
# for循环
需要掌握
lower,upper
str = "abCdEF"
print(str.lower()) # abcdef
print(str.upper()) # ABCDEF
startswith, endswith
str = "abcdefg"
print(str.startswith("abc")) # True
print(str.endswith("efg")) # True
split, rsplit
str = "a:b:c:d"
list1 = str.split(‘:‘)
print(list1) # [‘a‘, ‘b‘, ‘c‘, ‘d‘]
list1 = str.rsplit(‘:‘)
print(list1) # [‘a‘, ‘b‘, ‘c‘, ‘d‘]
list1 = str.rsplit(‘:‘, 2)
print(list1) # [‘a:b‘, ‘c‘, ‘d‘]
join
# 将列表拼接成字符串
# 列表中每个元素为字符(串)
list1 = [‘a‘, ‘b‘, ‘c‘, ‘d‘]
str1 = ‘:‘.join(list1) # a:b:c:
replace
# replace(old, new, count)
str1 = "Hello World"
print(str1.replace("H", "U")) # "Uello World"
isdigit
# 判断字符串是否由纯数字组成
str1 = "123123123123"
str2 = "1a2b3c"
print(str1.isdigit()) # True
print(str2.isdigit()) # False
了解
find, rfind
str1 = "Stephane"
# find和rfind若找不到会返回-1
print(str1.find(‘e‘)) # 2 返回查找字符串在大字符串中的起始索引
print(str1.rfind(‘e‘)) # 7 返回从右查找字符串在大字符串中的起始索引
index, rindex
str1 = "Stephane"
# index和rindex若找不到会直接报错
print(str1.find(‘e‘)) # 2 返回查找字符串在大字符串中的起始索引
print(str1.rfind(‘e‘)) # 7 返回从右查找字符串在大字符串中的起始索引
count
str1 = "Ada is Ada"
print(str1.count("Ada")) # 2 返回目标字符串出现的次数
center
# center(wideth, fullchar) 使字符串居中,并用填充字符填充至指定长度
str1 = "hello"
print(str1.center(11, ‘*‘)) # ***hello***
ljust
# ljust(wideth, fullchar)使字符串左对齐,并用填充字符填充至指定长度
str1 = "hello"
print(str1.ljust(11, ‘*‘)) # hello******
rjust
# ljust(wideth, fullchar)使字符串右对齐,并用填充字符填充至指定长度
str1 = "hello"
print(str1.rjust(11, ‘*‘)) # ******hello
zfill
# zfill(wideth)使字符串右对齐,并用0填充至指定长度
str1 = "hello"
print(str1.rjust(11, ‘*‘)) # 000000hello
expandtabs
# expandtabs(tabsize)若字符串中有制表符,指定制表符所包含的空格数
str1 = "hello\tworld"
print(str1.expandtabs(2)) # hello world
capatalize
# capatalize()使字符串首字母大写
swapcase
# swapcase()使字符串中的大小写反转
title
# title()使每个单词首字母大写
is其他
# islower()
# isupper()
# isspace()
# istitle()
# isalnum()
# isalpha()
# isidentifier() 判断是否为合法标识符
# isdecimal()
# isnumberic() 可判断数字、中文数字、罗马数字等
作用:按位置存多个值
定义:list1 = [ ]
类型转换:list()可以转换能被for循环遍历的类型
使用
优先掌握
按索引存取值
list1 = [111, 222, 333, 444]
# 正向取
list1[1]
# 反向取
list1[-1]
# 可修改
list1[0] = 100
list1[4] = 555 # 索引不存在,报错
切片
list1 = [11, 20, 36, 33, 22, 10]
print(list1[0:5:2]) # [11, 36, 22]
print(list1[5:0:-2]) # [10, 33, 20]
长度
len()
成员运算
list1 = [111, 222, 333, 444]
print(111 in list1) # True
print(555 not in list1) # True
追加
# append(object)
list1 = [111, 222]
list1.append(333)
list1.append(444)
print(list1) # [111, 222, 333, 444]
插入
# insert(index, object)
list1 = [111, 333]
list1.insert(1, 222)
print(list1) # [111, 222, 333]
列表添加可迭代值的元素
# extend(iterable)
list1 = [111, 222, 333]
list2 = [444, 555]
list1.extend(list2)
print(list1) # [111, 222, 333, 444, 555]
删除
list1 = [111, 222, 333]
del list1[0] # 通用删除方法,没有返回值
print(list1) # [222, 333]
list1 = [111, 222, 333]
# pop(index) 返回值为被删除的元素,不指定索引默认删最后一个元素
x = list1.pop(2)
print(list1) # [111, 222]
print(x) # 333
# remove(object) 无返回值,删除指定元素
list1 = [111, 222, 333]
x = list1.remove(111)
print(list1) # [222, 333]
print(x) # None
循环
list1 = [111, 222, 333]
for item in list1:
需要掌握
count()
list1 = [10, 100, 10, 10, 1000]
print(list1.count(10)) # 3
index()
list1 = [101, 1002, 103, 104, 10005]
print(list1.index(103)) # 2,从左开始查找
clear()
# clear() 将整个列表的元素删除,留下空列表
list1 = [101, 1002, 103, 104, 10005]
list1.clear()
print(list1) # []
reverse()
# reverse() 将列表元素倒置
list1 = [101, 1002, 103, 104, 10005]
list1.reverse()
print(list1) # [10005, 104, 103, 1002, 101]
sort()
# sort(key, reverse) # 默认升序,当reverse为真,为降序
list1 = [101, 1002, 103, 104, 10005]
list1.sort()
print(list1) # [101, 103, 104, 1002, 10005]
list1 = [100, ‘a‘, 1000]
list1.sort() # 不同类型混合,不可排序
了解
列表比大小,原理同字符串比大小
# 对相应位置的元素为同种类型
list1 = [‘aaa‘, 123, 100]
list2 = [‘a‘, 100, 1000]
print(list2 < list1) # True
补充(列表模拟)
队列
list1 = []
# 入队操作
list1.append(100)
list1.append(200)
list1.append(300)
print(list1)
# 出队操作,先进先出
print(list1.pop(0))
print(list1.pop(0))
print(list1.pop(0))
堆栈
list1 = []
# 入队操作
list1.append(100)
list1.append(200)
list1.append(300)
print(list1)
# 出队操作,后进先出
print(list1.pop())
print(list1.pop())
print(list1.pop())
作用:按照索引存放多个值,只能读,不能写
定义:tuple1 = () ()内用逗号分割开多个任意类型的元素
元组中的索引值也是指向元素内存地址
单独一个括号,只是包含,并不是元组
tuple1 = (100) # int
tuple1 = (100,) # tuple
类型转换:tuple()
使用
作用
定义:dict1 = {},value可以是任意类型, key只能是不可变类型
# 可使用这种方式进行定义/类型转换
dict1 = dict(x=1, y=2, z=3)
print(d) # {‘x‘: 1, ‘y‘: 2, ‘z‘: 3}
print(type(d)) # dict
# 也可使用这种方式创建字典(快速初始化),但value为相同值
Ages = [‘a‘, ‘b‘, ‘c‘]
dict1 = {}.fromkeys(Ages, 19)
print(dict1)
类型转换:dict()
# 可以使用这种方法
info = [
[‘11‘, ‘22‘],
[‘33‘, ‘44‘],
[‘55‘, ‘66‘]
]
info = dict(info)
print(info)
使用
优先掌握
按key存取值
dict1 = {‘a‘: 100, ‘b‘: 200}
print(dict1[‘a‘]) # 100
dict1[‘a‘] = 150 # 赋值
dict1[‘c‘] = 300 # 创建key
print(dict1) # {‘a‘: 150, ‘b‘: 200, ‘c‘: 300}
# 创建字典时,若一个key出现若干次,则以最后一次出现的value为准
长度len
# 若有若干个key相同,则该key只统计一次
dict1 = {‘a‘: 100, ‘b‘: 200, ‘a‘: 150, ‘b‘: 250}
print(len(dict1)) # 2
成员运算
删除
# del
dict1 = {‘a‘: 100, ‘b‘: 200}
del dict1[‘a‘]
print(dict1) # {‘b‘: 200}
# pop() 根据key删除
dict1 = {‘a‘: 100, ‘b‘: 200}
l = dict1.pop(‘a‘)
print(dict1) # {‘b‘: 200}
print(l) # 100 pop()返回删除的键值对的值
# popitem() 随机删除
dict1 = {‘a‘: 100, ‘b‘: 200}
l = dict1.pop(‘a‘)
print(dict1)
print(l) # popitem()返回一个元组,包含删除的key和value
keys()、values()、items()
# keys() 返回字典中所有的key组成的列表
# values() 返回字典中所有的value组成的列表
# items() 返回所有键值对组成的列表(每个键值对分别为子元组)
fromkeys()
# fromkeys([key1, key2], value) 给字典中添加可迭代列表中的所有key,value均为fromkeys的第二个参数值,返回值为包含相应键值对的字典
# 但子列表中所有的key对应的value始终指向同一块地址
dict1 = {}
dict2 = dict1.fromkeys([‘a‘, ‘b‘], 100)
print(dict1) # {}
print(dict2) # {‘a‘: 100, ‘b‘: 100}
循环
dict1 = {}
for key in dict1.keys():
...
for value in dict1.values():
...
for key, value in dict1.items():
...
需要掌握
clear()
update()
dict1 = {‘a‘: 100, ‘b‘: 200}
dict2 = {"c": 100}
dict2.update(dict1)
print(dict2) # {‘c‘: 100, ‘a‘: 100, ‘b‘: 200}
# 旧字典当中存在的key会被更新,旧字典当中不存在的key会由新字典追加
setdefault()
# setdefault(key, defalut) defalut为默认的值,返回值为字典中该键对应的值
dict1 = {‘a‘: 100, ‘b‘: 200}
dict1.setdefault(‘b‘: 10000) # 若原字典中有该键,则忽略其中的默认值,即不进行修改
dict1.setdefault(‘c‘: 10000) # 若原字典中没有该键,则将该键添加到字典各种,并将默认值作为其值
print(dict1) # {‘a‘: 100, ‘b‘: 200, ‘c‘: 10000}
get() 容错性好
# get(key) 返回key对应的value,若key不存在会返回None
dict1 = {‘a‘: 100, ‘b‘: 200}
print(dict1.get(‘a‘)) # 100
print(dict1.get(‘c‘)) # None
作用
定义 s = set(...), s = set() 定义空集合
在{}内用","分隔多个元素
每个元素必须是不可变类型
集合内没有重复的元素
集合内元素无序
类型转换
使用
优先掌握
intersection(s) ∩
union(s) ∪
difference(s) -
symmetric_difference(s) ^
issuperset(s) >=
issubset(s) <=
discard()
# discard(args)
# 删除指定元素,若不存在该元素,do nothing
s = {100, 200, 300}
s.discard(100)
print(s) # {200, 300}
s.discard(400)
print(s) # {200, 300}
remove()
# remove(args)
# 删除指定元素,若不存在该元素,则会报错
s = {100, 200, 300}
s.discard(100)
print(s) # {200, 300}
s.discard(400)
print(s) # 报错
update()
# update(s) 将新集合中的元素加入旧集合,并去重
s = {100, 200, 300}
s1 = {150, 200, 300, 450}
s.update(s1)
print(s) # {100, 200, 300, 150, 450}
需要掌握
isdisjoint()
# isdisjoint(s) 若两个集合交集为空,返回True,否则返回False
s = {100, 200, 300}
s1 = {150, 200, 300, 450}
print(s.isdisjoint(s1)) # False
s1 = {450, 150}
print(s.isdisjoint(s1)) # True
pop()
clear()
add()
人类与计算进行交互时,使用人类字符,而计算机只识别二进制数,因此字符必须翻译为二进制数
翻译的过程中必须参照特定标准,称之为字符编码表,存放字符与二进制数的映射关系
unicode(内存使用的固定编码)
兼容万国字符
与万国字符编码都有对应关系
采用16位二进制数对应一个中文字符串(个别生僻使用32、64位二进制数)
老的字符编码都可以转化为unicode,但不能通过unicode互相转化
![](引用图像/批注 2020-08-24 000408.png)
utf-8(unicode translate format-8) 可以作为字符编码格式直接存入硬盘
x = ‘上‘
x = x.encode(‘gbk‘) # 将unicode转为gbk格式
print(x, type(x)) # b‘\xc9\xcf‘ <class ‘bytes‘>
x = b‘\xc9\xcf‘
x = x.decode(‘gbk‘) # 按什么方式编码,则按什么方式解码
print(x, type(x)) # 上 <class ‘str‘>
编码格式应设置成支持文件内容字符的格式
以什么编码格式存的就必须以什么编码格式取
指定文件头,修改默认的编码,即存入硬盘时的编码格式
在文件首行写
# coding: xxx 如gbk,utf-8
python3默认为:utf-8
python2默认为:ASCII
python3的str类型默认直接存成unicode格式,不会乱码
保证python2的str类型不乱码,需强制存为unicode格式
str = u‘你好‘
python2有两种字符串类型
str1 = ‘你好‘ ----> str # 字符串值会按照文件头指定的编码格式存入变量值的内存空间
str2 = u‘你好‘ ----> unicode
设置文件头模板
File ---- Settings ---- File and Code Templates ---- Python Script
![](引用图像/批注 2020-09-01 125649.png)
打开文件
with:上下文管理
# 在with的子代码运行结束后,自动执行f.close()
with open(‘a.txt‘) as f:
res = f.read()
print(res)
# 可打开多个文件
with open(‘a.txt‘) as f, open(‘b.txt‘) as f1:
res = f.read()
print(res)
res1 = f1.read()
print(res1)
r 只读模式(默认操作模式)
一次性将文件所有内容读入内存中
w 只写模式
a 只追加写模式
+ 可读可写模式
x 只写模式
t和b不能单独使用,必须与a/w/r连用
t 文本(默认模式)
读写都以str(unicode)为单位
只针对文本文件
必须指定字符编码( encoding=‘xxx‘)
b 二进制
针对所有文件
读写都以bytes为单位
不能指定字符编码
bytes类型转换 byte(‘str‘, encoding)
将文件内容存入变量,并进行修改,最后再写入原文件,但是容易造成内存溢出
with open(r‘third.txt‘, mode=‘r+t‘, encoding=‘utf-8‘) as f:
for line in f:
line = line.replace(‘男‘, ‘Male‘)
line = line.replace(‘女‘, ‘Female‘)
print(line.strip())
打开一个新文件,将原文件每一行的新的修改都写入新文件中但硬盘的占用率多了一份
with open(r‘third.txt‘, mode=‘r+t‘, encoding=‘utf-8‘) as f, open(r‘third.txt.swap‘, mode=‘wt‘, encoding=‘utf-8‘) as g:
for line in f:
g.write(line.replace(‘1‘, ‘0‘))
移除原文件,并将新文件改为原文件的文件名
import os
os.remove(r‘third.txt‘)
os.rename(r‘third.txt.sap‘, r‘third.txt‘)
什么是函数
为什么要有函数
如何使用函数
定义函数的语法
def 函数名(参数1, 参数2...):
"""文档描述"""
函数体
return 值
定义的过程
# 无参函数
def func():
print(‘Hi‘)
# 有参函数
def func(x, y):
print(x, y)
func(1, 2) # x = 1, y = 2
# 空函数,函数体为pass
def func():
pass
func()
# 语句形式(只调用函数)
def func():
print(‘Hi‘)
func()
# 表达式形式
def func(x, y):
z = x + y
return z
sum = func(1, 2) # x = 1, y = 2
# 参数形式
def func(x, y):
z = x + y
return z
print(func(1, 2))
# 返回None,即函数体内没有return或返回空值
def func():
return
print(func())
# 返回一个值
def func(x, y):
z = x + y
return z
print(func(1, 2))
# 返回多个值:用‘,‘分隔开,解释器会将他们存入同一个元组中
def func(x, y):
z = x + y
return x, y, z
total = func(1, 2)
print(total, type(total)) # (1, 2, 3)
Python是解释型的强类型动态语言
Python对于函数的参数没有硬性要求,但在3.5之后,引入了类型提示功能
def func(name: str, age: int) -> int: # 参数后+":"+类型 表示类型提示功能
# name为字符串类型, age为整型, 函数返回值为整型
print(name)
print(age)
return 1
*形参名:接受溢出的位置实参,溢出的位置实参会被*保存成元组格式,将元组赋值给紧跟*的形参名(通常为*args)
*实参名:将*后紧跟的值拆分(打散)成位置实参
def func(x, y, *z)
print(x, y, z)
func(1, 2, 3, 4, 5) # 1 2 (3, 4, 5)
def func_1(x, y, z):
print(x, y, z)
func_1(*[1, 2], 3) # 1 2 (3,)
**形参名:接受溢出的关键字实参,溢出的关键字实参会被**保存成字典格式,将字赋值给紧跟*的形参名(通常为**kwargs)
**实参名:将**后紧跟的值(只能是字典)拆分成关键字实参
混用*与**:"*args" 必须在 "**kwargs" 之前
def func(x, y, **z)
print(x, y, z)
func(1, 2, fi=10, se=100, thd=1000) # 1 2 {‘fi‘: 10, ‘se‘: 100, ‘th‘: 1000}
def func1(x, y, z):
print(x, y, z)
func1(1, **{‘y‘: 2, ‘z‘: 3}) # 1 2 3
在定义函数时,*后定义的参数(不是"*"后紧跟的参数)称为命名关键字参数
def func(x, y, *, z): # z 为命名关键字参数
print(x, y, z)
func(1, 2, z=3) # 1 2 3
def func1(a, b, *args, c=1000, d): # 不局限于只能用"*",也可用"*args"(只要有*都算)
print(a, b, c, d)
func1(1, 2, d=2000) # 1 2 1000 2000
形参定义顺序:
def func(x, y=111, *args, z, **kwargs):
...
实参调用顺序:
名称空间:存放名字的空间,即令栈区分为不同的块,每一块为不同的名称空间,拥有了名称空间,就可以在栈区存放相同的名字
![](引用图像/批注 2020-09-11 170446.png)
作用域:不同名称空间作用的范围不同,即作用域不同
名称空间的嵌套关系以函数定义定义为准,与函数调用阶段无关
x = 10
def func1():
print(x)
def func2():
x = 100
func1()
func2() # 10
函数嵌套时的作用域:LEGB
查找优先级:根据名称空间的嵌套关系,查找规则同上
global:用于在局部名称空间声明该变量为全局作用域变量
nonlocal:用于修改函数外层函数包含的名字对应的值(不可变类型),直到找到最外层的函数中的该名字对应的变量
x = 10
def f1():
x = 11
def f2():
global x
x = 22
print("f1: ", x)
f2()
print("f2: ", x)
f1() # f1: 22 f2: 11
#------------------------------------
x = 10
def f1():
x = 11
def f2():
nonlocal x
x = 22
print("f1: ", x)
f2()
print("f2: ", x)
f1() # f1: 22 f2: 22
print(x) # 10
def func():
...
f = func
print(f, func) # <function func at 0x016C9FA0> <function func at 0x016C9FA0>
list1 = []
dict1 = {}
def func():
...
list1.append(func)
print(list1[0]) # <function func at 0x03239FA0>
dict1[‘k‘] = func
print(dict1) # {‘k‘: <function func at 0x03239FA0>}
def func(x):
print(x)
def func1():
...
func(func1) # <function func1 at 0x03239FA0>
def func(x):
return x
def func1():
...
print(func(func1)) # <function func1 at 0x010D9FA0>
闭包函数=名称空间与作用域+函数嵌套+函数对象l
什么是闭包函数
为何要有闭包函数
如何定义闭包函数
# 方案一:仍容易产生重复代码
import time
def index(*args, **kwargs): # index为被修饰函数
"""The number of elements in total."""
time.sleep(2)
number_of_argument = 0
for i in args:
print(i)
number_of_argument += 1
for i in kwargs.keys():
print(kwargs[i])
number_of_argument += 1
return number_of_argument
def out(func):
def wrapper(*args, **kwargs): # wrapper为闭包函数
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
wrapper.__name__ = func.__name__ # 将index对象的函数名由wrapper改为index
wrapper.__doc__ = func.__doc__ # 将index对象的说明文档由wrapper的改为index的
... # 各种函数的属性的赋值
return res # 保证与被装饰函数返回值相同
return wrapper
index = out(index) # 将函数结果赋值给与被修饰函数名字相同的对象
result = index(1, 2, 456878.222, k=3, g=666)
print(result)
print(index.__name__)
print(index.__doc__)
# 方案二:重复代码减少
import time
from functools import wraps
def out(func):
@wraps(func) # 将func的属性都赋值给wrapper
def wrapper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return wrapper
@out # 在被装饰对象的单独一行写"@装饰器名字"可以实现"将函数结果赋值给与被修饰函数名字相同的对象",即实现index = out(index)
def index(*args, **kwargs):
"""The number of elements in total."""
time.sleep(2)
number_of_argument = 0
for i in args:
print(i)
number_of_argument += 1
for i in kwargs.keys():
print(kwargs[i])
number_of_argument += 1
return number_of_argument
result = index(1, 2, 456878.222, k=3, g=666)
print(result)
print(index.__name__)
print(index.__doc__)
如果想实现叠加多个装饰器进行修饰
@out1 # out1(wrapper2) = wrapper1 ==> index = wrapper1
@out2 # out2(wrapper3) = wrapper2 ==> index = wrapper2
@out3 # out1(index) = wrapper3 ==> index = wrapper3
# 从下向上加载
# 从上而下执行
def index():
pass
def out(func):
def wrapper(*args, **kwwrgs):
# 1.调用原函数
# 2.添加装饰的新功能
res = func(*args, **kwargs)
return res
return wrapper
由于语法糖@的限制,若外层函数out的参数至少有一个与被修饰对象的参数不匹配,则使用有参数装饰器
模板
def out(x):
def deco(func):
def wrapper(*args, **kwargs):
# 1.调用原函数
# 2.添加装饰的新功能
res = func(*args, **kwargs)
return res
return wrapper
return deco
@out(x) # out(x) ---> return deco ---> func = deco(func)
def func(*args, **kwargs):
pass
只要内置有__iter__方法/iter()的都称为可迭代对象
# 调用可迭代对象下的__iter__方法会将其转换成迭代器对象
s = ‘Hi‘
res = s.__iter__() # 存在
# <str_iterator object at 0x00B0F538>
l = [1, 2]
l.__iter__() # 存在
t = (1, 2)
t.__iter__() # 存在
d = {1: 2, 3:4}
res = d.__iter__() # 存在
print(res.__next__()) # 1
print(res.__next__()) # 3
print(res.__next__()) # 抛出异常,StopInteration
# 遍历,在一个迭代器取值取干净的情况下,再对其取值无法取到
while True:
try:
print(res.__next__())
except StopIteration: # 捕捉异常
break
set = {1, 2, 3}
set.__iter__() # 存在
with open(r‘a.txt‘, mode=‘w‘) as f:
f.__iter__() # 存在
在函数内一旦存在yield,调用函数不会执行函数体代码,函数体返回一个生成器对象
生成器的__next__的方法会触发函数体代码的执行,遇到yield停下,并返回yield的值
yield类似于return,但可保存函数的状态并挂起函数,用于返回多次值
def func():
print("The first time")
yield 1
print("The second time")
yield 2
print("The third time")
yield 3
print("The forth time")
g = func()
res = g.__next__() # The first time
print(res) # 1
res = g.__next__() # The second time
print(res) # 2
res = g.__next__() # The third time
print(res) # 3
res = g.__next__() # The forth time
# 抛出异常(StopInteration)
yield表达式
g.send()(g.send(None)等同于next(g)) ---- 给函数(生成器)中当前挂起位置的yield赋值,第一次传入的值必须为"None"
g.close() ---- 关闭后无法传值
def func():
print("---Begin---")
while True:
x = yield 100
print("x = {}".format(x))
yield 200
g = func()
g.send(None)
res = g.send(5) # x = 5,此时函数挂起在第二个yield
print(res) # 200
res = g.send(10)
print(res) # 100,此时函数挂起在第一个yield
...
应用
可产生若干个元素的迭代器
# 自定义产生若干个元素的迭代器
def func(start, stop, step=1):
print("Start")
while start <= stop:
start += step
yield start-1
start_number = int(input())
stop_number = int(input())
g = func(start_number, stop_number)
while True:
try:
print(next(g))
except StopIteration:
print("End")
break
[a for b in c if d]
# 代码原理同下
for b in c:
if d:
a
(a for b in c if d)
# 例子
g = (i for i in range(10) if i > 8) # g内部一个值也没有
print(next(g)) # 9
print(next(g)) # IterationStop
是函数嵌套调用的一种特殊形式
递归的两个阶段
回溯:一层一层调用
递推:满足某种条件,结束递归调用,一层一层返回
![](引用图像/屏幕截图 2020-10-09 103923.png)
查找数据高效的方式
通过二分法查找数据
List1 = [1, 20, 21, 19, 25, 56, 87, 15, 12, 16, 20, 27, 23, 50]
def dichotomy(num, list1):
try:
print(list1)
middle = list1[len(list1)//2]
if middle > num:
list1 = list1[:len(list1)//2]
dichotomy(num, list1)
elif middle < num:
list1 = list1[len(list1)//2+1:]
dichotomy(num, list1)
else:
print("FIND IT")
except IndexError:
print("No such number")
List1.sort()
number = int(input())
dichotomy(number, List1)
对比使用def关键字创建的有名字的函数,使用lambda关键字创建的规则是没有名字的函数(匿名函数),在定义时就得被调用(由于没有绑定变量名,在运行至下一条语句时,会被回收),执行语句的结果为函数返回值
匿名函数用于临时调用一次的场景:更多的是将其与其他函数配合使用
lambda 参数1, 参数2,...: expression
# 定义
lambda x, y: x+y
# 调用1
total = (lambda x, y: x+y)(1, 2)
print(total) # 3
# 调用2
func = lambda x, y: x+y
total = func(1, 2)
print(total) # 3
应用
可以用于一次性调用的场景
dic = {‘Tom‘: 123123,
‘Step‘: 100000,
‘Pop‘: 124560
}
res = max(dic, key=(lambda x: dic[x]))
print(res) # Pop
filter(函数,可迭代对象)
函数的参数为可迭代对象的每一次的返回值
会判断函数返回的布尔值,若为真,则保留
persons = [‘Step‘, ‘Egon‘, ‘Ada‘]
res = filter(lambda name: name.endswith(‘a‘), persons)
print(next(res)) # Ada
print(next(res)) # StopIteration
map(函数,可迭代对象)
persons = [‘Step‘, ‘Egon‘, ‘Ada‘]
res = map(lambda name: name+‘.M‘, persons)
# res是可迭代对象
print(next(res)) # Step.M
ress = map(lambda name: name.replace(‘.M‘, ‘_M_‘))
print(next(ress)) # Step_M_
sorted(x, key=函数名, reverse) ---- reverse=False(默认从小到大排)
遍历一个可迭代对象,并对其排序,返回一个列表
max(x, key=函数名)
迭代一个可迭代对象,并找出最大值
需要从外部模块导入
from functools import reduce
reduce(函数, 可迭代对象, 初始值) ---- 初始值默认为0
合并数据,前一步合并结果赋值给初始值
不只限于数字类型
res = reduce(lambda x, y: x+y, [10, 20, 30], 10)
print(res) # 70
ress = reduce(lambda x, y: x+y, [‘a‘, ‘b‘, ‘c‘], ‘Hi‘)
print(ress) # Hiabc
函数 | 作用 |
---|---|
abs () | 绝对值 |
all() | 当可迭代对象中所有元素都为True或可迭代对象为空,则返回True |
any() | 当可迭代对象中有一个元素为True则或返回True |
bin() hex() oct() | 略 |
callable() | 判断一个对象可否被调用 |
setattr() | |
getattr() | |
hasattr() | |
delattr() | |
dir() | 查看某个对象有哪些属性 |
divmod(x, y) | 返回x除以y的商和余数 |
set() | 创建集合shib |
frozenset() | 创建不可变集合 |
isinstance(a, b) | 判断对象a是否为类b的实例 |
slice() | 完全等于切片操作 |
zip(x, y) | 返回x和y元素相互对应的元组组成的列表(多出的元素部分不配对) |
__import__() | 将字符串形式的模块名转化为可使用的模块名并导入 |
首次导入模块时会发生:
之后的导入都是引用首次导入产生的命名空间,无论查看还是修改操作,与调用位置无关
引用模块
可以一条语句导入多个模块(不建议)
import 模块1, 模块2...
可以在函数内导入模块,作用域为函数的局部作用域
import语句的优点:不会与当前名称空间中的对象名字冲突
import语句的缺点:加前缀显得麻烦
导入时会发生:
导入多个对象:
__all__ ----导入的对象的名字组成的列表
from ... import语句的优点:不用加前缀,直接使用,代码精简
from ... import语句的缺点:会与当前名称空间中的对象名字冲突
当导入模块的名字复杂或过长等情况,使用as进行名字的重命名
import 模块 as new_name
from 模块 import 对象 as new_name
模块查找优先级
无论是import还是 from...import,再导入模块时都涉及到查找问题
优先级
优先级 | 位置 |
---|---|
1 | 内存 |
2 | 按照sys.path中的文件夹位置逐个查找 |
sys.path中的第一项为当前文件夹,第二项为当前项目文件夹...(列表形式)
环境变量是以执行文件为准的,被导入的模块或者后续其他文件引用的sys.path都是参照执行文件的sys.path
sys.modules可以查看已经加载到内存的模块(字典形式)
以包的文件夹作为起始来导入
仅限于包内使用,不能跨出包。包内模块之间的导入,推荐使用
时间分为三种格式
时间戳:从1970.1.1到现在经过的秒数
格式化显示的时间
可自行设置格式 time.strftime(‘格式‘)
字符 | 含义 |
---|---|
%Y | 年 |
%m | 月 |
%d | 日 |
%H | 时 |
%M | 分 |
%S | 秒 |
%p | AM/PM |
%X | %H:%M:%S |
%a | 星期 |
%b | 月份的英文简写 |
time.asctime() ---- ,不需进行特定格式化,显示格式为‘%a %b %d %H:%M:%S %Y‘
结构化的时可用于展示时间间
时间格式的互相转化
![](引用图像\屏幕截图 2020-11-01 233950.png)
import time
s_time = time.localtime() # 结构化的时间
t_time = time.time() # 时间戳
f_time = time.strftime("%Y-%m-%d %H:%M:%S") # 格式化的时间
print(time.mktime(s_time)) # 结构化时间转化为时间戳s_time
print(time.strftime("%Y-%m-%d %H:%M:%S",s_time)) # 结构化的时间转化为格式化的时间
print(time.strptime(f_time, "%Y-%m-%d %H:%M:%S")) # 格式化的时间转化为结构化的时间
print(time.localtime(t_time)) # 时间戳转化为结构化的时间
print(time.gmtime(t_time)) #时间戳转化为结构化的时间(世界标准时间,0经度处)
用于控制操作系统
os.getcwd() ---- 获取当前目录
os.getsize(‘dirname‘) ---- 获取当前文件夹大小(Bytes)
os.chdir(‘dirname‘) ---- 改变当前脚本目录,相当于cd
os.curdir ---- 返回当前目录字符串名(.)
os.pardir ---- 返回父目录字符串名(..)
os.makedirs(‘dirname/dirname2/...‘) ---- 可生成多层递归目录
os.removedirs(‘dirname‘) ---- 若目录为空,则删除,递归至上级目录,若为空,则删除......
os.mkdir(‘dirname‘) ---- 相当于shell中的mkdir
os.rmdir(‘dirname‘) ---- 删除单级空目录,若目录不为空则无法删除并报错;相当于rmdir
os.listdir(‘dirname‘) ---- 列出指定目录下所有文件和子目录,包括隐藏文件,相当于ls -a
os.remove() ---- 删除一个文件
os.rename(‘oldname‘, newname) ---- 文件重命名
os.stat(‘path/filename‘) ---- 获取文件/目录信息
os.sep ---- 输出操作系统的路径分隔符(\\ /)
os.linesep ---- 输出当前平台使用的行终止符
os.pathsep ---- 输出用于分割文件路径的字符串
os.name ---- 输出字符串指示当前使用平台
os.system("bash command") ---- 运行shell命令,直接显示
os.environ ---- 获取系统环境变量
os.path
高级的文件(夹)、压缩包处理模块
shutil.copyfileobj(fsrc, fdst[, lenght])
将文件内容拷贝到另一个文件中
import shutil
shutil.copyfileobj(open(‘src‘, ‘r‘), open(‘dst‘, ‘w‘))
shutil.copyfile(src, dst)
shutil.copymode(src, dst)
shutil.copystat(src, dst)
shutil.copy(src, dst)
shutil.copy2(src, dst)
shutil.ignore_pattern(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归拷贝文件夹
目标目录不能存在
目标目录的父目录应有写权限
import shutil
shutil.copytree("dir1", "dir2", ignore=shutil.ignore_pattern(‘*.txt‘)) # 忽略.txt文件
shutil.rmtree(path[,ignore_errors[, onerror]])
shutil.move(src, dst)
shutil.make_archive(nase_name, format, ...)
shutil对于解包分两个模块进行
zipfile
#解压缩
import zipfile
z = zipfile.ZipFile(‘a.zip‘, ‘r‘)
z.extractall(path=‘.‘) # 解压至指定目录
z.close()
tarfile
# 解压缩
import tarfile
t = tarfile.open("a.tar.gz", ‘r‘)
t.extractall(‘/tmp‘) # 解压至指定目录
t.close()
序列化与反序列化
是什么:
为何需要:
如何序列化与反序列化
json模块
json.dumps() json.dump() json.loads() json.load() (适用于pickle模块,且用法相同,但写入格式为二进制而不是字符串)
import json
# 序列化写入文件的复杂形式
res = json.dumps([1, 2, ‘a‘, True]) # 序列化
print(res, type(res)) # [1, 2, "a", true] <class ‘str‘>
with open(‘t.json‘, mode=‘wt‘, encoding=‘utf-8‘) as f:
f.write(res)
# 序列化写入文件的简单方式
with open(‘t.json‘, mode=‘wt‘, encoding=‘utf-8‘) as f:
json.dump([1, 2, ‘a‘, True], f)
# 反序列化读出文件的复杂方式
with open(‘t.json‘, mode=‘rt‘, encoding=‘utf-8‘) as f:
res = json.loads(f.read())
print(res, type(res)) # [1, 2, ‘a‘, Trrue] <class ‘list‘>
# 反序列化读出文件的简单方式
with open(‘t.json‘, mode=‘rt‘, encoding=‘utf-8‘) as f:
res = json.load(f)
猴子补丁
属性在运行时的动态替换,叫做猴子补丁(Monkey Patch);拥有在模块运行时替换的功能
应在入口处打补丁,即在执行文件处打补丁
def monkey_patch_json():
json.__name__ = ‘ujson‘ # ujson的性能比json更好,且方法相同,可用补丁替换
jsonn.dumps = ujson.dumps
json.loads = ujson.loads
pickle模块
shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;
key必须为字符串,而值可以是python所支持的数据类型
import shelve
f=shelve.open(r‘sheve.txt‘)
# f[‘student_1‘] = {‘name‘: ‘Stephane‘, ‘age‘:19}
print(f[‘student_1‘]) # {‘name‘: ‘Stephane‘, ‘age‘:19}
f.close()
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单
xml格式如下,通过"<>"节点来区别数据结构
<?xml version="1.0"?>
<data>
<country name="Liechtenstein">
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
配置文件后缀一般为*.ini或*.cfg
文件格式
# 注释1
; 注释2
[section1]
k1 = v1
k2:v2
user=egon
age=18
is_admin=true
salary=31
[section2]
k1 = v1
读取
import configparser
config = configparser.ConfigParser()
config.read(‘tt.ini‘)
# 获取sections
print(config.sections())
# 获取某个section下的keys
print(config.options(‘section1‘)) # 所有key组成的列表
# 获取items
print(config.items(‘section1‘))
# 获取特定section下特定key的值
print(config.get(‘section1‘, ‘user‘))
print(config.getint(‘section1‘, ‘age‘))
print(config.getboolean(‘section1‘, ‘is_admin‘))
改写
import configparser
config=configparser.ConfigParser()
config.read(‘tt.ini‘,encoding=‘utf-8‘)
#删除整个标题section2
config.remove_section(‘section2‘)
#删除标题section1下的某个k1和k2
config.remove_option(‘section1‘,‘k1‘)
config.remove_option(‘section1‘,‘k2‘)
#判断是否存在某个标题
print(config.has_section(‘section1‘))
#判断标题section1下是否有user
print(config.has_option(‘section1‘,‘‘))
#添加一个标题
config.add_section(‘Stephane‘)
#在标题Stephane下添加name=Stephane,age=19的配置
config.set(‘Stephane‘,‘name‘,‘Stephane‘)
config.set(‘Stephane‘,‘age‘,19) #报错,必须是字符串
#最后将修改的内容写入文件,完成最终的修改
config.write(open(‘tt.ini‘,‘w‘))
什么是哈希
哈希的用处
如何用哈希
import hashlib
passwd = hashlib.md()
passwd.update("Hello".encode(‘utf-8‘)) # 必须为bytes类型
passwd.update("World".encode(‘utf-8‘))
print(passwd.hexdigest()) # "HelloWorld"对应的哈希值
用来执行系统命令
import subprocess
obj = subprocess.Popen(‘dir .‘,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
print(obj.stdout.read().decode(‘utf-8‘))
print(obj.stderr.read().decode(‘utf-8‘))
日志模块
日志级别与配置
import logging
#1. 日志配置
logging.basicConfig(
# 1、日志输出位置:1、终端 2、文件
# filename=‘access.log‘, # 不指定,默认打印到终端
# 2、日志格式
format=‘%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s‘,
# 3、时间格式
datefmt=‘%Y-%m-%d %H:%M:%S %p‘,
# 4、日志级别
# critical => 50
# error => 40
# warning => 30
# info => 20
# debug => 10
level=30,
)
# 2. 输出日志
logging.debug(‘debug‘) # 调试日志 级别10
logging.info(‘info‘) # 消息日志 20
logging.warning(‘warning‘) # 警告日志 30
logging.error(‘error‘) # 错误日志 40
llogging.critical(‘critical‘) # 严重日志 50
# 输出:
"""
WARNING:root:warning
ERROR:root:error
CRITICAL:root:critical
"""
日志字典配置
"""
logging配置
"""
import os
# 1、定义三种日志输出格式,日志中可能用到的格式化串如下
# %(name)s Logger的名字
# %(levelno)s 数字形式的日志级别
# %(levelname)s 文本形式的日志级别
# %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
# %(filename)s 调用日志输出函数的模块的文件名
# %(module)s 调用日志输出函数的模块名
# %(funcName)s 调用日志输出函数的函数名
# %(lineno)d 调用日志输出函数的语句所在的代码行
# %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
# %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
# %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
# %(thread)d 线程ID。可能没有
# %(threadName)s 线程名。可能没有
# %(process)d 进程ID。可能没有
# %(message)s用户输出的消息
# 2、强调:其中的%(name)s为getlogger时指定的名字
standard_format = ‘[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]‘ ‘[%(levelname)s][%(message)s]‘
simple_format = ‘[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s‘
test_format = ‘%(asctime)s] %(message)s‘
# 3、日志配置字典
LOGGING_DIC = {
‘version‘: 1,
‘disable_existing_loggers‘: False,
‘formatters‘: {
‘standard‘: {
‘format‘: standard_format
},
‘simple‘: {
‘format‘: simple_format
},
‘test‘: {
‘format‘: test_format
},
},
‘filters‘: {},
# handlers是日志的接收者,不同的handler会将日志输出到不同位置
‘handlers‘: {
#打印到终端的日志
‘console‘: {
‘level‘: ‘DEBUG‘,
‘class‘: ‘logging.StreamHandler‘, # 打印到屏幕
‘formatter‘: ‘simple‘
},
#打印到文件的日志,收集info及以上的日志
‘default‘: {
‘level‘: ‘DEBUG‘,
‘class‘: ‘logging.handlers.RotatingFileHandler‘, # 保存到文件,日志轮转
‘formatter‘: ‘standard‘,
# 可以定制日志文件路径
# BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
# LOG_PATH = os.path.join(BASE_DIR,‘a1.log‘)
‘filename‘: ‘a1.log‘, # 日志文件
‘maxBytes‘: 1024*1024*5, # 日志大小 5M
‘backupCount‘: 5,
‘encoding‘: ‘utf-8‘, # 日志文件的编码,不用担心中文log乱码了
},
‘other‘: {
‘level‘: ‘DEBUG‘,
‘class‘: ‘logging.FileHandler‘, # 保存到文件
‘formatter‘: ‘test‘,
‘filename‘: ‘a2.log‘,
‘encoding‘: ‘utf-8‘,
},
},
# loggers是日志的产生者,产生的日志会传递到handler然后控制输出
‘loggers‘: {
#logging.getLogger(__name__)拿到的logger配置
‘my_logger‘: {
‘handlers‘: [‘default‘, ‘console‘], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
‘level‘: ‘DEBUG‘, # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
‘propagate‘: False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
},
‘public_logger‘: {
‘handlers‘: [‘other‘,],
‘level‘: ‘DEBUG‘,
‘propagate‘: False,
},
# 当找不到日志名时,默认调用‘‘(空key)
‘‘: {
‘handlers‘: [‘other‘,],
‘level‘: ‘DEBUG‘,
‘propagate‘: False,
},
},
}
导入配置文件
import os
from logging import config, getLogger
current_path = os.path.dirname(os.path.dirname(__file__))
os.path.join(current_path)
from logging_config import LOGGING_DIC # 从定义日志配置的文件中导入配置字典
config.dictConfig(LOGGING_DIC)
mode = logging.getLogger(‘my_logger‘)
mode.info(‘Info‘) # [2020-11-08 02:35:49,389][MainThread:19000][task_id:my_logger][logging模块.py:22][INFO][Info]
日志轮转
日志记录程序运行过程中的关键信息,当日志存储过大时们需要进行定期迁移(轮转)
在logging下的handlers中有RotatingFileHandler类,用来日志轮转
# 日志配置文件中handlers的一部分
‘default‘: {
‘level‘: ‘DEBUG‘,
‘class‘: ‘logging.handlers.RotatingFileHandler‘, # 保存到文件,日志轮转
‘formatter‘: ‘standard‘,
‘filename‘: ‘a1.log‘, # 日志文件
‘maxBytes‘: 1024*1024*5, # 日志大小 5M
‘backupCount‘: 5, # 最多产生份数
# 新内容总是会保存到a1.log中,如果超出,则自动将就内容拷贝至备份中,当达到最大份数,第一个备份文件会被覆盖
‘encoding‘: ‘utf-8‘,
},
日志名的命名
正则
正则表达式
元字符 | 作用 |
---|---|
* | 匹配前一个字符匹配0次或任意多次 |
. | 匹配除了换行符意外任意一个字符 |
^ | 匹配行首(如 ^Hi 会匹配以Hi开头的行) |
$ | 匹配行末(如 Hi$ 会匹配以Hi结尾的行) |
[] | 匹配中括号中指定的任意一个字符,只匹配一个字符(如[ab]会匹配任意a或b,[a-z][0-9] 会匹配小写字母和数字组成的两位字符) |
[^] | 匹配除中括号中内容以外的任意一个字符,只匹配一个字符(如[ab]会匹配除过a或b的任意字符,[a-z][^0-9] 会匹配除过小写字母和数字组成的任意两位字符) |
\ | 转义符 |
{n} | 表示其前面的字符恰好出现n次(如[0-9]{4},表示匹配四位数字) |
{n,} | 表示其前面的字符出现不少于n次(如[0-9]{4,},表示匹配四位以上数字) |
{n,m} | 表示其前面的字符至少出现n次,至多出现m次(如[0-9]{4,6},表示匹配4-6位数字) |
\w | 匹配字母数字及下划线(也可匹配中文) |
\W | 匹配非字母数字及下划线 |
\s | 匹配任意空白字符,等价于[\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意数字 |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\z | 匹配字符串结束 |
\Z | 匹配字符串结束,如果存在换行,只匹配到换行前的结束字符串 |
\G | 匹配最后匹配完成的位置 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
+ | 匹配前一个字符匹配1次或任意多次 |
? | 匹配前一个字符匹配0次或1次,非贪婪方式 |
() | 匹配括号内的表达式,也表示一个组 |
a|b | 匹配a或b |
re.findall(元字符(串), 字符串, ...)
re.search(元字符(串), 字符串).group()
re.match(元字符(串), 字符串)
re.split(元字符(串), 字符串)
类也是容器,该容器用来存放同类对象共有的数据与功能
类的使用思路
先定义类
类名使用驼峰体
class ClassName:
# 变量的定义
# 功能的定义
类体代码在定义阶段就会执行,产生类的名称空间
在类中创建__init__方法,在定义阶段不会执行
# self参数为自动传入,self即对象本身
def __init__(self, argu1, argu2, ...):
...
访问类
调用类产生对象
object_1 = ClassName()
调用类的过程又被称为实例化
object_1 = ClassName(argu1, argu2, ...)
产生的对象也可以产生特定的变量
object_1.x = 1 # x不是在ClassName定义时产生的
什么是隐藏属性
在类内可以使用隐藏属性
在类外无法直接访问双下划线开头的属性,但知道了类名和属性名就可拼出名字,这种操作只是语法意义上的变形
这种变形在检测类体语法时(即类的定义阶段)发生,在此之后定义__开头属性不会被隐藏
在类的__dict__属性中,隐藏属性的key会变形成
# "_ClassName.__var" 而非 "ClassName.__var"
怎样隐藏属性
在属性前加"__前缀"
class ClassName:
__x = 1 # 隐藏属性
ClassName.__x # 报错
为何要有隐藏属性
是一种装饰器,用类实现,用来绑定给对象的方法伪造成一个数据属性
class ClassName:
def __init__(self, weight, height):
self.weight = weight
self.height = height
@property
def bmi(self):
return self.weight / (self.height ** 2)
info = ClassName(70, 1.83)
print(info.bmi) # bmi原本是函数,此时被装饰器伪装成数值
可以将操作同一属性的函数对象进行封装,伪装成数据
# 方法一:
class ClassName:
def __init__(self, name):
self.__name = name
def get_name(self): # 无参数有返回值
return self.__name
def set_name(self, var): # 有参数
if type(var) is not str:
return
self.__name = var
def del_name(self): # 无参数无返回值
del self.__name
name = property(get_name, set_name, del_name) # 将三个类型不同的函数封装到property,伪装为数据属性
person_1 = ClassName(‘Stephane‘)
print(person_1.name) # 调用get_name
person_1.name = ‘Zhang‘ # 调用set_name
del person_1.name # 调用del_name
# 方法二:
class ClassName:
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
# name下有setter getter deleter...等访问操作属性
# 操作同一属性的函数对象需命名相同,但功能不同
@name.setter
def name(self, var):
if type(var) is not str:
return
self.__name = var
@name.deleter
def name(self):
del self.__name
print("The var has been deleted.")
person_1 = ClassName(‘Stephane‘)
print(person_1.name)
person_1.name = ‘Zhang‘
del person_1.name
class Parent1: # 父类
pass
class Parent2:
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1, Parent2): # 多继承
pass
print(Sub1.__bases__) # Parent1
print(Sub2.__bases__) # Parent1, Parent2
会从对象所在类开始查找,即使在当前位置在父类中
class Person:
def out(self):
print("Out from Person.")
def out2(self):
print("Out2 from Person.")
self.out()
class Student(Person):
def out(self):
print("Out from Student.")
student_1 = Student()
student_1.out2() # Out2 of Person. Out of Student.
若使用隐藏属性,则会在查找时替换为_ClassName. __xxx
class Person:
def __out(self):
print("Out from Person.")
def out2(self):
print("Out2 from Person.")
self.__out() # self._Person.__out()
class Student(Person):
def out(self):
print("Out from Student.")
student_1 = Student()
student_1.out2()
如果多继承是非菱形问题,Python1与Python3的属性查找顺序一样,都是一个分支一个分支找下去,最后找到object
当多继承是菱形问题,那么经典类与新式类会有不同的MRO,分别对应属性的两种查找方式:深度优先和广度优先
一个子类继承多个类,而多个类最终都汇集于同一个非object类时,就容易引发“菱形问题”
class A:
def out(self):
print("Out from A.")
class B(A):
def out(self):
print("Out from B.")
class C(A):
def out(self):
print("Out from C.")
class D(B, C):
pass
obj = D()
obj.out() # out from B.
D.mro()
"""
[<class ‘__main__.D‘>, <class ‘__main__.B‘>, <class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘object‘>]
"""
# 因此查找顺序为 D -> B -> C -> A -> object
Python2中的经典类为深度优先查找,即在检索第一条分支时就“一条道走到黑”,会检索到共同父类
Python2的新式类和Python3为广度优先查找,会在检索最后一条分支时“一条道走到黑”,检索共同父类
核心:在多继承背景下尽可能地提升多继承的可读性,让多继承满足人的继承习惯
使用Mixin类时,需注意
class Vehicle:
pass
# 添加后缀Mixin表示一种规范,表示混合,而非表示"is"
class FlialeMixin:
...
class CivilAirCraft(FlialeMixin, Vehicle):
pass
class Helicoper(FlialeMixin, Vehicle):
pass
class NormalCraft(Vehicle):
pass
class Car(Vehicle):
pass
在子类派生的新方法中如何重用父类的功能
直接调用某个类下的函数,不依赖继承
用super()调用父类的函数,严格依赖继承关系
class A:
def out(self):
print("Out from A")
super().out()
# 依照C类的MRO列表,B在A之后,因此super().out()为B中的out()
class B:
def out(self):
print("Out from B")
class C(A, B):
pass
obj = C()
obj.out() # Out of A Out of B
多态:同一事物有多种形态
为何要有多态、多态性
如何使用多态
多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象
class Animals:
def say(self):
print("The frequency of voice.")
class People(Animals):
def say(self):
super().say()
print("Talk")
class Pig(Animals):
def say(self):
super().say()
print("Woof")
class Dog(Animals):
def say(self):
super().say()
print("Heng")
def animals_say(type_val): # 可以将这个方法单独提取出来,使用统一接口,方便调用
type_val.say()
obj_1 = People()
obj_2 = Pig()
obj_3 = Dog()
obj_1.say()
obj_2.say()
obj_3.say()
animals_say(obj_1)
animals_say(obj_2)
animals_say(obj_3)
可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
pass
cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象。
比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度
只要两个类中有名字相同且功能相同的函数,就可以“蒙混过关”,起到与多态相同的效果
class People:
def say(self):
print("The frequency of voice.")
print("Talk")
class Pig:
def say(self):
print("The frequency of voice.")
print("Woof")
调用者是类,即自动传入的是类
import ipSettings # 含参数的文件
# IP = "192.168.20.5"
# PORT = 3306
class Mysql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
def output(self):
print("The IP is %s, the port is %d" % (self.ip, self.port))
@classmethod # 将下面的函数装饰为绑定给类的方法
def put(cls): # cls即class,与self相似
return cls(ipSettings.IP, ipSettings.PORT)
# 赋值给对象时,相当于调用__init__方法,并把相信的参数传入__init__()
obj = Mysql.put()
print(obj)
obj.output()
##### 6.7.2 非绑定方法(静态方法)
- 为类中某个函数加上装饰器@staticmethod后,该函数就变成了非绑定方法,也称为静态方法
- 该方法***不与类或对象绑定***,类与对象都可以来调用它,但它就是一个普通函数,没有自动传值那么一说
```python
import ipSettings
class Mysql:
def __init__(self, IP, PORT):
# self.nid
self.IP = IP
self.PORT = PORT
@staticmethod # 将该函数装饰为静态方法(非绑定方法)
def createId():
pass
@classmethod
def put(cls):
return cls(ipSettings.IP, ipSettings.PORT)
def output(self):
print("The IP is %s, the port is %d" % (self.IP, self.PORT))
obj = Mysql.put()
obj.output()
print(Mysql.createId()) # 不需实例化即可调用该方法
python是动态语言,而反射(reflection)机制被视为动态语言的关键
反射机制指的是在程序运行状态中
对于任意一个类,都可以知道这个类的所有属性和方法
对于任意一个对象,都能够调用他的任意方法和属性
这种动态获取程序信息以及动态调用对象的功能称为反射机制
什么是反射?
为何要用?
如何使用?(步骤)
dir(对象) 查看对象的属性(列表)
可以通过字符串反射到真正的属性上
四个内置函数的使用
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def get(self):
print(‘name is %s and age is %d‘ %(self.name, self.age))
print(dir(obj)) # [‘__class__‘, ‘__delattr__‘, ‘__dict__‘,...]
print(obj.__dict__[‘name‘])
print(hasattr(obj, ‘name‘))
print(getattr(obj, ‘name‘))
print(setattr(obj, ‘name‘, ‘XXX‘))
print(delattr(obj, ‘name‘))
定义在类内部,以__开头,并以__结尾的方法
特点 会在某种情况下自动触发执行
为何要用?
如何使用?(例子)
__new__ 在创建空对象之后,调用元类__init__方法之前,用于创造空对象
class People:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
res = ‘Name is %s and age is %d‘ %(self.name, self.age)
return res # 返回值须为字符串
obj = People(‘Stephane‘, 19)
print(obj) # Name is Stephane and age is 19
什么是元类?
在实例化对象(调用类)时格式为 objectname = ClassName(...)
类本身也是对象,调用类的类格式为 ClassName1 = Classname2(),后者被称为元类(用来实例化产生类的类)
元类 ---- 实例化 ----> 类 ---- 实例化 ----> 对象
# 查看内置的元类type
print(type(People)) # <class ‘type‘>
class关键字定义的所有的类以及内置类都是由元类type实例化产生
如何使用元类?
class关键字创建类的步骤
类名
class ClassName = "..."
类的基类(并非元类)
class_bases = (object|...)
执行类体代码拿到类的名称空间
class_dic = {} #类的名称空间
class_body = "类体代码"
exec(class_body, {}, class_dic) # 第二个参数为类体中使用的的全局名称空间,没有则为空
调用元类
Classname = type(class_name, class_bases, class_dic)
如何自定义元类控制类的产生
class Mymeta(type): #只有继承了type类的类才是元类
def __init__(cls, class_name, clas_sbases, class_dic):
super().__init__(cls)
pass
def __new__(mcs, *args, **kwargs):
# 参数为调用元类时传入的参数
return super().__new__(mcs, *args, **kwargs)
class People(metaclass=Mymeta): # 调用Mymeta元类
pass
# People = Mymeta(class_name, class_bases, class_dic)
调用自定义元类发生的事情
调用自定义元类实质是在调用该元类的__call__方法
标签:属性绑定 面向过程 重用 lse case star 开始 超过 形参与实参
原文地址:https://www.cnblogs.com/stephane-zhang/p/14198513.html