前言
时隔20天,也该给这个系列画上一个句号啦。
后期我会对这个系列进行一个整合,预计会整理为13篇,不用担心,是原地整理,一般不会删博客。
这一篇不是爬虫相关,是我重新梳理了Python中的相关知识之后,发现了一些和C++异同之处,觉得应该再整理一份。
函数的不定长参数
有的时候,你会需要一个函数去处理比它预设时更多的参数,这时候如果没有预先占位,就比较尴尬了。
python自定义函数中有两中不定长参数,第一种是*name
,第二种是**name
。加了星号 *
的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。加了两个星号 **
的参数会以字典的形式导入。
第一种形式的不定长参数,在传入额外的参数时可以不用指明参数名,直接传入参数值即可,第二种因为返回的是字典,所以传入时需要指定参数名。
下面是两个简单的栗子:
def funA(a, b, *args):
print(a)
print(b)
print(args)
funA(1, 2, 3, 5, 6, 7)
输出如下:
1
2
(3, 5, 6, 7)
def funB(a, b, **vardict):
print(a)
print(b)
print(vardict)
print(vardict['l'])
funB(1, 2, l=3, m=4)
输出结果如下:
1
2
{
'l': 3, 'm': 4}
3
至于它的真实应用场景,小白是用不上了,不过你们以后要往深了走肯定是要碰上的。
有兴趣的话可以看一下C++版的:argc和argv的妙用
lambda函数
匿名函数lambda:是指一类无需定义标识符(函数名)的函数或子程序。lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值。
说明:我将它们用在需要封装特殊的、非重用代码上,避免令我的代码充斥着大量单行函数。
代码示例:
p = lambda x,y:x+y
print(p(4,6))
a=lambda x:x*x
print(a(3)) # 注意:这里直接a(3)可以执行,但没有输出的,前面的print不能少
a = lambda x,y,z:(x+8)*y-z
print(a(5,6,8)
在C++当中这叫做宏定义函数,各位不要惧怕C++,更不要去排斥,如果还年轻,要在程序员这一行越走越远,C++是个不错的首选。
我们来看一下C++中的实现:C++ #define
创建包
包其实就是文件夹,更确切的说,是一个包含“init.py”文件的文件夹。
因此,如果我们想手动创建一个包,只需进行以下 2 步操作:
1、新建一个文件夹,文件夹的名称就是新建包的包名;
2、在该文件夹中,创建一个 __init__.py 文件(前后各有 2 个下划线‘_’),该文件中可以不编写任何代码。当然,也可以编写一些 Python 初始化代码,则当有其它程序文件导入包时,会自动执行该文件中的代码
生成器
什么是生成器
在Python中,一边循环一边计算的机制,称为生成器:generator。
为什么要有生成器
列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。
如:仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。
简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!
如何创造一个生成器
把一个列表推导式的[]改成()
L = [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10))
<generator object <genexpr> at 0x1022ef630>
yield关键字
如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。调用函数就是创建了一个生成器(generator)对象。
生成器的工作原理
(1)生成器(generator)能够迭代的关键是它有一个next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。
(2)带有 yield 的函数不再是一个普通函数,而是一个生成器generator。可用next()调用生成器对象来取值。next 两种方式 t.next() | next(t)。(基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。
(3)yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。
(4).send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果
换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是5的,那么a=10。
代码示例
来看一段杨辉三角的代码示例:
def triangle():
_list, new_list = [], []
while True:
length = len(_list)
if length == 0:
new_list.append(1)
else:
for times in range(length + 1):
if times == 0:
new_list.append(1)
elif times == length:
new_list.append(1)
else:
temp = _list[times - 1] + _list[times]
new_list.append(temp)
yield new_list #返回值,然后挂起函数,等待下一次调用
_list = new_list.copy()#调用后会继续执行下去
new_list.clear()
n = 0
for result in triangle():
n += 1
print(result)
if n == 10:
break
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
如果觉得太难啦,那就换一个斐波那契吧哈哈哈哈
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
# 拿去打印ab看看
生成器这个东西嘛,说实在的,学C++两年来没见过类似的。
装饰器
一看这个名字,我就想到了装饰者模式。
Python的装饰器(decorator)可以说是Python的一个神器,它可以在不改变一个函数代码和调用方式的情况下给函数添加新的功能。
牛逼都吹成这样了,我也不想多说什么,直接上代码吧:
import time
def time_it(func):
def inner():
start = time.time()
func()
end = time.time()
print('用时:{}秒'.format(end-start))
return inner
@time_it
def func1():
time.sleep(2)
print("Func1 is running.")
if __name__ == '__main__':
func1()
能看懂不?怪我,我的装饰者模式写了两篇,也没写出精华来,就不能放链接给你们了。
看了装饰器,我才知道装饰着模式的精妙之处,强!!!
运行结果:
Func1 is running.
用时:2.0056326389312744
嵌套函数
讲到装饰器,就不得不讲一下内函数(我也不知道为什么,每本书上都是这么说的)
我们先来看一个最简单的嵌套函数的例子。
def outer():
x = 1
def inner():
y = x + 1
print(y)
inner()
outer() #输出结果 2
可曾有见过这类函数 ?
def outer():
x = 1
def inner():
y = x + 1
print(y)
return inner
outer() # 输出<function outer.<locals>.inner at 0x039248E8>
f1 = outer()
f1() # 输出2
上面那俩比较简单,我们来看个抽象的:
def decorator(func):
def inner(*args, **kwargs):
add_other_actions()
return func(*args, **kwargs)
return inner
能转的过弯吗?
这里就要用到装饰器了。
我们现在可以开始动手写个名为hint的装饰器了,其作用是在某个函数运行前给我们提示。这里外函数以hint命名,内函数以常用的wrapper(包裹函数)命名。
def hint(func):
def wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
@hint
def hello():
print("Hello!")
我们现在对hello已经进行了装饰,当我们调用hello()时,我们可以看到如下结果。
hello()
hello is running.
Hello!
值得一提的是被装饰器装饰过的函数看上去名字没变,其实已经变了。当你运行hello()后,你会发现它的名字已经悄悄变成了wrapper,这显然不是我们想要的。这一点也不奇怪,因为外函数返回的是由wrapper函数和其外部引用变量组成的闭包。
为了解决这个问题保证装饰过的函数__name__属性不变,我们可以使用functools模块里的wraps方法,先对func变量进行wraps。
from functools import wraps
def hint(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
return func(*args, **kwargs)
return wrapper
@hint
def hello():
print("Hello!")
这是一个通用装饰器的模板,要不要收藏起来就看你个人了。
就算你不想看,我也要再放一个高级的装饰器通用模板,因为我自己早晚用得上:
from functools import wraps
def hint(coder):
def wrapper(func):
@wraps(func)
def inner_wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
print('Coder: {}'.format(coder))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@hint(coder="John")
def hello():
print("Hello!")
这就叫做:书又不是读给别人的。
类装饰器也一并放这儿了:
from functools import wraps
#类的装饰器写法, 不带参数
class Hint(object):
def __init__(self, func):
self.func = func
@wraps(func)
def __call__(self, *args, **kwargs):
print('{} is running'.format(self.func.__name__))
return self.func(*args, **kwargs)
#类的装饰器写法, 带参数
class Hint(object):
def __init__(self, coder=None):
self.coder = coder
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print('{} is running'.format(func.__name__))
print('Coder: {}'.format(self.coder))
return func(*args, **kwargs) # 正式调用主要处理函数
return wrapper
装饰器部分到此告一段落。
到这里。
这个系列,就到这里啦,感谢大家一路相伴。
要是想我了,或者说一个人学Python有些孤单,这里有个传送门:传送门
转载:https://blog.csdn.net/qq_43762191/article/details/111167376