因为简书字数限制,所以将整部笔记拆分发布,依次为 A、B、C.
笔记中代码均可运行在Jupyter NoteBook下。
建议下载笔记用VSCode打开文件中.ipynb格式的笔记阅读,或是用Jupyter运行于浏览器,此阅读模式下可以边阅读边运行代码,便于理解
笔记链接
如果无法打开,请自行复制地址到浏览器打开。
#在download源代码的时候要适当选择较低的版本,因为语言的源码版本越低越能看出作者的意图
python注释
python的编码格式注释:#-*- coding: utf-8 -*-
linux书写环境变量注释:#!/usr/bin/env python3
'''python中的帮助函数:help(func_name)'''
# 例子
help(input)
# 例子结束
Help on method raw_input in module ipykernel.kernelbase:
raw_input(prompt='') method of ipykernel.ipkernel.IPythonKernel instance
Forward raw_input to frontends
Raises
------
StdinNotImplentedError if active frontend doesn't support stdin.
python运算符
#算数运算符
'''python中 / 表示浮点除法 // 向下取整除法'''
# 例子
print(3/4)
print("*"*8)# python符号也可进行算数运算
print(3//4)
# 例子结束
python中的比较运算符:>,<,>=,<=,==,!=,<>(不等于的这种表示方式会逐渐淘汰)
python逻辑运算符 and,or,not
'''
python赋值运算符:=, +=, -=,*=,/=,**= (算术运算符和=相结合的操作)
python 位运算符:&[按位与,同为1,否则为假],| [按位或,二者中一个即可]
^ [按位异或,相异为1],~[按位取反,1变0,0变1],
<<[左移,乘2],>>[右移,除以2]
进制转换 :转二进制:bin(),八进制:oct(),16进制:hex(),转10进制:int()
'''
#例子
a=3
print(a<<1)
b=4
print(b>>1)
# 例子结束
# python 成员运算符:in,not in
# python身份运算符:is, is not
'''is和 == 的区别:is用于判断两个变量引用是否为同一个(即id是否相同),==用于判断变量值是否相等'''
# 例子
x=y=[1, 2, 3]
z=[1, 2, 3]
print(x is y, id(x),id(y), x==y)
print(x is z, id(x), id(z), x==z)
6
2
True 140646103441800 140646103441800 True
False 140646103441800 140646103444296 True
'''
对于python中a,b = b,a的换值方法的解析
a, b = b, a操作是将a,b的地址互换
关键在于ROT_TWO的地址互换
'''
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
from dis import dis
def demo(a, b):
print(a,b)
a, b = b, a
print('new: ', a,b)
a, b = 2, 3
demo(a,b)
dis(demo)
2 3
new: 3 2
10 0 LOAD_GLOBAL 0 (print)
2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 CALL_FUNCTION 2
8 POP_TOP
11 10 LOAD_FAST 1 (b)
12 LOAD_FAST 0 (a)
14 ROT_TWO
16 STORE_FAST 0 (a)
18 STORE_FAST 1 (b)
12 20 LOAD_GLOBAL 0 (print)
22 LOAD_CONST 1 ('new: ')
24 LOAD_FAST 0 (a)
26 LOAD_FAST 1 (b)
28 CALL_FUNCTION 3
30 POP_TOP
32 LOAD_CONST 0 (None)
34 RETURN_VALUE
比较:type()和 isinstance()
'''type()和isinstance()区别
type()不认为子类是父类的一种类型,而isinstance()认为是
isinstance()也用来判断是不是可迭代对象'''
#l例子
class A:
pass
class B(A):
pass
print("isinstance(, A(),A) is ", isinstance(A(),A))
print("type(A())==A is ",type(A())==A)
print("isinstance(B(),A) is ", isinstance(B(),A))
print("type(B())==A is ",type(B())==A)
isinstance(, A(),A) is True
type(A())==A is True
isinstance(B(),A) is True
type(B())==A is False
基本数据类型
# 变量可使用del语句删除
# 例子
vara=1
print(vara)
del vara
print(vara) # 删除后打印会报错
1
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-2-1f49fa2b38a1> in <module>()
4 print(vara)
5 del vara
----> 6 print(vara) # 删除后打印会报错
NameError: name 'vara' is not defined
'''
六个标准数据类型:
Number(数字),String(字符串),List(列表),Tuple(元组),Set(集合),Dictionary(字典)
不可变数据:Number,String,Tuple
可变数据:list,dict,set
'''
Number(数字)
int、float、bool、complex(复数)
#例子
complex(1,3)
(1+3j)
数学函数
'''ads(x)[绝对值],ceil(x)[向上取整],
cmp(x,y)[比较xy,x>y=1,x<y=-1,x==y=0,<python3已弃用,使用(x>y)-(x<y)>]
exp(x)[e的x次方],fabs(x)[浮点绝对值],floor(x)[向下取整],
pow(x,y)[x的y次],round(x,y)[对x四舍五入,y表示精确度]
'''
# abs()与fabs()比较
from math import fabs
print(abs(-5))
print(fabs(-5))
# round(x,y)例子
print(round(5.6482133, 5))
5
5.0
5.64821
随机数函数
'''
choice(seq)[从序列seq中随机挑一个](在模块random中)
randrange([start,]stop[,step])
random()[随机在(0-1中生成)]
seed([x])[改变随机数生成器种子]
shuffle(lst)[随机排序一个序列]
uniform(x,y)[在(x-y中随机生成一个浮点数)]
'''
# choice()例子
import random
print("choice()例子:", random.choice(range(10)))
# randrange()使用例子
print("randrange()例子:", random.randrange(1,90,1)) # 随机数范围为1-90,步长为1
# seed()不常使用
# shuffle()例子
var_shuffle_ =[1, 2, 3, 4, 5]
random.shuffle(var_shuffle_)
print(var_shuffle_)
# uniform()例子
print("uniform例子:", random.uniform(1, 45))
choice()例子: 0
randrange()例子: 79
[3, 4, 5, 2, 1]
uniform例子: 4.053759455798719
from math import pi
print(pi)
3.141592653589793
字符串
'''
原始字符串:r/R
'''
#例子
a = r'test\n'
print('test\n')
print(a)
test
test\n
格式化输出
# 格式化输出可以用% 和"str".format()
# "str".format()中在字符串"str"中用{[var]}表示变量,[var]为可选的变量名
# 这里注意:{}中无变量名,切未指示位置的时候,无变量名的参数应在有变量名的参数的位置前面
# 在{}中表示数字时用':'(冒号),表示数字格式化
# format()还可以在"str"的{}中通过指定位置来输出
# 在format中对大括号的转义用{},例子{{}}
'''
%c[字符,或ascii码]
'''
print("这是 %s" %('用%格式化输出例子'))
# format()格式化输出例子
print("这是{name}{}".format('有无变量名混用的例子', name='format'))
print("这是{0}而不用{1}的例子,{1}{0}".format("通过位置输出", "变量名输出"))
from math import pi
print("这是数字格式化例子:输出保留两位小数的pi{:.2f}, 不设置保留位数的pi={ppi}".format(pi,ppi=pi))
print("format对{{}}的转义例子输出".format())
这是 用%格式化输出例子
这是format有无变量名混用的例子
这是通过位置输出而不用变量名输出的例子,变量名输出通过位置输出
这是数字格式化例子:输出保留两位小数的pi3.14, 不设置保留位数的pi=3.141592653589793
format对{}的转义例子输出
字符串内建函数
'''
capitalize()[首字母大写],
center(width,fillchar)[在宽度为width的输出空间中居中显示fillchar字符串],
count(str,beg=0,end=len(string))[统计str在string中出现的次数,
若beg或end指定,则返回指定范围的次数],
bytes.decode(encoding="utf-8",errors="strict")[指定解码,
用的是bytes对象的方法]
encode(encoding="utf-8",errors='strict')[指定编码方式]
查找:find,index,rfind,rindex(相同都返回下标,不同,-1,异常)
replace(原,现):替换
split(切割符) :切割
capitalize:单个首字母大写
title:全部首字母大写
endswith(str):判断结尾
startswith(str):判断开头
rjust(int),ljust(int),center(int):在int个字符中右左中对齐
strip():去掉str两边空格,\n
partition(str):以左起第一个str分割为元组
split(str):以str分割
isalpha():是否纯字母
isalnum():是否数字字母组合
isdigit():是否纯数字
str.join(a,b):以str连接a,b
'''
列表 list
'''
列表切片:list[start:end [:step(默认为1)]]
'''
# 列表生成器
[x for x in range(0,10,2)]
[0, 2, 4, 6, 8]
增
''' list.
append():追加(整体添加)
insert(位置,内容):添加
extend(list):追加list(合并list)
'''
#例子
A=[12,34,55]
B=['aa','bb']
print('this is A:',A,'\nthis is B:',B)
A.append(666)
print('this is A.append(666)',A)
B.insert(1,'this is 1')
print('this is B.insert:',B)
A.extend(B)
print('A.extend(B)',A)
this is A: [12, 34, 55]
this is B: ['aa', 'bb']
this is A.append(666) [12, 34, 55, 666]
this is B.insert: ['aa', 'this is 1', 'bb']
A.extend(B) [12, 34, 55, 666, 'aa', 'this is 1', 'bb']
删
''' list.
pop():默认最后一个
remove(str):删除str内容
clear():清空列表
'''
#例子
A=[11,22,33,44]
print(A)
print('*'*8)
#A.pop()会返回弹出值
print(A.pop())
print(A)
print('*'*8)
A.remove(11)
print(A)
A.clear()
print('*'*8,"\nA=", A)
[11, 22, 33, 44]
********
44
[11, 22, 33]
********
[22, 33]
********
A= []
改
'''
直接用下标进行修改
'''
#例子
A=[11,22,33]
print(A)
print('*'*8)
A[0]="changed"
print(A)
[11, 22, 33]
********
['changed', 22, 33]
查
'''
in:布尔值
list.index:下标,异常
'''
#例子
A=[11,22,33]
print(11 in A)
print(A.index(1234))
True
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-17-f32251f40118> in <module>()
7 A=[11,22,33]
8 print(11 in A)
----> 9 print(A.index(1234))
ValueError: 1234 is not in list
其他内置函数
''' list.
count(obj) 统计obj出现次数
reverse() 反向排列
sort([key=None,reverse=False]) 排序,key表示排序依据,
reverse表示反向,默认False
copy() 复制列表
'''
'''
len(list):list长度
max(list):list中的最大值
min(list):list中的最小值
list(seq):将元组转换为list
'''
A = [1, 2, 3, 4]
B = (22, 33)
print("对A的操作:len(A): {lena}\tmax(A):{maxa}\tmin(A):{mina}\n".format(lena=len(A), maxa=max(A), mina=min(A)))
print("对B的操作:list(B):{listb}".format(listb=list(B)))
对A的操作:len(A): 4 max(A):4 min(A):1
对B的操作:list(B):[22, 33]
元组
'''
不可更改的列表,死的
扩展通过新建一个组合元组
tuple(list):将list转换为tuple
'''
字典
增
'''
dict['key']='value'
'''
删
'''
del
pop(key,[default]) 删除字典给定键的键值对,返回被删除的值,key必须给出
popitem() 随机删除并返回一对键值对(一般删除的是末尾的键值对)
'''
改
'''
键值对赋值
dict.update(dict2) 把字典dict2中的键值对更新到dict中
'''
dict_a = {'name':'value'}
dict_b = {'name':'18'}
print(dict_a)
dict_a.update(dict_b)
print(dict_a)
{'name': 'value'}
{'name': '18'}
查
'''
dict.get(key)
in
'''
其他内置函数
'''
len(dict):键值对个数
dict.keys():dict的所有键
dict.values():dict的所有值
dict.
clear() 清空字典
copy() 浅复制
fromkeys() 创建新字典
ietms() 以列表返回可遍历的键值对
'''
#例子
D={'a':'111','b':222}
print(D)
print(D.items())
D.clear()
print(D)
{'a': '111', 'b': 222}
dict_items([('a', '111'), ('b', 222)])
{}
集合 set
'''
集合中没有重复值,(集合会自动去重)
list、tuple可以有重复值,不会自动去重
字典中,当键重复时,会保留最后的那个,其他的去重(不论值是否相同)
强制类型转换得到的变量恢复到原类型用eval()
'''
增
'''set.
add() 添加元素
'''
删
'''set.
pop() 随机移除
remove() 移除指定元素
discard() 删除指定元素
clear() 清空
'''
其他
'''set.
copy() 拷贝
difference() 返回多个集合的差集
difference_update() 移除集合中的元素,该元素在指定的集合也存在。
intersection() 返回交集
isdisjoint() 布尔判断集合间是否包含相同元素
issubset() 子集判断
union() 并集
'''
条件控制
'''
if exp:
pass
elif exp1:
pass
else:
pass
if的各种真假判断
"",None,[],{},数字0,都表示假
'''
循环
'''
while exp:
pass
while exp:
pass
else:
pass
for var in seq: # 这里seq必须是可迭代数据
pass
break
continue
'''
'''
for循环中删除元素的坑:在循环的过程中删除元素可能出现漏删的情况。
'''
# for循环例子
a = [1,2,3,4,5,6,7]
for i in a:
print(i, end=',')
if i == 2 or i == 3:
a.remove(i)
print("\na = {0}".format(a))
#这时输出分别为1,2,4,5,6,7,和a = [1,3,4,5,6,7]
#从这里的结果可以看出在循环中漏删了3
1,2,4,5,6,7,
a = [1, 3, 4, 5, 6, 7]
'''
这里可以使用“缓存”的方法
用另一个变量临时存储要删除的变量,待循环结束后在删除
'''
# for循环解决例子
a = [1,2,3,4,5,6,7]
b = []
for i in a:
print(i, end = ",")
if i == 2 or i == 3:
b.append(i)
for i in b:
a.remove(i)
print("\na = {0}".format(a))
#此时得到的a的结果就是完全删除的结果 a = [1,4,5,6,7]
1,2,3,4,5,6,7,
a = [1, 4, 5, 6, 7]
函数
在一个Python文件中,定义重名的函数不会报错,但是会使用最后的一个函数
def funcName(*args,**kwargs):
pass
不定长参数
'''
*args:长度不限,来着不拒,输出的是元组形式
**kwargs:长度不限,输入必须是key=value的形式,否则无法传入;
输出为字典形式
注意:不定长参数应放在参数列表最后,否则会吞参数
'''
#*args,**kwargs例子
def funcDemo(a,*args,**kwargs):
print(a)
print(args)
print(kwargs)
funcDemo(111,1,2,3,4,name='name',age='age')
111
(1, 2, 3, 4)
{'name': 'name', 'age': 'age'}
'''
对不定长参数传入的拆包
元组传入加*:*tuple
字典传入加**:**dict
如果不拆包直接传入,会被*args吞并,不会区分传值
'''
#拆包例子
A=(1,2,3,4)
B={'name':'name','age':'age'}
def func(*args,**kwargs):
print(args)
print(kwargs)
func(A,B)
print("*"*8)
func(*A,**B)
((1, 2, 3, 4), {'name': 'name', 'age': 'age'})
{}
********
(1, 2, 3, 4)
{'name': 'name', 'age': 'age'}
匿名函数
'''
lambda [arg1,[arg2,arg3...]]:expression
'''
# lambda简单例子
f = lambda x,y:x+y
print(f(2,3))
5
变量作用域
'''
L(Local) 局部作用域
E(Enclosing) 闭包函数外的函数中
G(Global) 全局作用域
B(Built-in) 内建作用域
变量查找顺序:LEGB
'''
# global和nonlocal
'''
局部内改全局,变量前加global
内部修改外部函数变量,变量前加nonlocal
'''
#nonlocal 例子
def outer():
num=10
print('this is raw num:',num)
def inner():
nonlocal num # 声明外部变量
num='change'
print('this is changed num:',num)
inner()
print('this is num:',num)
outer()
this is raw num: 10
this is changed num: change
this is num: change
迭代器
'''
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
可迭代对象:list,tuple,dict,set,str,生成器
(generator,带yield的generator function)# 这里也可说是Iterable
判断是否是可迭代对象:isinstance:
1
from collections import Iterable
isinstance(flag, Iterable)
2
或者是使用for循环来判断
iter()函数用来将Iterable转换为Iterator
'''
# isinstance 迭代对象判断 例子
# 导入Iterable
from collections.abc import Iterable
# 判断是否可迭代
isinstance('flag', Iterable) # 会输出True
# 例子释义:isinstance是判断变量1(代码中的''flag)是否为变量2(Iterable)类的实例或是其子类的实例
# isinstance 例子
class A(object):
def __str__(self):
return 'this is class A'
class B(A):
def __str__(self):
str = 'this is class B, and is son class of A'
return str
b = B()
a = isinstance(b,B) # 判断b是否为B的实例
c = isinstance(b,A) # 判断b是否为A的实例
print(a,c) # 输出显示b是B的实例,b是A的子类的实例
print("*"*8)
# Iterable 和Iterator判断
from collections.abc import Iterator
a = isinstance('str', Iterator) # False
c = isinstance(iter('str'), Iterator) # True
print(a,c)
True True
********
False True
生成器 generator
对于要创建的数据(尤其是Iterable数据及其范类),不是一下全部创建,而是在程序运行中逐步生成。可以极大的节约内存。
'''
可以用next()获取生成器的下一个返回值
但是这里next()执行完最后一次还调用会报错
加了yield参数的函数就是一个生成器
对生成器函数不可用普通函数的调用方法,生成器函数返回的是一个对象。
此时需要用一个变量来指向这个生成器。
此时用next(x)来执行生成器时,
会在yield处停止,并返回yield后的参数,
这个返回的参数就是生成器生成的值,
在下一次调用生成器时,会在yield停止的地方接着执行。
next(x)和x.__next__()作用相同
一个生成器中可以有多个yield
协程多任务:运行一下,停一下
这里就可以用生成器的来实现多任务
'''
# yield function 例子 : next()输出fib数列
def fib():
a,b = 0,1
for i in range(10):
yield b
a,b = b,a+b
a = fib()
flag = 1
print('1-10的fib数列:')
while flag ==1:
try:
fib = next(a) # 这里next(generator)和a.__next__()是一样的
print(fib, end='\t')
except Exception as result:
print('\n1-10的最大fib数:', fib)
flag = 0
1-10的fib数列:
1 1 2 3 5 8 13 21 34 55
1-10的最大fib数: 55
# yield function 不用next(),使用循环输出生成器生成的值
# 一个生成任意数的斐波那契数列的函数
def fibs(num):
i = 0
a, b = 0, 1
while i<num:
yield b # 生成器核心
a,b = b, a+b
i +=1
# 这里以输出11的fib数列举例
for f in fibs(11):
print(f, end = '\t')
1 1 2 3 5 8 13 21 34 55 89
'''
x.send(value) # value必须有,可以是None,但是不可不写
'''
# x.send(value) 例子
def fib():
a,b = 0,1
for i in range(10):
temp = yield b
a,b = b,a+b
print(temp)
a = fib()
next(a)
# 这里print()的结果是None,因为生成器在yield处停止,
# 不会对temp赋值
k = a.send("yeyeye")
# 这里print()的结果是yeyeye,因为send()将这个值传入了temp
print('生成器返回的值',k)
print('1111111111')
yeyeye
生成器返回的值 1
1111111111
闭包
参考网址1(闭包):
https://blog.csdn.net/marty_fu/article/details/7679297
参考网址2(python编译与执行):
对于上一个参考网址中对for循环中的闭包问题解析的不同解释
https://blog.csdn.net/helloxiaozhe/article/details/78104975
闭包定义
'''
在一个函数内部定义一个函数,
并且内部的函数调用了外部函数的参数,
此时,内部函数和用到的参数就叫做闭包。
闭包 = 函数块+定义函数时的环境
'''
# 闭包定义例子
# 定义一个外部函数
def outer(out_num):
#在函数内部再定义一个函数,
# 并且这个函数用到了外边函数的变量,
# 那么将这个函数以及用到的一些变量称之为闭包
def inner(in_num):
sum = out_num + in_num
return sum
#这里返回的是闭包的结果
return inner
# 对outer函数中的out_num赋值为3
mid = outer(3)
print('mid的类型是:',type(mid))
print('mid的名字是:',mid.__name__)
print('此时的mid为:', mid)
# 这个6是对inner(in_num)中的in_num赋值
result = mid(6)
print('对内外均赋值后mid的类型是:',type(result))
print('对内外均赋值后的mid为:', result)
mid的类型是: <class 'function'>
mid的名字是: inner
此时的mid为: <function outer.<locals>.inner at 0x7ff6d804ce18>
对内外均赋值后mid的类型是: <class 'int'>
对内外均赋值后的mid为: 9
使用闭包的注意事项
'''
闭包内对外部函数的局部变量进行修改:
1、要么在闭包内添加 nonlocal 声明此变量为外部局部变量
(nonlocal 声明只有python3中有,在python2中只能用法2)
2、要么外部变量为列表,字典,集合这种可扩展变量,
在闭包内部才可进行修改
方法2实际上还是由legb次序返回到外部函数环境中,所以两个方法殊途同归
'''
# 例子
def out():
a = 1
def inner():
a = a+1
return a
return inner
t = out()
t() # 这里报错
# 报错原因:python规则指定所有在赋值语句左面的变量都是局部变量
# 则在闭包inner()中,变量a在赋值符号"="的左面,被当做inner()的局部变量,但是未在inner找到a的定义
---------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-28-e57172f27c27> in <module>
16
17 t = out()
---> 18 t() # 这里报错
19 # 报错原因:python规则指定所有在赋值语句左面的变量都是局部变量
20 # 则在闭包inner()中,变量a在赋值符号"="的左面,被当做inner()的局部变量,但是未在inner找到a的定义
<ipython-input-28-e57172f27c27> in inner()
11 a = 1
12 def inner():
---> 13 a = a+1
14 return a
15 return inner
UnboundLocalError: local variable 'a' referenced before assignment
# 解决方法1
def out():
a = 1
def inner():
nonlocal a
a = a+1
return a
return inner
t = out()
print('解决方法1:',t())
# 解决方法2
def out():
# 定义a为一个列表
a = [1]
def inner():
# 在执行时在inner()内未找到a的定义,但是在外部函数空间找到a[0]为list的定义
a[0]= a[0]+1
return a
return inner
t = out()
print('解决方法2:',t())
type(t())# 注意:此时输出的t的类型为list
解决方法1: 2
解决方法2: [2]
list
'''
闭包在循环体中的使用:
'''
# 错误示例
flist = []
for i in range(3):
print('FOO START')
def foo(x):
print('foo running')
print(x + i)
print('foo run over')
print('FOO END')
print('for START')
print('i is : ', i)
flist.append(foo)
print('for END')
for f in flist:
f(2) # 此时输出为4 4 4
# 原因是在执行for是遇到foo定义,只录入了foo(x),而没执行foo(x)的函数体
FOO START
FOO END
for START
i is : 0
for END
FOO START
FOO END
for START
i is : 1
for END
FOO START
FOO END
for START
i is : 2
for END
foo running
4
foo run over
foo running
4
foo run over
foo running
4
foo run over
'''对循环中使用闭包的正确用法'''
# 正确示例
flist = []
for i in range(3):
print('FOO START')
def foo(x, y=i):
print('foo running')
print(x + y)
print('foo run over')
print('FOO END')
print('for START')
print('i is : ',i)
flist.append(foo)
print('for END')
for f in flist:
f(2) # 此时输出为2 3 4
# 在程序执行时将foo(x,y=i)录入,此时foo中的y=i是随着for函数一起执行的,在append到flist中的元素为
# [foo(x,y=0),foo(x,y=1),foo(x,y=2)]
# 在执行foo函数体中的内容的时候会有i的不同次序值,而不是最终的i
FOO START
FOO END
for START
i is : 0
for END
FOO START
FOO END
for START
i is : 1
for END
FOO START
FOO END
for START
i is : 2
for END
foo running
2
foo run over
foo running
3
foo run over
foo running
4
foo run over
闭包的作用
'''
1、持久化编程,保持住当前的运行环境
'''
# 跳棋 例子
origin = [0, 0] # 坐标系统原点
legal_x = [0, 50] # x轴方向的合法坐标
legal_y = [0, 50] # y轴方向的合法坐标
def create(pos=origin):
def player(direction,step):
# 这里应该首先判断参数direction,step的合法性,
# 比如direction不能斜着走,step不能为负等
# 然后还要对新生成的x,y坐标的合法性进行判断处理,
# 这里主要是想介绍闭包,就不详细写了。
new_x = pos[0] + direction[0]*step
new_y = pos[1] + direction[1]*step
pos[0] = new_x
pos[1] = new_y
#注意!此处不能写成 pos = [new_x, new_y],原因在上文有说过
return pos
return player
player = create() # 创建棋子player,起点为原点
print (player([1,0],10)) # 向x轴正方向移动10步
print (player([0,1],20)) # 向y轴正方向移动20步
print (player([-1,0],10)) # 向x轴负方向移动10步
[10, 0]
[10, 20]
[0, 20]
'''
2、根据外部作用域的局部变量来得到不同的结果
这有点像一种类似配置功能的作用,我们可以修改外部的变量,
闭包根据这个变量展现出不同的功能
'''
#
def make_filter(keep):
def the_filter(file_name):
with open(file_name,'r', encoding='utf-8') as file:
lines = file.readlines()
filter_doc = [i for i in lines if keep in i]
return filter_doc
return the_filter
# 获取文件《闭包用途2使用文档.txt》有关"闭包"的关键字
filter = make_filter("闭包")
filter_result = filter("./Gnerate_files/闭包用途2使用文档.txt")
for f in filter_result:
print(f)
这是一个关于闭包用途的使用文档
但是我对原作者在循环中使用闭包的错误剖析有不同的见解,所以没有全部参考原作者的思路
装饰器
写代码的开放封闭原则
开放:对代码进行扩展开发
封闭:对代码已实现的功能,最好不要随意修改
语法糖:写一个函数,用@符号加在其他功能前,达到一个想要的结果
对装饰器有充分的理解要先掌握以下两点:
1、一切皆对象
2、闭包
装饰器的功能:
引入日志
函数执行时间统计
执行函数前预备处理
执行函数后清理功能
权限校验等场景
缓存
装饰器很好的实现了开放封闭原则
# 装饰器例子
def zsq(func):
print('zsq print START')
def prt():
print('prt print'.center(10,"*"))
func()
print('{}执行完毕'.format(func.__name__))
print('zsq print END')
return prt
@zsq
def f1():
print("这是一个f1函数".center(10,"*"))
def f2():
print('这是f2函数')
# test
f1()
print('*'*18)
# 这里的f2 = zsq(f2) 和上面的@zsq作用是一样的
f2 = zsq(f2) # 这里将f2()作为参数,传入zsq()中的func
f2() # 这里的f2已经不是原来的函数了,而是zsq()中封装的prt函数
# 执行顺序为(二者顺序相同,这里以f1为例):
# 1、zsq 函数头
# 2、func = f1
# 3、 f1函数头
# 4、zsq 函数体第一行,这里是print('zsq print START')
# 5、 prt函数头
# 6、prt()后紧跟的一行
# 7、prt函数体中的第一行,这里是print...
# 8、func() 这里也就是f1()
# 9、prt中func()后紧跟的一行
# 依次执行,直到执行完最后一行
# f1 为函数名
# f1() 为调用f1函数,只写f1不会调用f1函数
zsq print START
zsq print END
prt print*
*这是一个f1函数*
f1执行完毕
******************
zsq print START
zsq print END
prt print*
这是f2函数
f2执行完毕
# 多个装饰器 例子
def zsq1(f1):
# 定义装饰器1
def prt1():
print('装饰器1'.center(10, '*'))
return f1() # 回传传入的f函数
return prt1 # 回传闭包内的函数名
def zsq2(f2):
def prt2():
print('装饰器2'.center(10,'*'))
return f2()
return prt2
@zsq1
@zsq2 # 用zsq1()和zsq2()装饰test()
def test():
print('test'.center(10, '*'))
return 'hello world!'
# 测试
tmp = test()
print(tmp)
# 解释:
# python程序是顺序执行的,
# 装入顺序为取近优先,最靠近被装饰函数最先被装饰
# 执行顺序为剥洋葱,由外向内
***装饰器1***
***装饰器2***
***test***
hello world!
'''
装饰器执行的时间
Python解释器执行到@处时,就已经开始装饰了
装饰器对有参数,无参数函数进行装饰:
当无参时,不必特别处理,
当有参数时,对闭包中的函数进行设置形参,
这里就可以进行有参数的函数进行装饰了.
这里需要特别注意:
在有参数时,可以用不定长参数的方式,
*args和**kwargs,在调用装饰的函数时,
需要用同样的不定长参数进行解包
装饰器对带有返回值的函数进行装饰:
在闭包的函数中设置一个return 在调用被装饰的函数时,赋值,这样就可以输出
'''
# 对含定长参函数进行装饰 例子
print('对定长参数函数装饰')
def decorator_var(func):
def decorator_var_in(a, b):
print(a,'\t',b)
func(a,b)
return decorator_var_in
@decorator_var
def foo(a, b):
print('sum = ', a+b)
foo(1,2)
print('定长装饰例子结束\n')
对定长参数函数装饰
1 2
sum = 3
定长装饰例子结束
# 对不定长参数函数装饰 例子
print('对不定长参数函数装饰')
def decorator_var(func):
def decorator_var_in(*args, **kwargs):
print(args,'\n',kwargs)
func(*args, **kwargs)
return decorator_var_in
@decorator_var
def foo(*args, **kwargs):
l = [args, kwargs]
print('set = :',l)
t = (1,2,3)
d = {'name':'sss','age':16}
foo(*t, **d)
print('不定定长装饰例子结束\n')
对不定长参数函数装饰
(1, 2, 3)
{'name': 'sss', 'age': 16}
set = : [(1, 2, 3), {'name': 'sss', 'age': 16}]
不定定长装饰例子结束
# 对带有返回值的函数进行装饰 例子
print('带有返回值的函数进行装饰')
def zsq(func):
def bb():
re = func()
return re # 这里是注意点
return bb
@zsq
def test():
print("this is a test def !".center(30,"*"))
return 666
ret = test()
# 注意这里的return
print(ret)
print('带有返回值的函数进行装饰结束')
带有返回值的函数进行装饰
*****this is a test def !*****
666
带有返回值的函数进行装饰结束
# 通用装饰器 例子
def decorator_pub(func):
def decorator_in(*args, **kwargs):
print("这是一个通用装饰器")
tmp = func(*args, **kwargs)
return tmp
#注意,在Python中,如果return 返回的是None,那么不算是错误,
# 所以,在这里一直写上return 是不算错的
return decorator_in
@decorator_pub
def test(a,b):
print("this is a test")
return a+b
ret = test('a','c')
print(ret)
这是一个通用装饰器
this is a test
ac
带有参数的装饰器
装饰器带参数,在原有的装饰器的基础上,设置外部变量
这里的设置外部变量就是在原有的装饰器的基础上,在外部再加一个带参数的函数
带有参数的装饰器能起到在运行时使用不同的功能
# 带参数的装饰器 例子
def func_arg(tmp):
print("the value is {}".format(tmp))
def zsq(func):
print("zsq")
def bb():
print("bb")
func()
return bb
return zsq
@func_arg(6666) # python执行到这里时,先执行了func_arg(tmp = 6666)
def test():
print("this is test")
test()
print('\n我是分隔符\n')
def demo():
print('this is demo')
demo = func_arg('11111')(demo)
demo()
# test的形式和demo的形式等价
the value is 6666
zsq
bb
this is test
我是分隔符
the value is 11111
zsq
bb
this is demo
类做装饰器
用类做装饰器需要在类中定义一个__call__()方法
# 类做装饰器 例子
class Demo(object):
def __init__(self, func):
print("初始化{}".format(func.__name__))
self.__func = func
def __call__(self):
print("这是类装饰器")
self.__func()
@Demo
def test():
print("test".center(20,"*"))
test()
初始化test
这是类装饰器
********test********
wraps函数
用于消除装饰器在装饰函数时改变了被装饰函数的说明文档这一缺点
# 未使用warps() 例子
import functools
print('未使用warps() 例子')
def zsq(func):
"这是一个装饰器"
# @functools.wraps(func)
def a():
'这是装饰器的内部函数'
print("这里添加了一个装饰器")
func()
return a
@zsq
def test():
"这里是一个测试函数"
print("this is a test")
test()
# 输出test()的文档
print(test.__doc__)
未使用warps() 例子
这里添加了一个装饰器
this is a test
这是装饰器的内部函数
import functools
print('\n使用warps() 例子')
def zsq(func):
"这是一个装饰器"
@functools.wraps(func)
def a():
'这是装饰器的内部函数'
print("这里添加了一个装饰器")
func()
return a
@zsq
def test():
"这里是一个测试函数"
print("this is a test")
test()
print(test.__doc__)
使用warps() 例子
这里添加了一个装饰器
this is a test
这里是一个测试函数
装饰器使用场景
'''授权(Authorization)'''
# 导入wraps防止说明文档被覆盖
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not chech_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
'''日志(Logging)'''
# 导入wraps防止说明文档被覆盖
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + 'wass called')
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
'''Do some math.'''
return x + x
result = addition_func(4)
# output: addition_funcwass called
addition_funcwass called
'''在函数中嵌入装饰器(相当于带参数的装饰器)'''
# 记录日志并输出到日志文件
from functools import wraps
def logit(logfile='./Gnerate_files/out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__+' was called'
print(log_string)
# 打开logfile,写入日志
with open(logfile, 'a+', encoding='utf8') as log_file:
# 写入日志
log_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit()
def myFunc1():
pass
@logit(logfile='./Gnerate_files/func2.log')
def myFunc2():
pass
# 测试
myFunc1()
# 输出myFunc1 was called
# 并且生成一个out.log文件,文件内容为上述字符串
myFunc2()
# 输出myFunc2 was called
# 并且生成一个out.log文件,文件内容为上述字符串
myFunc1 was called
myFunc2 was called
模块
'''
os.__file__
import os
xxx-cpython-35.pyc 表示c语言编写的Python编译器3.5版本的字节码
import Name
from name import defName,defName2
from name import * 注意:这里尽量不要使用这种方法导入;
使用这种方式导入,若存在同名函数,后导入的函数会覆盖先导入的函数
import Name as sName
__name__属性
在模块被调用时不希望调用模块中的某些功能可是使用"__name__"属性来实现
例子:
if __name__ == '__main__':
print('在本体模块内被调用')
else:
print('在其他模块内被调用')
例子结束
__all__属性
在模块被导入时的所有方法名
所有在模块被调用的方法名都存在__all__[]中
__all__属性的功能还可用dir([x])来实现
dir()函数会罗列当前定义的所有名称
并不是所有模块都有"__all__"属性
程序执行时导入模块路径:
import sys
sys.path.append('/home/itcast/xxx')
# 末尾追加路径
sys.path.insert(0, '/home/itcast/xxx')
#头部添加路径(可以确保先搜索这个路径)
'''
# 程序执行时添加路径 例子
import sys
raw_list = sys.path
sys.path.append('D:\\Documents\\Jupyter_notebook')
append_list = sys.path
sys.path.insert(0, 'D:\\Documents')
insert_list = sys.path
print('raw_list = {}\n\nappend_list = {}\n\ninsert_list = {}'.format(
raw_list,append_list,insert_list
))
raw_list = ['D:\\Documents', '', 'D:\\Documents\\Jupyter_notebook', 'C:\\MyPrograms\\Anaconda3\\python37.zip', 'C:\\MyPrograms\\Anaconda3\\DLLs', 'C:\\MyPrograms\\Anaconda3\\lib', 'C:\\MyPrograms\\Anaconda3', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\win32', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\win32\\lib', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\Pythonwin', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\IPython\\extensions', 'C:\\Users\\shj\\.ipython', 'D:\\Documents\\Jupyter_notebook']
append_list = ['D:\\Documents', '', 'D:\\Documents\\Jupyter_notebook', 'C:\\MyPrograms\\Anaconda3\\python37.zip', 'C:\\MyPrograms\\Anaconda3\\DLLs', 'C:\\MyPrograms\\Anaconda3\\lib', 'C:\\MyPrograms\\Anaconda3', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\win32', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\win32\\lib', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\Pythonwin', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\IPython\\extensions', 'C:\\Users\\shj\\.ipython', 'D:\\Documents\\Jupyter_notebook']
insert_list = ['D:\\Documents', '', 'D:\\Documents\\Jupyter_notebook', 'C:\\MyPrograms\\Anaconda3\\python37.zip', 'C:\\MyPrograms\\Anaconda3\\DLLs', 'C:\\MyPrograms\\Anaconda3\\lib', 'C:\\MyPrograms\\Anaconda3', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\win32', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\win32\\lib', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\Pythonwin', 'C:\\MyPrograms\\Anaconda3\\lib\\site-packages\\IPython\\extensions', 'C:\\Users\\shj\\.ipython', 'D:\\Documents\\Jupyter_notebook']
# __all__和dir() 比较例子
print('this is a example for __all_ and dir()')
import os
A=os.__all__
B=dir(os)
print('this is __all__:',A)
print('\n***\n'*2)
print('this is dir(): ',A)
print('\n***\n'*2)
C=list(set(A).difference(set(B)))
print('this is difference of __all__ and dir()', C)
this is a example for __all_ and dir()
this is __all__: ['altsep', 'curdir', 'pardir', 'sep', 'pathsep', 'linesep', 'defpath', 'name', 'path', 'devnull', 'SEEK_SET', 'SEEK_CUR', 'SEEK_END', 'fsencode', 'fsdecode', 'get_exec_path', 'fdopen', 'popen', 'extsep', '_exit', 'DirEntry', 'F_OK', 'O_APPEND', 'O_BINARY', 'O_CREAT', 'O_EXCL', 'O_NOINHERIT', 'O_RANDOM', 'O_RDONLY', 'O_RDWR', 'O_SEQUENTIAL', 'O_SHORT_LIVED', 'O_TEMPORARY', 'O_TEXT', 'O_TRUNC', 'O_WRONLY', 'P_DETACH', 'P_NOWAIT', 'P_NOWAITO', 'P_OVERLAY', 'P_WAIT', 'R_OK', 'TMP_MAX', 'W_OK', 'X_OK', 'abort', 'access', 'chdir', 'chmod', 'close', 'closerange', 'cpu_count', 'device_encoding', 'dup', 'dup2', 'environ', 'error', 'execv', 'execve', 'fspath', 'fstat', 'fsync', 'ftruncate', 'get_handle_inheritable', 'get_inheritable', 'get_terminal_size', 'getcwd', 'getcwdb', 'getlogin', 'getpid', 'getppid', 'isatty', 'kill', 'link', 'listdir', 'lseek', 'lstat', 'mkdir', 'open', 'pipe', 'putenv', 'read', 'readlink', 'remove', 'rename', 'replace', 'rmdir', 'scandir', 'set_handle_inheritable', 'set_inheritable', 'spawnv', 'spawnve', 'startfile', 'stat', 'stat_result', 'statvfs_result', 'strerror', 'symlink', 'system', 'terminal_size', 'times', 'times_result', 'truncate', 'umask', 'uname_result', 'unlink', 'urandom', 'utime', 'waitpid', 'write', 'makedirs', 'removedirs', 'renames', 'walk', 'execl', 'execle', 'execlp', 'execlpe', 'execvp', 'execvpe', 'getenv', 'supports_bytes_environ', 'spawnl', 'spawnle']
***
***
this is dir(): ['altsep', 'curdir', 'pardir', 'sep', 'pathsep', 'linesep', 'defpath', 'name', 'path', 'devnull', 'SEEK_SET', 'SEEK_CUR', 'SEEK_END', 'fsencode', 'fsdecode', 'get_exec_path', 'fdopen', 'popen', 'extsep', '_exit', 'DirEntry', 'F_OK', 'O_APPEND', 'O_BINARY', 'O_CREAT', 'O_EXCL', 'O_NOINHERIT', 'O_RANDOM', 'O_RDONLY', 'O_RDWR', 'O_SEQUENTIAL', 'O_SHORT_LIVED', 'O_TEMPORARY', 'O_TEXT', 'O_TRUNC', 'O_WRONLY', 'P_DETACH', 'P_NOWAIT', 'P_NOWAITO', 'P_OVERLAY', 'P_WAIT', 'R_OK', 'TMP_MAX', 'W_OK', 'X_OK', 'abort', 'access', 'chdir', 'chmod', 'close', 'closerange', 'cpu_count', 'device_encoding', 'dup', 'dup2', 'environ', 'error', 'execv', 'execve', 'fspath', 'fstat', 'fsync', 'ftruncate', 'get_handle_inheritable', 'get_inheritable', 'get_terminal_size', 'getcwd', 'getcwdb', 'getlogin', 'getpid', 'getppid', 'isatty', 'kill', 'link', 'listdir', 'lseek', 'lstat', 'mkdir', 'open', 'pipe', 'putenv', 'read', 'readlink', 'remove', 'rename', 'replace', 'rmdir', 'scandir', 'set_handle_inheritable', 'set_inheritable', 'spawnv', 'spawnve', 'startfile', 'stat', 'stat_result', 'statvfs_result', 'strerror', 'symlink', 'system', 'terminal_size', 'times', 'times_result', 'truncate', 'umask', 'uname_result', 'unlink', 'urandom', 'utime', 'waitpid', 'write', 'makedirs', 'removedirs', 'renames', 'walk', 'execl', 'execle', 'execlp', 'execlpe', 'execvp', 'execvpe', 'getenv', 'supports_bytes_environ', 'spawnl', 'spawnle']
***
***
this is difference of __all__ and dir() []
#dir() 例子
dir()
['In',
'Out',
'_',
'__',
'___',
'__builtin__',
'__builtins__',
'__doc__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'_dh',
'_i',
'_i1',
'_i10',
'_i11',
'_i12',
'_i13',
'_i14',
'_i15',
'_i16',
'_i17',
'_i18',
'_i19',
'_i2',
'_i20',
'_i3',
'_i4',
'_i5',
'_i6',
'_i7',
'_i8',
'_i9',
'_ih',
'_ii',
'_iii',
'_oh',
'a',
'add',
'alist',
'astring',
'exit',
'get_ipython',
'i',
'is_odd',
'l',
'newlist',
'numlist',
'produce',
'quit',
'reduce',
'sort_list',
'sorted_str',
'sum']
os模块
'''
os.access(path,mode)
检查权限;path:路径;mode{
os.F_OK: 作为access()的mode参数,测试path是否存在。最常用
os.R_OK: 包含在access()的mode参数中 , 测试path是否可读。
os.W_OK 包含在access()的mode参数中 , 测试path是否可写。
os.X_OK 包含在access()的mode参数中 ,测试path是否可执行。
}
os.chdir(path) 改变当前工作目录
os.chmod(path,mode) 更改权限
os.chown(path, uid, gid) 更改文件所有者
os.chroot(path) 改变当前进程的根目录
os.path.realpath(__file__) 返回真实路径
os.path.split() 返回路径的目录和文件名
os.getcwd() 得到当前工作的目录
__file__ 是用来获得模块所在的路径的
更多参照下方的help
'''
import os
dir(os)
['DirEntry',
'F_OK',
'MutableMapping',
'O_APPEND',
'O_BINARY',
'O_CREAT',
'O_EXCL',
'O_NOINHERIT',
'O_RANDOM',
'O_RDONLY',
'O_RDWR',
'O_SEQUENTIAL',
'O_SHORT_LIVED',
'O_TEMPORARY',
'O_TEXT',
'O_TRUNC',
'O_WRONLY',
'P_DETACH',
'P_NOWAIT',
'P_NOWAITO',
'P_OVERLAY',
'P_WAIT',
'PathLike',
'R_OK',
'SEEK_CUR',
'SEEK_END',
'SEEK_SET',
'TMP_MAX',
'W_OK',
'X_OK',
'_Environ',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'_execvpe',
'_exists',
'_exit',
'_fspath',
'_get_exports_list',
'_putenv',
'_unsetenv',
'_wrap_close',
'abc',
'abort',
'access',
'altsep',
'chdir',
'chmod',
'close',
'closerange',
'cpu_count',
'curdir',
'defpath',
'device_encoding',
'devnull',
'dup',
'dup2',
'environ',
'error',
'execl',
'execle',
'execlp',
'execlpe',
'execv',
'execve',
'execvp',
'execvpe',
'extsep',
'fdopen',
'fsdecode',
'fsencode',
'fspath',
'fstat',
'fsync',
'ftruncate',
'get_exec_path',
'get_handle_inheritable',
'get_inheritable',
'get_terminal_size',
'getcwd',
'getcwdb',
'getenv',
'getlogin',
'getpid',
'getppid',
'isatty',
'kill',
'linesep',
'link',
'listdir',
'lseek',
'lstat',
'makedirs',
'mkdir',
'name',
'open',
'pardir',
'path',
'pathsep',
'pipe',
'popen',
'putenv',
'read',
'readlink',
'remove',
'removedirs',
'rename',
'renames',
'replace',
'rmdir',
'scandir',
'sep',
'set_handle_inheritable',
'set_inheritable',
'spawnl',
'spawnle',
'spawnv',
'spawnve',
'st',
'startfile',
'stat',
'stat_result',
'statvfs_result',
'strerror',
'supports_bytes_environ',
'supports_dir_fd',
'supports_effective_ids',
'supports_fd',
'supports_follow_symlinks',
'symlink',
'sys',
'system',
'terminal_size',
'times',
'times_result',
'truncate',
'umask',
'uname_result',
'unlink',
'urandom',
'utime',
'waitpid',
'walk',
'write']
hashlib模块
用于生成hash(哈希)码等操作
import hashlib
m = hashlib.md5() #创建hash对象
print(m)
# 更新哈希对象以字符串参数,注意,这里需要先编码,不然只是字符串会报错
m.update(b"str")
# 返回16进制数字字符串
m.hexdigest()
<md5 HASH object @ 0x7f44ee3bb828>
'341be97d9aff90c9978347f66f945b77'
常用标准库
官方网址:
https://docs.python.org/3.7/library/
'''
builtins 内建函数默认加载
os 操作系统接口
sys python自身的运行环境
functools 常用的工具
json 编码和解码JSON对象
logging 记录日志,调试
multiprocessing 多进程
threading 多线程
copy 拷贝
time 时间
datetime 日期和时间
calendar 日历
hashlib 哈希加密算法
random 随机数
re 正则
socket 标准BSD Sockets API
shutil 文件和目录管理
glob 基于文件通配符搜索
'''
'''
深拷贝、浅拷贝
这里的深浅指的是拷贝的程度
浅拷贝:只是对同一个变量的地址引用(地址的拷贝),这里的拷贝只是用引用指向
深拷贝:这里需要导入模块【import copy ;new = copy.deepcopy(old)】这里的拷贝是新建了内存的拷贝
copy模块中的copy和deepcopy的区别:copy只copy第一层,而deepcopy是完全copy
copy拷贝的特点:自动判断>>>当copy的对象是不可变类型时,浅拷贝,当是可变类型时,copy第一层
'''
functools模块
'''
这个模块中放置相对常用的一些方法
partial()(偏函数):把一个函数的某些参数设置成默认值,返回一个新的函数,
调用这个新的函数更简单
'''
# 例子
import functools
def test(*args, **kwargs):
#注意这里的可变长参数,如果设置定长参数,会出现参数多给报错
print(args)
print(kwargs)
q = functools.partial(test, 1,2)
q()
q(666, 2333)
(1, 2)
{}
(1, 2, 666, 2333)
{}
常用扩展库
'''
requests 使用urllib3,继承了urllib2的所有特性
urllib 基于http的高层库
scrapy 爬虫
beautifulsoup4 HTML/XML的解析器
celery 分布式任务调度模块
redis 缓存
Pillow(PIL) 图像处理
xlsxwriter 仅写excle功能,支持xlsx
xlwt 仅写excel功能,支持xls,2013或更早版office
xlrd 只读excle功能
elasticsearch 全文搜索引擎
pymysql MySQL链接库
mongoengine/pymongo mongodbpython接口
matplotlib 画图
numpy/scipy 科学计算
django/tornado/flask web框架
SimpleHTTPServer 简单HTTPServer,不使用Web框架
gevent 基于协程的Python网络库
fabric 系统管理
pandas 数据处理库
scikit-learn 机器学习库
'''
包和init
'''
在一个文件夹下有py文件,有__init__.py文件称为包
包中__init__.py文件的内容应含有__all__,以及
from . import xxxName : 这里是对from * (all)以及 import 的导包方法的使用声明
如果__init__文件为空,导包会失败
模块重新导入
向导入模块中添加新的路径:sys.path.append("yourPath")
重新导入模块
from ModelName import *
reload("old_Model") # 这里的参数是要重新导入的模块
模块的循环导入问题
a模块中导入了b模块,然而在b模块中又导入了a模块(这样就出现了一个模块导入的死递归)
解决方法:再建立一个新的模块,让这两个递归的模块成为子模块,让那个新的模块调用这两个模块
'''
输入输出
'''
给程序传参:sys
import sys
print(sys.argv) #这里sys.argv是一个列表,0表示程序名,1:表示传入的参数
'''
# sys.argv 例子
import sys
print(sys.argv)
['/home/shj/WorkStations/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py', '-f', '/run/user/1000/jupyter/kernel-fc9aa604-d8c9-4367-a568-145f624bfffd.json']
输入
'''
input() 键入字符串
'''
输出
'''
repr(): 产生一个解释器易读的表达形式。
print() 打印
'''
# repr() 例子
s = '123456'
repr(s)
"'123456'"
'''
格式化输出
print('xxx %d' %(123))
print('strings{key}'.format())# {}中可以设置变量名,在format中用key=value赋值,{}中设置数字顺序在format中可以按序号给出值
'''
#format()例子
print("this is sequence:{} {} {}".format('who','am', 'i'))
print('*'*8)
print("this is no sequence:{2} {1} {0}".format('who','am', 'i'))
this is sequence:who am i
********
this is no sequence:i am who
文件
'''
file.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)
参数说明:
{
file: 必需,文件路径(相对或者绝对路径)
mode: 可选,文件打开模式
buffering: 设置缓冲
encoding: 一般使用utf8
errors: 报错级别
newline: 区分换行符
closefd: 传入的file参数类型
}
file.close() 关闭
file.flush() 刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件
file.fileno() 返回一个整型的文件描述符(file descriptor FD 整型),
可以用在如os模块的read方法等一些底层操作上。
file.isatty() 如果文件连接到一个终端设备返回 True,否则返回 False。
file.next() 返回文件下一行。
file.read([size]) 从文件读取指定的字节数,如果未给定或为负则读取所有。
file.write(str)
file.readlines([sizeint]) 读取所有行并返回列表,若给定sizeint>0,
返回总和大约为sizeint字节的行,
实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
file.readline([size]) 读取整行,包括 "\n" 字符。
file.seek(字节,pos(0开头,1中间,2结尾))
file.tell() 返回地址
file.writelines(sequence) 向文件写入一个序列字符串列表,
如果需要换行则要自己加入每行的换行符。
file.truncate([size]) 从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;
截断之后后面的所有字符被删除,其中 Widnows 系统下的换行代表2个字符大小。
OS
import os
os.rename("old","new") #重命名
os.mkdir("name") #创建dir
os.getcwd() #获取当前路径
os.chdir("newpath") #改变默认目录
os. listdir("./") #获取目录
'''
# 文件例子
import os
print(os.listdir('./'))
with open('./fileDemo.txt','w',encoding='utf-8') as f_out:
f_out.write('this is a file wirte demo.')
print("file writed!")
print(os.listdir('./'))
with open('./fileDemo.txt','r',encoding='utf-8') as f_in:
print("file read in:")
print(f_in.read())
['.ipynb_checkpoints', 'fileDemo.txt', 'Jupyter notebook笔记.ipynb', 'NoteDemo.ipynb', 'python_note_txt.ipynb', 'Python学习笔记.ipynb']
file writed!
['.ipynb_checkpoints', 'fileDemo.txt', 'Jupyter notebook笔记.ipynb', 'NoteDemo.ipynb', 'python_note_txt.ipynb', 'Python学习笔记.ipynb']
file read in:
this is a file wirte demo.
pickle模块
'''
pickle 模块
python的pickle模块实现了基本的数据序列和反序列化。
序列化操作:将程序中运行的对象信息保存到文件中去,永久存储。
反序列化操作:可从文件中创建上一次程序保存的对象。
基本接口:
pickle.dump(obj, file, [,protocol])
x = pickle.load(file)
'''
# 序列化
import pickle
# 使用pickle模块将数据对象保存到文件
data1 = {'a': [1, 2.0, 3, 4+6j],
'b': ('string', u'Unicode string'),
'c': None}
selfref_list = [1, 2, 3]
selfref_list.append(selfref_list)
output = open('./Gnerate_files/data.pkl', 'wb')
# Pickle dictionary using protocol 0.
pickle.dump(data1, output)
# Pickle the list using the highest protocol available.
pickle.dump(selfref_list, output, -1)
output.close()
#反序列化
import pprint, pickle, os
#使用pickle模块从文件中重构python对象
pkl_file = open('./Gnerate_files/data.pkl', 'rb')
data1 = pickle.load(pkl_file)
pprint.pprint(data1)
data2 = pickle.load(pkl_file)
pprint.pprint(data2)
pkl_file.close()
os.listdir('./')
{'a': [1, 2.0, 3, (4+6j)], 'b': ('string', 'Unicode string'), 'c': None}
[1, 2, 3, <Recursion on list with id=140177480230920>]
['.ipynb_checkpoints',
'f.py',
'Gnerate_files',
'images',
'multiprocessing_test',
'multiprocessing_test-复件',
'Python学习笔记.ipynb',
'reference_books']
面向对象
'''
类:
class ClassName:
pass
对代码基本相同的类抽取基类:当不同类中的方法基本相同时,
可以抽取出一个基类,用来简化代码
属性:
实例属性可以通过在__init__方法中设置,不必刻意写出
方法:
def func(self):
pass
# 这里的self相当于java中的this,这是python中写成了this
'''
魔法方法
'''相当于java中的实例属性'''
new方法
'''
__new__至少要有一个参数cls,代表要实例化的类,
此参数在实例化时由python解释器自动提供
__new__必须要有返回值,返回实例化出来的实例,
这点在自己实现__new__时要特别注意,
可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
__new__(cls):这个方法在类被引用时会被执行
(自动的,如果不被创建,类会调用默认的__new__方法)
这里的cls此时指向的是类的名字[其实类是通过父类object.__new__(cls)来生成的]
__new__方法只负责创建,__init__方法只负责初始化
__init__的self参数就是__new__返回的实例,__init__在__new__的基础上完成初始化动作,__init__不需要返回值
注意,在创建对象时最先调用__new__方法,这时向类中添加参数会报错,
所以这时需要在__new__方法中设置虚参,只是一个样子,不需要用到
'''
# 在此比较java和c++中的构造方法,构造方法其实包含了Python中的这两个方法
'''单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。'''
# 创建单例对象:不管创建多少只有一个对象
class A(object):
pass
d = A()
e = A()
print(id(d))
print(id(e))
#注意,这里的两个print的输出值并不相同
#这里不是单例模式
"""单例模式可以通过类变量来实现"""
# 这个单例模式只是一个例子,可以看出只能创建单例模式中的类属性,方法的创建就要添加参数
class A(object):
__num = None
def __new__(cls):
if cls. __num == None: # 这里判断是否为第一次实例化
cls.__num = object.__new__(cls) # 第一次实例化,进行实例化
return cls.__num
else :
# 不是第一次实例化:返回上面生成的cls.__num
return cls.__num
# 这里依据类属性在实例化对象中公用的原理,创建单例模式
'''在实例化a时,将A类的私有属性__num'''
a = A()
b = A()
print(id(a))
print(id(b))
#此时打印的a、b的id是相同的
#也就是表明此时是单例模式
139934035060440
139934035059712
139934035060216
139934035060216
init方法
用于初始化:构造函数,在生成对象时调用
# __init__方法:
class Demo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return ('your name is {name},your age is {age}'.format(name = self.name,
age = self.age))
demo = Demo('shj', 22)
print(demo)
your name is shj,your age is 22
str方法
用于获取描述信息
# __str__方法:
class Demo:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return ('your name is {name},your age is {age}'.format(name = self.name,
age = self.age))
demo = Demo('shj', 22)
print(demo)
your name is shj,your age is 22
__del__方法
析构函数,释放对象时使用
在硬链接的obj完全删除时自动调用此方法,可视为死前善后处理
其他方法
''' 这些方法都是类(Class)专有方法
__repr__ : 打印,转换
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
'''
引用计数
'''
sys模块中的getrefcount()方法
sys.getrefcount() :用于查看一个对象被引用的次数
返回的值是引用次数+1(次数=返回值-1)
'''
# 引用计数例子
from sys import getrefcount
class DD:
def __init__(self,info):
self.info = info
def __str__(self):
return 'this is {} Demo'.format(self.info)
d1 = DD('d1')
d2 = DD('d2')
print(d1)
print(d2)
print('*'*8)
count = getrefcount(DD)-1
print('the getrefcount is {}'.format(count))
this is d1 Demo
this is d2 Demo
********
the getrefcount is 6
继承
'''
class sonClass(FatherClass):
pass
世袭:子类可以继承父类(基类),也可以继承父类的父类
多继承
class sonClass(Base1, Base2, Base3):
pass
继承后对方法的查找顺序:从左至右
可用className.__mro__打印这个类可调用的类
私有方法、私有属性不能被继承
但是调用的公有方法中包含私有方法或属性,这个公有方法可以被继承
'''
# 多继承例子
#类定义
class people:
#定义基本属性
name = ''
age = 0
#定义私有属性,私有属性在类外部无法直接进行访问
__weight = 0
#定义构造方法
def __init__(self,n,a,w):
self.name = n
self.age = a
self.__weight = w
def speak(self):
print("%s 说: 我 %d 岁。" %(self.name,self.age))
#单继承示例
class student(people):
grade = ''
def __init__(self,n,a,w,g):
#调用父类的构函
people.__init__(self,n,a,w)
self.grade = g
#覆写父类的方法
def speak(self):
print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
#另一个类,多重继承之前的准备
class speaker():
topic = ''
name = ''
def __init__(self,n,t):
self.name = n
self.topic = t
def speak(self):
print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
#多重继承
class sample(speaker,student):
a =''
def __init__(self,n,a,w,g,t):
student.__init__(self,n,a,w,g)
speaker.__init__(self,n,t)
test = sample("Tim",25,80,4,"Python")
test.speak() #方法名同,默认调用的是在括号中排前地父类的方法
print('\nsample can use\n {}'.format(sample.__mro__))
我叫 Tim,我是一个演说家,我演讲的主题是 Python
sample can use
(<class '__main__.sample'>, <class '__main__.speaker'>, <class '__main__.student'>, <class '__main__.people'>, <class 'object'>)
重写
'''
子类重写父类方法和java相同
调用被重写的方法:
BaseClass.funcName()
super.funcName()
'''
'''多态:定义的方法根据传递进的方法的不同而得到不同的结果'''
# 一个多态的小例子
class F1(object):
def show(self):
print ('F1.show')
class S1(F1):
def show(self):
print ('S1.show')
class S2(F1):
def show(self):
print ('S2.show')
# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象
'''
对java或C#
def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""
print (obj.show())
'''
# python
def func(obj):
obj.show()
print("*"*8)
s1_obj = S1()
func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
s2_obj = S2()
func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
S1.show
********
S2.show
********
类属性
'''
定义在class内部,但是在方法外部的变量称为类的属性
类属性属于类,并且类属性在实例间共享
'''
实例属性
'''
和具体的某个实例对象有关,在类内部的实例属性不同实例对象共用,类外部单独创建的实例属性不共用
实例属性和类属性重名会强制覆盖类属性,但是无法通过实例属性对类属性进行更改
'''
类方法
'''
类中的方法,需要在方法前添加@classmethod
注意:类方法必须有有一个cls变量做形参,这个cls表示类本身
@classmethod
def classFunc(cls): # 这里必须要定义一个形参,名称无所谓,惯用cls,这个变量表示类本身
pass
'''
实例方法
'''
实例方法:类中定义的方法没有特殊标注都是实例方法
self 必须写
def intanceFunc(self): # self 表示实例本身
pass
'''
实例方法中self的含义
'''
self代表的是实例,不是类
'''
# 例子
class Demo(object):
def get(self):
print(self) # 打印self
print(self.__class__) # 打印self的类
demo = Demo()
demo.get()
<__main__.Demo object at 0x7f44eda2cd68>
<class '__main__.Demo'>
'''
实例方法中的self必须写,不写的话将变成类方法,
并且此类方法只能用类名调用,用实例名调用会报错,
因为少self参数,无法实例化
'''
# 例子
class Demo(object):
# 定义和调用都有self
def get(self):
print(self) # 打印self
print(self.__class__) # 打印self的类
demo = Demo()
print('实例方法中有self:')
demo.get()
print("*"*8)
class Test(object):
# 定义时无self,调用时有self
def get():
print(self)
# 异常处理
try:
test = Test()
test.get()
except Exception as result:
print('实例化方法中无self会抛出异常:')
print(result)
class Test_1(object):
# 定义和调用均无self
def get():
print('方法定义时无self,调用时也没有self')
print(__class__)
Test_1.get()
实例方法中有self:
<__main__.Demo object at 0x7f44eda10208>
<class '__main__.Demo'>
********
实例化方法中无self会抛出异常:
get() takes 0 positional arguments but 1 was given
方法定义时无self,调用时也没有self
<class '__main__.Test_1'>
'''
继承时,实例为被实例化的类的实例,不是定义了实例所调用的方法的类的实例
'''
# 例子
class People(object):
def infos(self):
str = 'i am People{}'.format(self)
return str
class Man(People):
def about(self):
str = 'I am man {}'.format(self)
return str
man = Man()
print(man.about())
print(man.infos())
print('*'*8)
people = People()
print(people.infos())
I am man <__main__.Man object at 0x7f44eda29668>
i am People<__main__.Man object at 0x7f44eda29668>
********
i am People<__main__.People object at 0x7f44ede3c0f0>
'''
在描述符类中,self指的是描述符类的实例
'''
# 例子
class Desc:
def __get__(self, ins, cls):
print('self in Desc: %s ' % self )
print(self, ins, cls)
class Test:
x = Desc()
def prt(self):
print('self in Test: %s' % self)
t = Test()
t.prt()
'''
这里调用的是t.x,也就是说是Test类的实例t的属性x,
由于实例t中并没有定义属性x,所以找到了类属性x,
而该属性是描述符属性,为Desc类的实例而有,
所以此处并没有顶用Test的任何方法。
那么我们如果直接通过类来调用属性x也可以得到相同的结果。
[把t.x改为Test.x]
'''
t.x
self in Test: <__main__.Test object at 0x7f44eda29748>
self in Desc: <__main__.Desc object at 0x7f44eda29438>
<__main__.Desc object at 0x7f44eda29438> <__main__.Test object at 0x7f44eda29748> <class '__main__.Test'>
静态方法
'''
静态方法:在类中,方法前添加@staticmethod
静态方法可以不定义参数,也可以定义参数(self在静态方法中没啥意义)
@staticmethod
def staticFunc([self]): # 静态方法的self可写可不写,静态方法相当于一个普通方法
pass
'''
#! /usr/bin/env python3
# -*- coding:utf-8 -*-
"""this is a test py flie"""
# 类属性 实例属性 类方法 实例方法 静态方法 例子
class classDemo:
# 类属性
msg = 'class msg'
# 初始化
def __init__(self, info): # 这里info是实例属性
self.info = info
# 实例方法
def about(self):
return self.info
# 类方法
@classmethod
def classFunc(cls):
msg = 'class method'
return msg
# 静态方法
@staticmethod
def staticFunc():
return 'static method'
# 声明两个实例对象 classDemo中的参数传给self.info
demo1 = classDemo('demo1')
demo2 = classDemo('demo2')
# 在实例对象外部为不同实例对象创建不同的实例属性,类外部创建的实例属性不共用
demo1.flag = 'ddd'
demo2.key = 'asdemo'
print(demo1.flag) # ddd
# 这里demo2调用了非共用实例属性,会报错
try:
print(demo2.flag)
except Exception as result:
print(result) # 'classDemo' object has no attribute 'flag'
print("*" * 8)
# info是二者共有的实例属性,不会报错
print(demo1.info) # demo1
print(demo2.info) # demo2
print("*" * 8)
# 类属性可通过实例对象调用
print('use by instance:', demo1.msg) # use by instance: class msg
# 类属性还可通过类名调用
print('use by class name: ', classDemo.msg) # use by class name: class msg
print("*" * 8)
# 类属性不能通过同名实例属性更改
demo1.msg = 'demo1 msg'
# demo1.msg=demo1 msg,class.msg=class msg
print('demo1.msg={0},class.msg={1}'.format(demo1.msg, classDemo.msg))
classDemo.msg = 'changed class msg'
# demo1.msg=demo1 msg,class.msg=changed class msg
print('demo1.msg={0},class.msg={1}'.format(demo1.msg, classDemo.msg))
print("*" * 8)
# 类方法同样可以通过类名和实例对象调用
print('use by instance:', demo1.classFunc()) # use by instance: class method
print('use by class name: ', classDemo.classFunc()) # use by class name: class method
print("*" * 8)
# 静态方法同样可通过类名和实例对象调用
print('use by instance:', demo1.staticFunc()) # use by instance: static method
print('use by class name: ', classDemo.staticFunc()) # use by class name: static method
ddd
'classDemo' object has no attribute 'flag'
********
demo1
demo2
********
use by instance: class msg
use by class name: class msg
********
demo1.msg=demo1 msg,class.msg=class msg
demo1.msg=demo1 msg,class.msg=changed class msg
********
use by instance: class method
use by class name: class method
********
use by instance: static method
use by class name: static method
动态添加属性以及方法
'''
添加属性就是简单的"."操作,但是添加方法时,需要用到模块,types
instanceName.method = types.MethodType(methodName, instance)
'''
# 动态添加方法例子
class Demo(object):
def __init__(self, name):
self.name = name
# Demo类中并没有tmp方法,现在为其添加tmp方法
# 先定义方法
def tmp(self):
print("this is a test function for {}".format(self.name))
d = Demo("666")
try:
d.tmp()
except Exception as result:
print('目前未对Demo添加tmp方法,使用tmp方法会报错')
print(result)
# 导入types模块
import types
# 用types.MethodType(func, instanceName) 添加了tmp方法
d.tmp = types.MethodType(tmp, d)
print('\nafter add a method')
d.tmp()
#这样就添加了一个方法
目前未对Demo添加tmp方法,使用tmp方法会报错
'Demo' object has no attribute 'tmp'
after add a method
this is a test function for 666
'''
types.MethodType的作用:将对象和要添加的方法绑定
这里需要特别注意:用到types.MethodType时,
只是用来添加实例对象的方法,对静态方法(staticmethod)
和类方法(classmethod)只是需要用"."操作:
className.p = p这种方式就可以直接添加
'''
# 对类添加类方法和静态方法 例子
class Demo(object):
def __init__(self, name):
self.name = name
@staticmethod
def addstaticFunc():
print("static method")
@classmethod
def addClassFunc(cls):
print("class method")
demo = Demo(666)
#添加静态方法
Demo.addstaticFunc = addstaticFunc
#添加类方法
Demo.addClassFunc = addClassFunc
#使用静态方法和类方法
Demo.addstaticFunc()
print('*'*8)
demo.addClassFunc()
static method
********
class method
解耦合
'''
对于两个类有交互的时候,可以通过中间件(函数或其他方式)来解决耦合
但是在开发中,尽量使用一个类型,即用类尽量都用类,用方法尽量都用方法
'''
# 工厂模式 解耦合 例子
class Factory(object):
def factFun(self):
return fac
class A(object):
def __int__(self):
self.factory = Factory()
def useB(self):
return use_b
class B(object):
def theBFun(self):
return theB
私有化
'''
name:公有
_name (单_):私有,from XX improt * 禁止导入,类和子类可以访问
__name (双_):私有,避免和子类发生冲突,在外部无法访问
__name__(双_)私有,特有的魔法方法或属性,个人最好不要创建
name_:用于区别与关键字重复
只要开头有_,基本from xx import * 就无法导入,但是只是导入模块,还是可以用的
name mangLing(名字重整)为了防止对私有属性或方法的使用(访问时可以_ClassName.__name)
'''
property的使用
'''
这里需要建立像Java中的setter和getter方法
property通过setter方法和getter方法,
将本不可在外部通过 对象.属性 的调用方式成功实现了
'''
# 例子
class Test(object):
def __init__(self):
self.__num = 0
def getNum(self):
return self.__num
def setNum(self,newNum):
self.__num = newNum
new = property(getNum, setNum)
t = Test()
print('实例未对私有属性赋值:',t.new)
t.new = 666 # 实例对私有属性赋值
print('实例对私有属性赋值后:',t.new)
print('用getter方法获取私有属性的值:',t.getNum())
实例未对私有属性赋值: 0
实例对私有属性赋值后: 666
用getter方法获取私有属性的值: 666
'''
property也可以用装饰器的方法调用
'''
# 例子
class Test(object):
def __init__(self):
self.__num = 0
@property
def num(self):
"""getter"""
return self.__num
@num.setter
def num(self, newNum):
"""setter"""
self.__num = newNum
# 这里就不用写property(getter,setter)方法了
t = Test()
t.num
print('实例未对私有属性赋值:',t.num)
t.num = 666 # 实例对私有属性赋值
print('实例对私有属性赋值后:',t.num)
实例未对私有属性赋值: 0
实例对私有属性赋值后: 666
__slots__的作用
通过在类中用__slots__来限制该类实例能添加的属性
'''
__slots__只对当前类的属性有限制作用,对子类并没有作用
'''
# __slots__限制实例属性的例子
class Person(object):
__slots__ = ('name', 'age')
p = Person()
p.name = 'jack Ma'
p.age = 50
# 这里添加了不允许的实例属性
p.qq = 123456
---------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-98-136e7cfed380> in <module>
13
14 # 这里添加了不允许的实例属性
---> 15 p.qq = 123456
AttributeError: 'Person' object has no attribute 'qq'
元类
'''
类也是对象,所以也可以在运行时动态的创建
'''
# 动态创建类 例子 这是一个半动态的例子
def choose_classs(name):
if name == 'foo':
class Foo(object):
pass
return Foo
else:
class Lol:
pass
return Lol
myClass = choose_classs('foo')
print(myClass) # 函数返回类
print(myClass()) # 可以通过这个类创建实例
<class '__main__.choose_classs.<locals>.Foo'>
<__main__.choose_classs.<locals>.Foo object at 0x7f44ed9db6d8>
'''
还可以用type来创建类
type除了可以用来查看一个变量的属性,同样也可以用来创建类
type("classname", 由父类名组成的tuple(), 由属性组成的dict{})#注意:这里这种方法只是用来保证版本的兼容性,不建议用
注意:这里的type就相当于一个元类
'''
# type动态创建类
Demo = type('Demo', (object,), {'dd':'dd'})
print(Demo())
print(Demo.dd)
<__main__.Demo object at 0x7f44ee2ce710>
dd
__metaclass__属性
用于指定创建class的类
'''
__metaclass__ = FatherName(这里也可以是一坨代码)
#这里是用于定义类的生成方式这里系统默认会指定,
但是自己写过后会自动调用自己的
'''
# 自定义类的生成方式
def upper_attr(future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
#调用type来创建一个类
return type(future_class_name, future_class_parents, newAttr)
# python3 用法
class Foo(object, metaclass=upper_attr):
bar = 'bip'
# 在Python2中用如下操作:
# class NewClass(object):
# __metaclass__ = DiyClass
# pass
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
False
True
bip
'''自定义类生成方式完整版'''
# 完整版
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
# return type(future_class_name, future_class_parents, newAttr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, future_class_name, future_class_parents, newAttr)
# 方法3:使用super方法
return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)
#python2的用法
# class Foo(object):
# __metaclass__ = UpperAttrMetaClass
# bar = 'bip'
# python3的用法
class Foo(object, metaclass = UpperAttrMetaClass):
bar = 'bip'
print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True
f = Foo()
print(f.BAR)
# 输出:'bip'
False
True
bip
内建属性
'''
__init__:创建实例后,赋值时使用,在__new__后
__new__:新建实例
__class__:实例.__class__
__str__:print()如果没有,使用repr结果,美化版repr
__repr__:类实例 回车 或者print(类实例),打印的结果是程序的表达方式
__del__:删除实例
__dict__:vars(实例.__dict__)
__doc__:help(类或实例)
__bases__:类名.__bases__
__getattribute__:访问实例属性时,属性拦截器,会在所有属性使用之前调用
def __getattribute__(self, obj):
pass
'''
# __getattribute__ 例子
class Demo(object):
def __init__(self, tmp1):
self.tmp1 = tmp1
self.tmp2 = "shj"
def __getattribute__(self, obj):
print("this obj is {}".format(obj).center(20, "*"))
if obj == "":
print("the input is {}".format(self.obj))
else:
tmp = object.__getattribute__(self, obj)
print("this is not if %s" %tmp)
return tmp
def test(self):
print("this is the function named test")
s = Demo("666")
print('test START')
print(s.tmp1)
print('000'*8)
print(s.tmp2)
s.test()
# 注意这里:这里调用的test方法是显示的一个绑定
# this is not if <bound method Demo.test of <__main__.Demo object at 0x7f2f524ab9e8>>
#这里说明,类中的方法同样作为一个属性
# this is the function named test
#通过这两个注意可以看到,类中的方法其实是不存在的,
# 而是通过创建一个属性,通过对属性绑定方法来达到创建方法的操作
test START
**this obj is tmp1**
this is not if 666
666
000000000000000000000000
**this obj is tmp2**
this is not if shj
shj
**this obj is test**
this is not if <bound method Demo.test of <__main__.Demo object at 0x7f44ede34a90>>
this is the function named test
getattribute的坑
class Demo(object):
def __getattribute__(self, obj):
print("这是一个关于attribute属性的坑")
if obj.startswith("a"):
return "666"
else:
return self.test
def test(self):
print("this is the function test")
d = Demo()
print(d.a)
# print(d.b)
#会让程序死掉
#原因是:当d.b执行时,会调用PDemo类中定义的__getattribute__方法,但是在这个方法的执行过程中
#if条件不满足,所以 程序执行else里面的代码,即return self.test 问题就在这,因为return 需要把
#self.test的值返回,那么首先要获取self.test的值,因为self此时就是d这个对象,所以self.test就是
#d.test 此时要获取d这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产
#生了递归调用,由于这个递归过程中 没有判断什么时候推出,所以这个程序会永无休止的运行下去,又因为
#每次调用函数,就需要保存一些数据,那么随着调用的次数越来越多,最终内存吃光,所以程序 崩溃
#
# 注意:以后不要在__getattribute__方法中调用self.xxxx
这是一个关于attribute属性的坑
666
描述符
参考网址:
https://www.cnblogs.com/Lynnblog/p/9033455.html
https://blog.csdn.net/zhaoyun_zzz/article/details/82179481
描述符定义
'''
把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符,
描述符的本质是新式类,并且被代理的类(即应用描述符的类)也是新式类。
描述符的作用是用来代理一个类的属性,
需要注意的是描述符不能定义在类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例。
'''
描述符种类及优先级
'''
种类:
描述符分为数据描述符和非数据描述符。
数据描述符:至少实现了__get__()和__set__()
非数据描述符:没有实现__set__()
分类的原因是因为二者具有严格的优先级
获取一个属性的时候:
首先,看这个属性是不是一个数据描述符,
如果是,就直接执行描述符的__get__(),并返回值。
其次,如果这个属性不是数据描述符,那就按常规去从__dict__里面取。
如果__dict__里面还没有,但这是一个非数据描述符,
则执行非数据描述符的__get__()方法,并返回。
最后,找不到的属性触发__getattr__()执行
而设置一个属性的值时,访问的顺序又有所不同
优先级:
类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()
__get__(self, instance, owner):调用一个属性时,触发
__set__(self, instance, value):为一个属性赋值时,触发
__delete__(self, instance):采用del删除属性时,触发
参数解释:
[
self: 是定义描述符的对象,不是使用描述符类的对象
instance: 这才是使用描述符类的对象
owner: 是instance的类(定义使用描述符的实例的类)
value: 是instance的值(使用描述符的实例的值)
]
'''
# 对参数解释的例子
class Desc(object):
def __get__(self, instance, owner):
print("__get__...")
print("self : \t", self) # self : <__main__.Desc object at 0x7f957c069470>
print("instance : \t", instance) # instance : <__main__.TestDesc object at 0x7f957c069400>
print("owner : \t", owner) # owner : <class '__main__.TestDesc'>
print('='*40, "\n")
def __set__(self, instance, value):
print('__set__...')
print("self : \t\t", self)
print("instance : \t", instance)
print("value : \t", value)
print('='*40, "\n")
class TestDesc(object):
x = Desc()
#以下为测试代码
t = TestDesc()
t.x
__get__...
self : <__main__.Desc object at 0x7f957c069470>
instance : <__main__.TestDesc object at 0x7f957c069400>
owner : <class '__main__.TestDesc'>
========================================
# 描述符例子
class Descriptors(object):
'''数据描述符'''
def __init__(self, key, key_type):
'''
key: 用户传入的值
key_type: 用户传入的key的数据类型
'''
self.key = key
self.key_type = key_type
def __get__(self, instance, owner):
return instance.__dict__[self.key] # 将参数存入实例的字典
def __set__(self, instance, value):
# 如果用户输入的值(key)和值的类型(key_type)的类型不一致,抛出TypeError
if not isinstance(value, self.key_type):
raise TypeError('参数{key}必须为{key_type}'.format(key = self.key, key_type = self.key_type))
instance.__dict__[self.key] = value # 为实例字典添的key设值
def __delete__(self, instance):
instance.__dict__.pop(self.key) # 删除实例字典的key
class Light:
# 使用描述符
name = Descriptors('name',str)
price = Descriptors('price', float)
def __init__(self, name, price):
self.name = name
self.price = price
# 设置两个参数,触发两次set执行
light = Light('MAC',12345)
print(light.__dict__)
light.name = 'IPhone'
print(light.__dict__)
{'name': 'MAC', 'price': 12345}
{'name': 'IPhone', 'price': 12345}
类属性>数据描述符
class Descriptors(object):
'''数据描述符'''
def __get__(self, instance, owner):
print('执行Descriptors的__get__()')
def __set__(self, instance, value):
print('执行Descriptors的__set__()')
def __delete__(self, instance):
print('执行Descriptors的__delete__()')
class DoDesc(object):
# 使用描述符
useDesc = Descriptors()
# test
DoDesc.useDesc # 执行描述符的get内置属性
print(DoDesc.__dict__) # 此时name显示的是描述符的对象
DoDesc.useDesc = 'TEST' # 未执行set内置属性
print(DoDesc.useDesc) # TEST
del DoDesc.useDesc # 未执行delete内置属性
# print(DoDesc.useDesc) # 报错,因为属性被删除了,无法找到
执行Descriptors的__get__()
{'__module__': '__main__', 'useDesc': <__main__.Descriptors object at 0x7f957c0424a8>, '__dict__': <attribute '__dict__' of 'DoDesc' objects>, '__weakref__': <attribute '__weakref__' of 'DoDesc' objects>, '__doc__': None}
TEST
数据描述符>实例属性
数据描述符的优先级大于实例属性的优先级,
此时实例属性name被数据描述符所覆盖,
而price没有描述符代理,所以它仍然是实例属性。
class Descriptors:
"""
数据描述符
"""
def __get__(self, instance, owner):
print("执行Descriptors的get")
def __set__(self, instance, value):
print("执行Descriptors的set")
def __delete__(self, instance):
print("执行Descriptors的delete")
class Light:
#使用描述符
name = Descriptors()
def __init__(self, name, price):
self.name = name
self.price = price
#使用类的实例对象来测试
light = Light("电灯泡",60) #执行描述符的set内置属性
light.name #执行描述符的get内置属性
print(light.__dict__) #查看实例的字典,不存在name
print(Light.__dict__) #查看类的字典,存在name(为描述符的对象)
del light.name #执行描述符的delete内置属性
执行Descriptors的set
执行Descriptors的get
{'price': 60}
{'__module__': '__main__', 'name': <__main__.Descriptors object at 0x7f957c05a0b8>, '__init__': <function Light.__init__ at 0x7f957572a1e0>, '__dict__': <attribute '__dict__' of 'Light' objects>, '__weakref__': <attribute '__weakref__' of 'Light' objects>, '__doc__': None}
执行Descriptors的delete
实例属性>非数据描述符
实例属性中使用了非数据描述符,就不能对其进行复制操作。
可见非数据描述符应该应用于不需要设置值的属性或者函数中。
class Descriptors:
"""
非数据描述符
"""
def __get__(self, instance, owner):
print("执行Descriptors的set")
def __delete__(self, instance):
print("执行Descriptors的delete")
class Light:
#使用描述符
name = Descriptors()
def __init__(self, name, price):
self.name = name
self.price = price
#测试
light = Light("电灯泡",60) #报错,描述符中没有__set__()方法
---------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-28-437b7a47917d> in <module>
18
19 #测试
---> 20 light = Light("电灯泡",60) #报错,描述符中没有__set__()方法
<ipython-input-28-437b7a47917d> in __init__(self, name, price)
14
15 def __init__(self, name, price):
---> 16 self.name = name
17 self.price = price
18
AttributeError: __set__
'''
在该类中并没有set方法,所以该类是一个非数据描述符,
Python中一切皆对象,函数也是一个对象,
既然是对象那也可以是类实例化所得到的结果。
函数在类中本身也是一种属性(函数属性),
描述符在应用的时候也是被实例化为一个属性。
'''
class Descriptors:
"""
非数据描述符
"""
def func(self):
print("世界的变化真快!近日00后都已经开始在街头打小三了")
d = Descriptors()
d.func()
print(hasattr(Descriptors.func,"__set__")) #False
print(hasattr(Descriptors.func,"__get__")) #True
print(hasattr(Descriptors.func,"__delete__")) #False
d.func = "函数也是属性,也可以赋值,没毛病"
print('print: ',d.func)
del d.func
d.func
世界的变化真快!近日00后都已经开始在街头打小三了
False
True
False
print: 函数也是属性,也可以赋值,没毛病
<bound method Descriptors.func of <__main__.Descriptors object at 0x7f957c0247f0>>
描述符使用注意点
'''
描述符必须定义在类层次上,否则Python无法自动调用设置的__get__()和__set__()
访问类层次上的描述符可以自动调用__get__(),但是访问实例层次上的描述符只会返回描述符本身
描述符在所有的实例间共享数据(描述符是类属性的一种)
'''
# 使用注意点 例子
class Desc(object):
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
print("__get__...")
print('name = ', self.name)
print('=' * 40, "\n")
class TestDesc(object):
x = Desc('x')
def __init__(self):
self.y = Desc('y')
# 以下为测试代码
t = TestDesc()
t.x # 相当于默认值
print(t.__dict__)
print(t.y) # 在__init__中对描述符有了新的赋值
__get__...
name = x
========================================
{'y': <__main__.Desc object at 0x7f957c024860>}
<__main__.Desc object at 0x7f957c024860>