摘要:也就是给原函数加个外壳。类装饰填充了啊我是原函数类装饰填充了啊我是原函数说明后面关于类的装饰器如果理解困难当做了解即可,用的也少。
可迭代对象、生成器、迭代器三者的关系
1. 迭代器一定是可迭代对象
2. 生成器是迭代器的一种
3. 可迭代对象:必须实现 __iter__方法
4. 迭代器:必须实现 __iter__方法 和 __next__ 方法
5. 生成器:必须实现 __iter__方法 和 __next__ 方法,yield 代替了这两个方法
6. 工具包: from collections import Iterable,Iterator
7. 查看抽象接口:
In [265]: Iterable.__abstractmethods__
Out[265]: frozenset({"__iter__"})
In [266]: Iterator.__abstractmethods__
Out[266]: frozenset({"__next__"})
8. 可迭代对象都可以被 for循环 所遍历, 另外 实现了 __getitem__的类 其对象也可for遍历
关于 __getitem__等魔法方法,以后会多带带写一篇文章。
生成器
生成器有两种写法:
形式一:
In [230]: def f():
...: for x in range(10):
...: yield x
In [231]: y = f()
形式二:
In [239]: y = ( x for x in range(10) )
In [240]: y
Out[240]: at 0x0000024A4D3AFB48>
"推动生成器" 迭代有三种方式:
方式1:
In [232]: next(y)
Out[232]: 0
方式2:
In [233]: y.__next__()
Out[233]: 1
方式3:
In [236]: y.send(None)
Out[236]: 2
前两种方式是等价的,第三种方式有些差别,且听我说:
In [248]: def f():
...: print(1)
...: a = yield 2
...: print(a)
In [249]: y = f()
In [250]: y.send(None)
1 # 这里是 print(1)的结果
Out[250]: 2
In [251]: y.send(100)
100 # 这里是print(a)的结果
不知道阁下能否看出和两种的差别:
1. yield 2 # 这行多了个赋值操作
2. send(100) # send() 函数里面放了个,然后print(a) 打印的就是100
举个例子:
1. a = yield 2 # 相当于一个士兵等待指令,等号左边还未执行,程序就被封锁了
2. send(100) # 长官输入了一个100,士兵收到后就把程序解封,并执行等号左边
3. 于是 就相当于 a 被赋值为 100
迭代器
先看两个简单的函数:
In [294]: iter([1,2,3])
Out[294]:
In [295]: reversed([1,2,3])
Out[295]:
自定义迭代器:
In [283]: class A(Iterator): # 注意这里:继承了Iterator就不用实现 __iter__接口了
...: def __init__(self, value):
...: self.value = value
...: self.index = -1
...: def __next__(self): # 注意这里:__next__是写逻辑的主要接口,每次返回单个值
...: self.index += 1
...: return self.value[self.index]
In [284]: a = A(["Tom", "Jerry"])
In [285]: next(a)
Out[285]: "Tom"
In [286]: next(a)
Out[286]: "Jerry"
串联合并迭代器:
1. 普通序列迭代器串联
In [287]: from itertools import chain
In [289]: chain(range(1),range(2),range(3))
Out[289]:
In [290]: list(chain(range(1),range(2),range(3)))
Out[290]: [0, 0, 1, 0, 1, 2]
2. 字典序列迭代器串联
In [288]: from collections import ChainMap
In [291]: ChainMap({1:1},{2:2,3:3})
Out[291]: ChainMap({1: 1}, {2: 2, 3: 3})
In [292]: dict(ChainMap({1:1},{2:2,3:3}))
Out[292]: {1: 1, 2: 2, 3: 3}
装饰器
装饰器如果按钻牛角尖的方式来理解的确是很头疼的事情。先说个例子吧:
In [296]: def f(func):
...: def f1():
...: print("原函数之前加点功能")
...: func() # 这就是原函数
...: print("原函数之后加点功能")
...: return f1
In [299]: @f #这句等价于=> func = f(func)
...: def func():
...: print("我是原函数哦")
In [300]: func()
>> 原函数之前加点功能
>> 我是原函数哦
>> 原函数之后加点功能
解释
用大白话来讲,装饰器就是在原函数的前后加功能。也就是给原函数加个外壳。看上去是调用的原函数,
实则调用的是外壳函数,外壳函数里面包括原函数和 一些其他的自定义功能
例子:
面包不好吃啊,但是你还想吃这个面包,咋整? 上下抹点奶油,就变成了三明治(我没吃过。。)
吃其他面包的时候继续抹上奶油就行了(封装性)
面包-原函数
三明治-被装饰器装饰后的函数
各种功能(吃法)快速拼接:
@奶油
def 面包1():
pass
@奶油
def 面包2():
pass
@沙拉
def 面包1():
pass
@沙拉
def 面包2():
pass
存在问题:
@f #这句等价于=> func = f(func)
这是我上面说过的一句话,你仔细看看:
func之前指向的是(原函数)面包的空间,恩,func函数名 也就是(__name__) 是func(面包)
现在他指向的是(新函数)三明治的空间,恩,它的函数名是f1(三明治)
函数名变了,有些张冠李戴的感觉,如果不想让它变,并保持本身的函数名,看我操作:
In [301]: from functools import wraps
In [316]: def f(func):
...: @wraps(func) ### 没错 这里是最主要的,基本格式固定写法,照着写即可
...: def f1():
...: print(func.__name__)
...: print("原函数之前加点功能")
...: func()
...: print("原函数之后加点功能")
...: return f1
标准装饰器使用(原函数带有返回值 和 参数):
In [323]: def f(func):
...: @wraps(func)
...: def f1(*args, **kwargs):
...: print("报年龄和性别~~~")
...: return func(*args, **kwargs)
...: return f1
In [324]: @f #每次都要记住 test=f(test) 这个隐含的变形,熟了就好了
...: def test(name,age):
...: print(name, age)
In [325]: test("Tom",18)
-------------------------------下面是打印部分
报年龄和性别~~~
Tom 18
带参数的装饰器:
构造如下:
def foo(arg): # 其实就是在原来的基础上再再再次包装个外壳,仅此而已
# 之前讲的装饰器f实现代码原封不动全放在这里面,看下面帮你写好了。
def f(func):
@wraps(func)
def f1(*args, **kwargs):
return func(*args, **kwargs)
return f1
return f
调用形如:
@foo("123") #多了个参数,可分解为foo的返回值f 放在 @ 的后面,是不是一切又回到了从前?
def func():
pass
装饰类:
应用场景:
Python WEB框架 Flask/Django都有 FBV和CBV模式 (以后我也会写这方面的文章)
FBV: Function Based View 简单来说,逻辑视图用函数来写
----那么需要装饰器的时候,直接用普通函数装饰器装饰到函数上即可
CBV: Class Based View 简单来说,逻辑视图用类来写
----这种没有函数,是用类写的,那么这时候就需要对类进行装饰了
核心思想:
还是记住上面讲的隐式变形,只不过这次传给装饰器的是类,对类的操作可就太多了。
里面一大堆黑魔法,下面我就来写一下类初始化功能性装饰器。(以后也会多带带写黑魔法的文章)
In [1]: def f(c):
def f1(*args, **kwargs):
return c(*args,**kwargs)
return f1
In [2]: @f
...: class A:
...: def __init__(self,name):
...: print(name)
In [3]: A("Tom")
Tom # 这是print打印的输出
Out[3]: <__main__.A at 0x2948e8fb828>
类装饰器:
这个场景真没遇到过,不过实现也很容易的。
In [8]: class A:
...: def __init__(self,func):
...: self.func = func
...: def __call__(self,*args,**kwargs):
...: print("类装饰填充了啊")
...: self.func(*args, **kwargs)
...:
In [9]: @A
...: def test():
...: print("我是原函数")
...:
In [10]: test()
类装饰填充了啊
我是原函数
说明:后面关于类的装饰器如果理解困难当做了解即可,用的也少。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/43829.html
摘要:解释就相当于把每个序列元素的每一个单独用一个管道函数处理,再把他们按顺序组合成一个新可迭代对象注意这个管道函数只能是单参数函数,如果想传递多个参数怎么办使用偏函数怕有些人看不懂,这里就不用了,而是用普通函数定义方式固定值固定值固定值固定值固 map In [25]: list(map(lambda a:a**2, [1,2,3,4])) Out[25]: [1, 4, 9, 16] 解...
摘要:不要疑惑,告诉你答案这个代表正负号的正。虽然一点技术含量没有,但是你要懂序列也许叫可迭代对象更为合适,但是我喜欢叫序列。 数据结构 可变类型与不可变类型(重头戏) 基操: 可变类型:[], {} # 可增删改 查 不可变类型: int float str () # 无法增删改, 只可查 升操: + 与...
摘要:多线程对于爬虫方面也可以表现出较好的性能。计算密集型就别想多线程了,一律多进程。所以同一时刻最大的并行线程数进程数的核数这条我的个人理解很模糊,参考吧多线程多线程有种通过的那种方式,非常普遍,此处就不写了。 GIL的理解 GIL这个话题至今也是个争议较多的,对于不用应用场景对线程的需求也就不同,说下我听过的优点: 1. 我没有用过其他语言的多线程,所以无法比较什么,但是对于I/O而言,...
摘要:类的继承类继承有三种调用方式,其实是有区别的,听我慢慢道来第一种父类方法参数直接调用第二种方法参数直接调用在谁的类下调用,就找此类对应的下一个就是要继承的第三种方法参数找类名对应的的下一个,就是继承的,一般写本身的类名上下文管理器上下文管理 类的继承 类继承有三种调用方式,其实是 有区别 的,听我慢慢道来 class A: def say(self, name): ...
预编译 import re re1 = re.compile(r元字符 组成的正则规则) # 元字符下面会说 re1.方法() # 方法下边也会说 元字符: 表示普通字符: . # 除了 外 都可以匹配的到 d # 只匹配 纯数字 0-9 D # 和 d相反, 除了数字全都匹配 ...
阅读 2261·2019-08-30 15:53
阅读 3202·2019-08-30 15:44
阅读 3098·2019-08-30 14:11
阅读 3129·2019-08-30 14:01
阅读 2903·2019-08-29 15:16
阅读 4027·2019-08-29 13:10
阅读 1462·2019-08-29 10:56
阅读 2699·2019-08-26 13:58