本基础知识整理参考,廖雪峰老师的Python3教程整理,点击后面的链接访问老师的教程
廖雪峰老师的Python3教程
如果发现错误之处,请及时联系本人。感谢~ 邮箱:[email protected]
数据类型和变量
变量
变量由变量名
,赋值符号
,变量值
组成,例如: n = 1
在Python中数据又称为对象,每创建一个对象都会创建对象的id,type,值
使用id()
查看变量的内存地址,使用type
查看变量的类型
身份比较:
# 身份比较比较的是id值
>>> x = 300
>>> id(x)
>>> y = 300
>>> id(y)
>>> x is y
False
>>>
值比较:
# 值比较的是两个变量的值是否相同
>>> x = 300
>>> y = 300
>>> id(x)
54002288
>>> id(y)
54002352
>>> x == y
True
>>>
类型比较
# 类型比较的是两个变量的类型
>>> x = 300
>>> y = 300
>>> type(x)
>>> type(y)
>>> type(x) is type(y)
True
>>>
变量赋值操作补充
# x = 1 # 就是一个赋值操作 没有任何返回值
# 链式赋值:
# a=b=c=10
# 多元赋值(变量值交换):
# x,y=y,x
>>> x = 1
>>> y = 2
>>> x,y = y,x
>>> x
2
>>> y
1
>>>
# 解压序列类型:
# s = 'hello'
# a,b,c,d,e=s
# 只打印出 对应的 a 和 e的值,使用'_'来填充中间的变量, '_'约定俗称代表丢弃的变量
# a,_,_,_,e = s
# a,*_,e # 使用*_ 代表中间全部
数据类型分类
可变或者不可变的定义:在id和type不变的情况下,值可变的数据是可变类型.id,type,value都不可变的是不可变类型
- 可变和不可变
- 可变:列表,字典,集合
- 不可变:数字,字符串,元组
- 存放数值的个数
- 单个值:数字,字符串
- 多个值:列表,元组,字典,集合
- 取值方式
- 直接取值: 数字
- 下标取值: 字符串,列表,元组
- 按key取值: 字典
- 特殊的取值方式: 集合
整数
Python可以处理任意大小的整数,包括负整数,在程序中表示的方式和数学上的是一样的。
由于计算机使用的是二进制,有时候使用十六进制表示整数比较方便,十六进制使用0x
为前缀和0-9
,a-z
表示。例如:0xff00
浮点数
浮点数就是小数,在Python中整数运算永远是精确的,但是浮点数则可能出现四舍五入的情况。
字符串
字符串在Python中以单引号或者双引号引起来的字符,例如: '1'
,'Python语言'
,"中文"
。如果,字符串含有'
或者"
则可以使用\
来进行转义。例如:'I\'m \"OK\"!'
。如果转义很多可以使用r'字符串'
,被r
包括的字符串默认不会转义
字符编码
Unicode编码可以转化为任意的编码,计算机内存统一使用的就是Unicode编码
如果想把UTF8编码的字符转成GBK编码的字符,则需要UTF8--->Unicode--->GBK
这个顺序转
硬盘上存储的都是字节
Python3版本中,字符都是以Unicode编码的,也就是说Python3是支持多语言的.
Python3内置函数ord()
和chr()
分别可以把字符转为ASCII
码和转回字符串
转换编码使用encode
和decode
,前者编码,后者解码
Python文件中开头的两行分别是指定Python解释器,这个py文件是以什么编码方式读取
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
Python字符串占位符
如果不确定使用什么占位符,那么可以使用使用%s代替,换句话说%s有点像万能的占位符
占位符 | 替换内容 |
---|---|
%d | 整数 |
%f | 浮点数 |
%s | 字符串 |
%x | 十六进制整数 |
list
Python内置的一种列表类型的数据类型,列表是有序的并且是可变的,可以随时删除或者添加其中的数据
特性: 列表是有序的,列表中的元素可以是任意类型的数据
常用列表操作
取值: 通过索引取值
>>> l = [1,2,3]
>>> l[0]
1
>>> l[2]
3
>>>
切片: 通过索引范围进行切片
l = [1, 2, 3, 4, 5, 67, 7, 8]
print(l[:1]) # [1]
print(l[1:4]) # [2, 3, 4]
添加值: append
从列表右边添加
l = [1, 2, 3]
print(l.append(4)) # None
print(l) # [1, 2, 3, 4]
插入值: insert
需要传入插入的位置
l = [1, 2, 3]
print(l.insert(0,7)) # None
print(l) # [7, 1, 2, 3]
删除值: pop
默认删除右边最后一个值,可以通过传入索引来删除某个值
l = [1, 2, 3]
print(l.pop()) # 3 返回被删除的值
print(l) # [1, 2]
l.pop(1)
print(l) # [1]
获取索引值: index
传入字符串,获取该字符串在列表中的第一个索引位置
l = [1, 2, 3, 1]
print(l.index(1)) # 0 列表中两个 1 只返回找到的第一个值得索引
扩展列表: extend
无法扩展元组元素或者字典元素
l = [1, 2, 3]
l2 = ['中', '文']
print(l.extend(l2)) # None
print(l) # [1, 2, 3, '中', '文']
移除元素: remove
通过传入元素的名来移除对应的元素,这里跟pop
区分,pop
接收的是索引
l = [1, 2, 3,'中', '文']
print(l.remove('中')) # None
print(l) # [1, 2, 3, '文']
tuple
特性:元组内的所有元素不能更改,元素可以是任意数据类型,元组也是有序的
元组中可以包含list,元组中的list中的元素是可以更改的.当然list中也可以有元组.
元组常用操作
元组操作基本同list是一致的,但是没有任何修改的方法,例如:insert
, pop
, remove
, extend
等修改数据的方法
dict
Python内置的字典数据类型,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。
字典的key必须是不可变类型,或者说必须是可hash类型.value的值是可以改变的,可以是任意类型的数据.字典的取值是无序的.无法通过索引取值
字典常用操作
取值: 通过字典的key
取值, 如果key
不存在会抛出错误
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d[1]) # a
print(d['b']) # 2
print(d[5]) # {'a': 1}
不报错取值方式: get
取值如果key
不存在不会报错,会返回None
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.get(77)) # None
print(d.get(4)) # (7, 6, 2)
循环: 普通的方式循环字典,只会取出字典的key
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
for x in d:
print(x) # 1 b 3 4 5
以元组形式显示: items()
for 循环时取出的是元组形式的数值
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
for x in d.items():
print(x)
# (1, 'a')
# ('b', 2)
# (3, [1, 2, 3])
# (4, (7, 6, 2))
# (5, {'a': 1})
指定取key
或者value
: .keys
和.values
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.keys()) # dict_keys([1, 'b', 3, 4, 5])
print(d.values()) # dict_values(['a', 2, [1, 2, 3], (7, 6, 2), {'a': 1}])
删除字典键值对: pop
传入key
删除对应的key和value
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.pop('b')) # 2 返回删除的值
print(d) # {1: 'a', 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
随机删除一组数据: popitem()
d = {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2), 5: {'a': 1}}
print(d.popitem()) # (5, {'a': 1})
print(d) # {1: 'a', 'b': 2, 3: [1, 2, 3], 4: (7, 6, 2)}
添加键值对: setdefault()
传入key
和value
如果key已存在则不尽心任何操作
d = {1: 'a', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}}
print(d.setdefault('test',1)) # 1 返回新添加的 value
print(d) # {1: 'a', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}, 'test': 1}
更新key
的value
,如果key
不存在会建立该键值对
d = {1: 'a', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}}
d[1] = 'abc'
print(d) # {1: 'abc', 'b': 2, 4: (7, 6, 2), 5: {'a': 1}}
字典格式化: .fromkeys()
传入key和value,前面所有的key都等于同一个value
d={}.fromkeys(['name','age'],None)
print(d) # {'name': None, 'age': None}
set
特性:集合内的元素必须是唯一的,集合内的元素必须是可hash的,也就是不可变类型,集合是无序的
集合运算:
python_s = {'egon', 'alex', 'abc', 'wang'}
linux_s = {'alex', 'abc', 'aaa', 'bbb'}
print(python_s.intersection(linux_s)) # 交集:python_s & linux_s
print(python_s.union(linux_s)) # 并集: python_s | linux_s
print(python_s.difference(linux_s)) # 差集: python_s-linux_s
print(python_s.symmetric_difference(linux_s)) # 对称差集: python_s ^ linux_s
更新: updata
如果单独传入字符串会被拆开,如下代码示例
s1={'a',1}
s2={'a','b',2,'中文'}
s1.update(s2)
print(s1) # {1, 2, '中文', 'b', 'a'}
s1.update('字符串')
print(s1) # {'字', 1, 2, '串', '中文', 'b', 'a', '符'}
添加: add
添加一个元素给集合
s1 = {'a', 1}
print(s1.add('asd')) # None
print(s1) # {1, 'asd', 'a'}
删除元素:discard()
,remove()
前者删除不存在元素不会报错,后者会,都是传入元素名
s1 = {'a', 1}
print(s1.discard('a')) # None
print(s1) # {1}
print(s1.remove(1)) # None
print(s1) # set()
其他补充:
s.issubset() # 判断是否是子集
s.issuperset() # 判断是否是父集
s.isdisjoint() # 没有交集返回True
函数
函数返回值
- 通常有参函数是需要返回值的。
- 返回值的几种形式:
- 如果函数内没有return就返回None
- return 返回的数据类型没有限制,也可以返回多个值,以元组形式,括号可加可不加
- return的效果:
- 终止函数的执行,只能返回一次值
函数参数
Python是一种弱类型的语言,参数可以传入任何类型数据
形参与实参:可以理解为形参就是变量名,而实参就是变量值
def foo(x,y) # x和y是形参
return x+y
# 执行
foo(1,2) # 1,2 是实参
位置参数:按照从左到右的顺序依次定义的参数叫位置参数。
def foo(x,y): # 按位置定义的形参,必须被传值,多一个少一个都不行。
print(x)
print(y)
foo(1,2) # 按位置定义的实参,会一一跟形参对应。
关键字参数:指的是实参在定义时,按照key=value形式定义。
def foo(x,y): #形参还是按位置定义的。
print(x)
print(y)
foo(y=1,x=2) #打破位置的限制,不需要跟形参一一对应,直接指定传值。但是还要遵循不能多不能少。
- 注意的问题:
- 位置实参可以和关键字实参混合使用,但是位置实参必须在关键字实参的前面。
- 位置实参可以和关键字实参混合使用,但是一个形参只能被传值一次不能被重复传值。
默认参数:定义函数阶段,就已经为形参赋值。
def foo(x, y=10): # 形参在定义的时候赋值。
print(x)
print(y)
foo(x=2) # 实参可以不输入对应的形参,如果输入了就不使用默认参数。
- 注意的问题:
- 必须放在位置参数后面。
- 默认参数通常要定义成不可变类型。
- 默认参数只在定义时被赋值一次。
形参的应用:值经常变化的需要定义成位置形参,值大多数情况下都一样需要定义默认参数。
实参的应用:怎么爽怎么用,但是要遵循相应的规则。
可变长参数:可变长值得是实参的个数不固定 *号等同位置参数
按位置定义的可变长实参:*
按关键字定义的可变长实参:**
def func(x,y,*args): # x=1 y=2, args=(3,4,5,6)元组形式
print(x,y) # 1 2
print(args) # (3, 4, 5, 6)
# 执行代码
func(1,2,3,4,5,6) # 实参为位置参数使用*args
func(1,2,*(3,4,5,6)) # 等同于 func(1,2,3,4,5,6)
def func(x, y, **kwargs): # x=1 y=2, kwargs={‘z’:6} 字典形式
print(x, y) # 2 4
print(kwargs) # {'z': 6}
# 执行代码
func(x=2, y=4, z=6) # 实参为关键字使用**kwargs
命名关键字参数:*后面定义的形参(*,name,age)必须以关键字形式被传值
def regist(*,name,age):
print(name)
print(age)
# 执行代码
regist(name='abc',age=11)
形参:位置参数,默认参数,*.args 命名关键字参数,**kwargs #一般定义形参就按照这种顺序
名称空间与作用域
命名空间:存放名字与值的绑定关系
- 种类:
- 内置名称空间
- 内置在Python解释器内的自带的名字,解释器一启动就生成这个空间
- 局部名称空间
- 定义在函数内部的名字
- 局部名称空间只有在调用函数的时候生效,执行完毕失效
- 全局名称空间
- 文件级别定义的名字都会存放于全局名称空间,执行python文件时会产生
- 内置名称空间
加载顺序:内置=>全局=>局部
取值顺序:局部=>全局=>内置
# 全局变量示例
def func():
pass
class foo:
pass
import os
if 1 > 2:
y = 3 # 这里y没有顶头写但也是全局变量
# 局部变量示例
max = 10
def tmp():
max = 20
print(max)
tmp() # 会打印出20 因为局部优先于全局
作用域:作用范围
- 种类:
- 全局作用域
- 内置名称空间和全局名称空间的名字,作用范围是全局范围。在整个文件的任意位置都能被引用,全局有效。
- 局部作用域
- 局部名称空间中的名字,作用范围是局部范围生效。只在函数内部可被引用,局部有效
- 全局作用域
作用域搜寻顺序: 局部作用域=>全局作用域
函数的作用域在定义的时候就已经固定死了。
补充:
搜寻变量时,只能由“内”看“外”。不可以反过来。例如在函数内部定义的变量,无法在全局被直接引用。
但是如果在函数内部引用一个变量,但是函数内没有定义,那么会去全局作用域搜寻变量。
x = 1
def func():
global x # 声明接下来声明的变量x为全局变量
nonlocal x # 声明接下来变量x为局部变量(只在函数内部寻找,找不到则会报错)
x =2
print(globals()) # 打印全局作用域name
print(locals()) # 打印局部作用域name
# 执行代码
func()
函数对象:函数是第一类对象,指的是函数可以被当数据传递,可以被引用,可以当函数的返回值,可以当容器的元素
闭包
闭包就是在函数内部定义的函数,包含对外部作用域的引用,但不包含全局作用域。因为函数的作用域在定义的时候就固定死了,所以闭包函数有自带作用域和延迟计算的特点。
闭包函数定义:如果一个内部函数,包含了对外部作用域的引用,但是不是包含全局作用域。那么这个函数就被认为是闭包函数。闭包函数可以使用“.closure” 来查看闭包函数的属性。下面我们来看一个示例:
def t():
money = 100
def s():
print(money)
return s # 返回函数s,不是函数执行的结果
c = t()
c()
print(c.__closure__) #查看属性
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-7/day7_笔记.py
# (,)
# Process finished with exit code 0
|
函数s就是在函数t中定义的内部函数.
函数s引用了一个外部的变量money,但是不是全局变量。则函数s就是一个闭包函数。
装饰器
装饰器本质可以是任意可调用对象,被装饰的对象也可以是任意可调用对象。
-
装饰器功能:
- 在不修改被装饰对象源代码以及调用方式的前提下,为其添加新的功能。
-
装饰器的语法:
- 在被装饰对象的正上方的单独一行,@装饰器名字。会把正下方的函数名调用装饰器,处理完返回给函数名。
- 多个装饰器:谁在上面谁先执行,谁在下面谁先计算。
import time # 导入模块
import random
def timmer(func): # 装饰器模块
def wrapper(): # 定义一个闭包函数wrapper
stime = time.time() # 闭包函数内 计算index的睡眠时间 ,这里是开始计算
func() # 执行index的函数内容
stptime = time.time() # index的函数执行完毕后的停止时间
print('run time is %s'%(stptime-stime)) # 打印运行了多久
return wrapper()
def index(): # 定义函数index
time.sleep(random.randrange(1,2)) # 函数内执行睡一定时间,然后打印欢迎信息
print('welecome to index page')
index = timmer(index) # 调用装饰器
# 运行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-7/tmp.py
# welecome to index page
# run time is 1.0000858306884766
# Process finished with exit code 0
上面这个是最简单的装饰器示例。如果我们还要传参,让用户感觉不出装饰器的怎么办?我们来看下面的。
import time
import random
# 装饰器
def timmer(func):
def wrapper(*args,**kwargs): # 接受可变长参数
start_time = time.time()
res=func(*args,**kwargs) # 接收保存对应函数 的return 返回值
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
return res # 执行完毕 return 函数
return wrapper
# 被装饰函数
timmer # 使用@方式调用装饰器
def index():
time.sleep(random.randrange(1,5))
print('welecome to index page')
@(知识梳理笔记)timmer
def home(name): # 需求一个传入参数
time.sleep(random.randrange(1,3))
print('welecome to %s HOME page' %name)
return 123123123123123123123123123123123123123123 #return 有返回值
index() #调用 无参数传入
print(home('abc')) # 调用传入参数‘abc’
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-7/tmp.py
# welecome to index page
# run time is 2.0000545978546143
# welecome to abc HOME page
# run time is 2.0000154972076416
# 123123123123123123123123123123123123123123
# Process finished with exit code 0
wraps模块
让函数保留原来的说明信息。
在闭包函数上面 @wraps 可以把原代码的解释,引用到装饰器中,让用户彻底无法感知装饰器的存在
使用 func.doc 和 print(help(func))来查看函数的注释信息
from functools import wraps
import time
def coutime(func):
@wraps(func) # 调用wraps模块
def wrapper(*args,**kwargs):
start = time.time()
ret = func(*args,*kwargs)
stop = time.time()
print('run time is %s'%(stop - start))
return ret
return wrapper
@coutime
def timerand(name,age):
'askdnalskdjlaksjdlasjdasd'
time.sleep(random.randrange(1,5))
print('Hello %s age:%s'%(name,age))
help(timerand) # 使用help查看函数的注释说明
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-8/day8_笔记.py
# Help on function timerand in module __main__:
# timerand(name, age)
# askdnalskdjlaksjdlasjdasd # 完全隐藏
# Process finished with exit code 0
有参装饰器
装饰器加括号表示执行,会执行外层函数,然后把函数对象传入内层接收
类似加壳(写代码的时候),脱壳(执行装饰器)的过程
def high(auth_path='file'): # 接受参数
def big(func):
def up(*args, **kwargs):
if auth_path == 'file':
func('file')
elif auth_path == 'ldap':
func('ldap')
elif auth_path == 'mysql':
func('mysql')
return up
return big
@high(auth_path='ldap') # 调用装饰器传入参数
def pr(*args):
print('hello %s' % args)
# 执行代码
pr() # hello ldap
迭代
点击这里>>>完全理解迭代对象,迭代器,生成器
重复执行
下一次的重复执行依赖于上一次执行的结果
以下代码只满足重复执行,每下一次执行都是从头开始执行的,所以不属于迭代。
while True:
cmd=input('>>: ')
print(cmd)
以下代码满足重复执行,而且每下一次执行都是从上一次执行后的结果开始执行的,属于迭代
l=['a','b','c','d']
count=0
while count < len(l):
print(l[count])
count+=1
迭代器
python为了提供一种不依赖于索引的迭代方式,会为一些对象内置
__iter__
方法,obj.__iter__
称为可迭代的对象。
字符串、元组、列表、集合、字典、文件等都是可迭代对象,迭代的过程中都能够用到迭代器。
s1='hello'
l=[1,2,3]
t=(1,2,3)
set1={1,2,3}
d={'a':1,'b':2,'c':3}
f=open('db.txt',encoding='utf-8')
迭代器就是obj.__iter__()
得到的一个结果,每次使用obj.__next__()
都能够获得一个迭代后的结果,当所有的元素都迭代后,会抛出StopIteration
的错误提示表示没有内容可以继续迭代了。
- 迭代器的优缺点:
- 迭代器的优点
- 提供了一种不依赖于索引的取值方式
- 惰性计算。节省内存
- 迭代器的缺点:
- 取值不如按照索引取值方便
- 一次性的。只能往后走不能往前退
- 无法获取长度
- 迭代器的优点
可迭代对象执行的结果就是迭代器。
判断是不是可迭代对象或者迭代器:
from collections import Iterable,Iterator #导入模块
print(isinstance(s, Iterable)) #是否是迭代对象
print(isinstance(s, Iterator)) #是否是迭代器
生成器
生成器是一种特殊的迭代器。它只需要一个yiled关键字。 生成器一定是迭代器(反之不成立)
def fib():
prev, curr = 0, 1
while True:
yield curr
prev, curr = curr, curr + prev
>>> f = fib()
>>> list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
- 总结:
- 容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。
- 可迭代对象实现了iter方法,该方法返回一个迭代器对象。
- 迭代器持有一个内部状态的字段,用于记录下次迭代返回值,它实现了next和iter方法,迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果。
- 生成器是一种特殊的迭代器,它的返回值不是通过return而是用yield。
三元表达式
示例:判断x,y的大小
def max2(x,y):
if x > y: #使用if else 的老的写法
return x
else:
return y
return x if x > y else y #使用三元表达式写的方式,两边的 x和y只是代表返回值,可以写成字符串形式,那样返回的就是字符串了
print(max2(10,11))
列表解析
示例:把hello所有字符转换成大写并生成list
s = 'hello'
res = [i.upper() for i in s]
print(res)
执行结果:
D:\Python\Python36-32\python.exe E:/Python/DAY-9/day9_笔记.py
['H', 'E', 'L', 'L', 'O']
Process finished with exit code 0
示例:打印出list中大于等于50小于70的数值
l = [1,50,2,3,69,47,32,45,78,80]
print([i for i in l if i >= 50 and i < 70 ])
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-9/day9_笔记.py
# [50, 69]
# Process finished with exit code 0
生成器解析
print([i for i in range(100)]) #生成列表形式
g = (i for i in range(100)) #生成生成器模式,使用for打印出结果 建议使用这种方式,上面的方法如果数字大了很容易卡死电脑
for x in g:
print(x)
生成器的使用
yield
可以接收send
发送的值,来触发执行
send
有next
的功能,并且还有给yield
传值的效果。并且运行时,是先传值在next
。
def foo():
print('starting')
while True:
x = yield
print('value: ',x)
g = foo()
next(g) # 先执行一遍函数
g.send(1) # 传送数值
g.send(2)
# 执行结果:
# D:\Python\Python36-32\python.exe E:/Python/DAY-10/day10_练习.py
# starting
# value: 1
# value: 2
# Process finished with exit code 0
由于每次执行生成器之前都需要next
一下,所以我们可以写个装饰器来干这个事情
def func(func):
def start(*args,**kwargs):
g = func(*args,**kwargs)
next(g)
return g
return start
根据上面的原理: 可以实现面向过程编程
模拟查找文件内容包含root
的文件
import os # 导入OS模块
def init(func): # 生成器初始化装饰器
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
# 阶段一:递归地找文件的绝对路径,把路径发给阶段二
@init
def search(target):
'search file abspath'
while True:
start_path=yield
g = os.walk(start_path)
for par_dir, _, files in g:
# print(par_dir,files)
for file in files:
file_path = r'%s\%s' % (par_dir, file)
target.send(file_path)
# 阶段二:收到文件路径,打开文件获取获取对象,把文件对象发给阶段三
@init
def opener(target):
'get file obj: f=open(filepath)'
while True:
file_path=yield
with open(file_path,encoding='utf-8') as f:
target.send((file_path,f))
# 阶段三:收到文件对象,for循环读取文件的每一行内容,把每一行内容发给阶段四
@init
def cat(target):
'read file'
while True:
filepath,f=yield
for line in f:
res=target.send((filepath,line)) #接收阶段四的tag
if res: #判断
break #跳出循环,终止本次文件读取循环
# 阶段四:收到一行内容,判断root是否在这一行中,如果在,则把文件名发给阶段五
@init
def grep(target,pattern):
'grep function'
tag=False
while True:
filepath,line=yield tag #yield返回值为 tag
tag=False #初始tag为 False
if pattern in line:
target.send(filepath)
tag=True #如果判断成功就 把tag变为 True
# 阶段五:收到文件名,打印结果
@init
def printer():
'print function'
while True:
filename=yield
print(filename)
start_path1=r'C:\Users\Administrator\PycharmProjects\python5期\a'
start_path2=r'C:\Users\Administrator\PycharmProjects\python5期\a\b'
g=search(opener(cat(grep(printer(),'root'))))
print(g)
# g.send(start_path1)
g.send(start_path2)
递归
在调用一个函数的过程中,直接或间接调用了该函数本身。
递归效率低下,需要在进入下一次递归时保留当前的状态。
Python没有尾递归,并且做了层级限制。
必须有一个明确的结束条件。
l = [1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]]
def readdate(list_name):
for i in list_name:
if isinstance(i,list):
readdate(i)
else:
print(i)
readdate(l)
匿名函数
自带return,无需函数名,一定有个返回值。匿名函数只能运行一次。
冒号左边是参数,右边是函数体,函数体执行完毕后直接返回执行的结果。
使用lambda定义匿名函数:
例如:lambda x:x**2
l = [1, 2, 3, 4]
m = map(lambda x: x ** 2, l)
print(list(m)) # [1, 4, 9, 16]
内置函数、模块与包
内置函数链接
内置函数
模块:time;random;hashlib;os
模块:sys,logging,json,pickle
模块:re_1
模块:re_2,subprocess
模块
模块的好处:首先模块可以大大提高代码的可维护性,其次可以减少大量的重复代码,可以通过模块调用来重复使用某些代码。另外,模块还可以避免函数名和变量名冲突。
在Python中一个py
文件就称之为一个模块(module)。
注意: 模块的在import
的时候,会执行模块内的所有代码,并新建一个名称空间保存这些。类似变量,重复导入同一模块,不会重复执行模块内代码。Python的模块就是天然的单例模式。
模块的搜索路径
模块搜索时会优先搜寻Python标准库(内置库)=>应用程序自定义模块=>第三方模块
查看环境变量
print(sys.path)
打印出当前的模块搜索路径,这是个列表可以添加新的变量,直接append
就行了
设置别名
import sys as a # 给sys模块设置别名为a
print(a.path)
包
按目录组织模块的方法,称为包(Package)。在Python中,包可以理解为一个文件夹,但是这个文件夹中必须要有一个
__init__.py
文件。
注意: 调用包的时候会执行一下__init__
文件内的代码。
类
类的三大特性: 封装、继承、多态
定义一个基本的类
包含类名,继承的父类(Python3中默认继承object),类静态字段,类方法
class ClassName(object):
classVal = 123
def classfunc(self):
pass
封装
注:Python中没有真正的隐藏,仅仅是语法上做了些操作。
在想要封装的变量或者函数名前加上"__"两个下划线,就可以。
加上两个下划线之后,只有在类的定义阶段或者对象的定义阶段(实例化)发生。且在类的外部无法直接调用,但是在类的内部可以直接调用(在定义时全部变形了)
封装其实就是变形。
# 运行代码得到结果
class A:
def foo(self):
print('from A foo')
self.__bar() # 做了隐藏,这时在执行找到就不是bar了,而是 _A__bar,下面的结果能看出来名字被变形了
def __bar(self): # 这里等于_A__bar(self)所以最后显示from A bar
print('from A bar')
class B(A):
def bar(self):
print('from B bar')
b = B()
print(A.__dict__)
b.foo()
继承
Python3中的继承关系
如下图继承关系:
[图片上传失败...(image-361b1c-1524577390607)]
根据上图代码示例:
class A:
def test(self):
print('from A')
pass
class B(A):
def test(self):
print('from B')
pass
class C:
def test(self):
print('from C')
pass
class D:
def test(self):
print('from D')
pass
class E(B,C,D):
def test(self):
print('from E')
pass
e = E()
e.test()
执行结果:
默认这样执行,会显示"from E" ,因为调用一个方法会,先从自己的类中查询,下面我们依次注释掉下列类的test:
把E类的test注释掉:结果显示"from B" ,
把B类的test注释掉:结果显示"from A" ,
把A类的test注释掉:结果显示"from C" ,
把C类的test注释掉:结果显示"from D" ,
把D类的test注释掉:结果显示"'E' object has no attribute 'test'"
根据上面的结果过程:在Python3中,当继承多个父类时且父类没有共同的父类时,这时属性的查询顺序是深度优先
上面的属性查询顺序总结:E-->B-->A-->C-->D
这个可能不是很有说服力,那么我们在新建一个F类,让C类继承F类,这时属性的查询顺序如下(仅列出结果):
属性查询顺序:E-->B-->A-->C-->F-->D
当继承多个父类且父类还有共同的父类,如下图
[图片上传失败...(image-822f3b-1524577390608)]
根据上图代码示例:
class A:
def test(self):
print('from A')
pass
class B(A):
def test(self):
print('from B')
pass
class C(A):
def test(self):
print('from C')
pass
class D:
def test(self):
print('from D')
pass
class E(B,C,D):
def test(self):
print('from E')
pass
e = E()
e.test()
执行结果:
默认执行:显示"from E"
把E类的test注释掉:结果显示"from B"
把B类的test注释掉:结果显示"from C"
把C类的test注释掉:结果显示"from A"
把A类的test注释掉:结果显示"from D"
把D类的test注释掉:结果显示"'E' object has no attribute 'test'"
根据上面测试的结果:得出结论在Python3中,当继承多个父类且父类还有共同的父类时,这时属性查找是广度优先
上面的属性查找顺序总结:E-->B-->C-->A-->D
Python2中的继承
新式类:同Python3中一样,是广度优先。所以属性查找顺序是:E-->B-->C-->A-->D
经典类:和新式类恰恰相反,是深度优先。所以属性查找顺序是:E-->B-->A-->C-->D
如下图:
[图片上传失败...(image-b42711-1524577390608)]
子类调用父类的方法
super()
:内置函数,使用绑定方法调用父类的方法。(仅支持新式类)
注:在Python2中需要写成:super(S1,self).__init__(name,age)
# S1 为子类的名字。
class Parent:
def __init__(self,name,age):
self.name = name
self.age = age
class D:
def walk(self):
return ('from D')
class S1(Parent,D):
def __init__(self,name,age,job,school):
super().__init__(name,age) #因为使用绑定方法调用父类的方法,所以不需要传递self
self.job = job
self.school = school
super().walk()
t = S1('egon',73,'teach','oldboy')
print(t.name,t.age,t.walk())
多态
多态就是同一类事物的多种形态。
Python本身就是多态的。
python对传入参数的数据类型没有限制,因此,只要是同一类的(比如序列),都可以用一些方法。比如序列:字符串、列表、元组 都有一些方法:obj.len、 切片。那么对于字符串 s, 列表 l,元组 t,都可以通过len(s)、len(l)、len(t) 来获取长度。
一些类方法
类方法列表,这个哥们总结的
__call__
方法:
对象后面加括号,触发执行
class Foo:
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo()
obj() # 触发执行
__init__
方法:
__init__
方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化
class Foo:
def __init__(self):
print('__init__')
obj = Foo() # 触发执行
__del__
方法:
在对象被删除后调用
class Foo:
def __del__(self):
print('__del__')
obj = Foo()
del obj # 触发执行
__new__
方法:
__new__()
在__init__()
之前被调用,用于生成实例对象,常用于实现单例模式
class Foo:
__instance = None
def __new__(cls, *args, **kwd): # 在__init__之前调用
if Foo.__instance is None: # 生成唯一实例
Foo.__instance = object.__new__(cls, *args, **kwd)
print('__new__')
return Foo.__instance
def __init__(self):
print('__init__')
Foo() # 触发执行
__str__()
:方法:
__str__()
用于表示对象代表的含义,返回一个字符串.实现了__str__()
方法后,可以直接使用
class Foo:
name = 'abc'
def __str__(self):
return self.name
print(Foo()) # 触发执行
@property、@staticmethod、@classmethod 三种方法
- @property
-
@property
让类的方法可以不加括号执行
-
- @staticmethod
-
@staticmethod
不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样 - 如果需求使用类的一些属性方法,只能直接类名.属性名或类名.方法名来调用
-
- @classmethod
-
@classmethod
也不需要self参数,但第一个参数需要是表示自身类的cls参数 - cls代指的就是类自己本身,可以用过cls点出类的各种属性
-
反射
主要是指程序可以访问,检测和修改本身状态或行为的一种能力。
class A:
name = 'abc'
age = 19
#使用, 类和实例化的对象均可以使用
print(hasattr(A,'n1me')) #判断类是否含有某个属性 hasattr(类或者对象名,属性名)
print(getattr(A,'name')) #获取对应属性的属性值 getattr(类或者对象名,属性名,出错返回的内容) <----出错返回内容如果不写的话,当获取一个不存在的属性会报错,写了就会返回写的内容
setattr(A,'name','aaa') #设置属性的属性值 setattr(类或者对象名,属性名,属性值)
print(A.name)
print(A.__dict__)
delattr(A,'name') #删除属性 delattr(类或者对象名,属性名)
print(A.__dict__)