1. 生成器
- 什么是生成器?generator
- 生成器记录一个算法,可以一边循环,一边计算的一种机制。
- 生成器生成方式有哪些?
- 有这样一个需求:存储1-10000000中所有的偶数。
-
使用list(列表)
# 直接使用list,但这种方式占用很多内存 import time import sys # 系统模块 time.clock() # 获取CPU计算的时长 list1 = [] for i in range(2,10000001,2): list1.append(i) print(list1) costTime = time.clock() print("创建列表耗时:%g"%costTime) print("创建列表的内存开销:%d"%sys.getsizeof(list1)) # 获取内存开销 # 使用列表推导式 list2 = [i for i in range(2,10000001) if i%2==0] print(list2)
- 使用列表会占用很多内存;如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
-
使用生成器:可以存储数据,其实就是存储算法
-
创建生成器:
- 方式一:
g = (i for i in range(2,10000001,2))
- 方式一:
############# 方式一:############# import time import sys # 系统模块 time.clock() # 获取CPU计算的时长 # 创建生成器g g = (i for i in range(2,10000001,2)) print(type(g1)) # 1.next():生成器元素访问方式,但是一次只生成一个元素 # print(next(g)) # 2.用for循环遍历 for i in g: print(i) # 3.使用send()方式访问生成器中的元素 print(g.send(None)) print(g.send("abc")) costTime = time.clock() print("创建生成器耗时:%g"%costTime) print("创建生成器的内存开销:%d"%sys.getsizeof(g))
- 方式二:在函数中使用关键字
yield
,函数在调用时候,不再是接收函数的返回值,而是接收一个生成器。
############# 方式二:############# def test(): for x in range(5) yield x f = test() print(type(f)) # 访问生成器的元素 print(next(f))
-
生成器的元素访问方式及注意事项:
next(g)
for 循环遍历
g.__ next __()
g.send()
(使用send的时候首个元素必须给None参数,后续元素,可以传任意参数)
-
廖雪峰官网【生成器】:https://www.liaoxuefeng.com/wiki/897692888725344/923029685138624
-
2. 迭代器
-
可迭代性:Iterable
-
迭代器:Iterator;能被next()访问,并不断返回下一个值的对象
-
判断是否是可以迭代:
insinstance(对象,类)
from collection import Iterable
num = 100
if isinstance(num,Iterable):
for i in num:
print(i)
else:
print('num不具备可迭代性')
- 可迭代性有:
- 集合类元素:list;tuple;string;dict
- 生成器
from collection import Iterable
# 列表
list = [1,2,3]
print(isinstance(list,Iterable))
# 字符串
str = "abcdef"
print(isinstance(str,Iterable))
# 生成器
g = (i for i in range(5))
print(type(g))
print(isinstance(g,Iterable))
- 具备可迭代性的元素,就一定是迭代器吗?
- 答案:不一定!!!
from collection import Iterable,Iterator
# 列表不是迭代器!!!
list = [1,2,3]
if isinstance(list,Iterator):
print('list是迭代器')
print(next(list))
else:
print('list不是迭代器')
iter()
函数:让具备可迭代性的元素变成迭代器next()
from collection import Iterable,Iterator
# 使用1iter()函数把list变成迭代器
list = iter(list)
list = [1,2,3]
if isinstance(list,Iterator):
print('list是迭代器')
print(next(list))
else:
print('list不是迭代器')
3. 闭包
- 定义:就是一个函数;在函数式编程中呗广泛使用。
- 创建闭包:(三个条件)
- 嵌套函数(外部函数,内部函数)
- 内部函数使用外部函数中定义的变量(参数)
- 外部函数一定要有返回值,返回内部函数名
- 例如:使用闭包完成求两个数字的和
def funcOut(num1):
def funcIn(num2):
# num1 += 1 # 不可以修改外部函数的值
nonlocal num1
# 如果想在内部函数中修改外部函数的变量,需要使用nonlocal声明
num1 += 1
return num2 + num1 # 满足条件2
return funcIn # 满足条件3
a = 10
b = 20
funcIn = funcOut(a) # 返回了函数funcIn
print(type(funcIn)) # funcIn是个函数
result = funcIn(b)
result = funcIn(100)
print(result)
-
注意:
-
闭包中的外部函数的局部变量不会因为函数调用而消失!
-
如果想在内部函数中修改外部函数的变量,需要使用nonlocal声明!
-
- 例子:求两个点之间的距离
############# 方式一:函数 #############
import math
def getDis(x1,y1,x2,y2):
return math.sqrt((x1-x2)**2 + (y1-y2)**2)
############# 方式二:闭包 #############
def getDisOut(x1,y1):
def getDisIn(x2,y2):
return math.sqrt((x1-x2)**2 + (y1-y2)**2)
return getDisIn
# 分别求点(10,10),(20,20)距离原点的距离
# 函数
dis = getDis(0,0,10,10)
print("(10,10)距离原点的距离为:%g"%dis)
dis = getDis(0,0,20,20)
print("(20,20)距离原点的距离为%g"%dis)
print("--------------------------------------")
# 闭包
getDisIn = getDisOut(0,0)
dis1 = getDisIn(10,10)
print("(10,10)距离原点的距离为:%g"%dis1)
dis1 = getDisIn(20,20)
print("(20,20)距离原点的距离为%g"%dis1)
-
闭包的特殊用途:
-
可以在不修改现有功能源码的前提下,增加新的功能
-
日志功能(统计访问时间,访问功能,写到日志文件中)
-
权限验证(下载之前会验证当前账户是否为会员)
-
开闭原则:
开放:添加功能
关闭:修改源代码
-
############# 日志:源码 ############# # 定义一个记录日志的函数:将访问事件以及访问的函数名写入到文件中(log.text) import time def writeLog(func): try: file = open('log.text','a',encoding='utf-8') # 写入相关数据信息(访问函数名,访问的时间) file.write(func.__name__) file.write('\t') # 写入访问时间 strTime = time.asctime() file.write(strTime) file.write('\t') except Exception as e: print(e.args) finally: file.close() # 闭包 def funcOut(func): def funcIn(): # 新增功能 writeLog(func) func() return funcIn def func1(): print('我是功能1') def func2(): print('我是功能2') # 闭包的调用 func1 = funcOut(func1) func2 = funcOut(func2) func1() func2()
-
4. 装饰器
- 装饰器与闭包关系紧密,很多时候,二者结合使用:
装饰器使一个函数或方法包装在另一个函数里面,可以在被包装的函数添加一些额外的功能,比如日志,还可以对参数,返回结果进行修改。
import time
def writeLog(func):
try:
file = open('log.text','a',encoding='utf-8')
file.write(func.__name__)
file.write('\t')
file.write(time.asctime())
file.write('\n')
except Exception as e:
print(e.args)
finally:
file.close()
def funcOut(func):
def funcIn():
writeLog(func)
func()
return funcIn
@funcOut
# 等价于:func1 = funcOut(func1)
def func1():
print('我是功能1')
@funcOut
# 等价于:func2 = funcOut(func2)
def func2():
print('我是功能2')
# func1 = funcOut(func1)
# func2 = funcOut(func2)
func1()
func2()
- 装饰器的例子:使用装饰器,不修改一个函数源码的前提下,增加新的功能。
# 给bookName()函数的返回值‘西游记’两侧添加‘《 》’
# 1.使用闭包来完成
def funcOut(func):
def funcIn():
return '《' + func() + '》'
return funcIn
def bookName():
return '西游记'
bookName = funcOut(bookName)
print(bookName())
# 2.使用装饰器来完成
def funcOut(func):
print('闭包1开始装饰')
def funcIn():
return '《' + func() + '》'
return funcIn
def funcOut2(func):
print('闭包2开始装饰')
def funcIn():
return '$' + func() + '$'
return funcIn
@funcOut2
# bookName = funcOut2(bookName)
@funcOut
# bookName = funcOut(bookNmae)
def bookName():
return '西游记'
print(bookName())
- 注意:在使用装饰器时,靠近函数的装饰器先执行!
- 指定参数个数的装饰器
def funcOut(func):
def funcIn(x,y):
func(x,y)
return funcIn
@funcOut
# test = funcOut(test)
def test(a,b):
print('a = %g b= %g'%(a,b))
test(1,2)
- 通用装饰器
# def funcOut1(func):
# def funcIn1(a):
# print('新增功能1')
# func(a)
# return funcIn1
# def funcOut2(func):
# def funcIn2(a,b):
# print('新增功能2')
# func(a,b)
# return funcIn2
def funcOut(func):
# 可变参数
def funcIn(*args,**kwargs):
print('新增功能')
return func(*args,**kwargs)
return funcIn
@funcOut
def func1(a):
print('a = %g'%a)
@funcOut
def func2(a, b, name='www'):
print('a = %g b = %g c = %s' % (a, b, name))
func1(10)
func2(20, 30, name = 'WYZ')
【类装饰器】https://blog.csdn.net/weixin_42375099/article/details/94859193#2__48
5. Python动态添加属性方法
- python是动态语言:可以在运行是改变类的结构(添加属性,添加方法,删除函数)
-
动态添加属性
-
添加对象属性:
对象名.属性名 = 值
- `setattr(对象, 属性, 值)
-
添加类属性:
类名.属性名 = 值
-
-
动态添加方法:
-
添加对象方法:(import types)
p1.study = types.MethodType(study,p1)
-
添加静态方法
@staticmethod
类名.静态方法名 = 定义的函数名
注意这里定义的函数不加括号! -
添加类方法
@classmethod
类名.类方法名 = 定义的函数名
注意这里定义的函数不加括号!
-
-
python是强类型语言:变量的类型运行时决定;变量类型在运行之后,可以任意改变,不需要强制转换。
- 动态添加属性:添加类属性;成员属性
class Person()
def __init__(self,name,age):
self.name = name
self.age = age
p1 = Person('学生',18)
p2 = Person('学生',20)
########## 动态添加属性 ###########
# 1.添加类对象属性 #
# 1. 直接通过对象添加
p1.address = '北京'
# 2. 通过setattr()添加gender属性
setattr(p1, 'gender', '男')
# 2.添加类属性 #
# print(p2.gender) # 会报错!没有gender这个属性
Person.CLS.ID = 100
print(p1.CLS.ID)
print(p2.CLS.ID)
- 动态添加方法:对象方法;类方法;静态方法
########## 添加方法 ###########
import types
class Person()
def __init__(self,name,age):
self.name = name
self.age = age
def study(self):
print('学习使我快乐!')
p1 = Person('学生',18)
p2 = Person('学生',20)
### 添加对象方法 ###
# 通过typesMethodType添加对象方法,只能p1来调用
p1.study = types.MethodType(study,p1)
p1.study
### 添加静态方法 ###
@staticmethod
def testStaticMethod():
print('我是静态方法')
# 给类添加静态方法
Person.method1 = testStaticMethod
# 1.对象访问
p1.method1()
# 2.类名访问
Person.method1()
### 添加类方法 ###
@classmethod
def testClsMethod(cls):
print('我是类方法')
# 添加类方法
Person.method2 = testClsMethod
# 1.对象访问
p1.method2()
# 2.类名访问
Person.method()
6. __ slots __的作用
- __ slots __可以对动态添加成员属性做限制,对类属性没有限制。
- __ slots __可以对动态添加对象方法做限制;对静态方法,类方法没有限制。
- __ slots __对子类没有任何限制。
import types
class Person():
__slots__ = ('name','age')
def __init__(self,name,age):
self.name = name
self.age = age
def eat(self):
print("人要吃饭")
def study(self):
print('学习使我快乐')
# 动态添加属性
p1 = Person('张三',19)
# p1.gender = '男' # 会报错!
Person.CLS_id = 102 # 没有报错!
# 动态添加类方法
# p1.study = types.MethodType(study,p1) # 会报错!
# 动态添加静态方法
@staticmethod
def testStatic():
print('我是静态方法')
Person.testStatic = testStatic
# 子类动态添加属性
class Student(Person):
pass
stu1 = Student('李四',26)
stu1.addr = '北京'
print(stu1.addr) # 没有报错!