函数闭包
定义
延伸了作用域的函数(能访问定义体之外定义的非全局变量
作用
- 共享变量的时候避免使用了不安全的全局变量
- 允许将函数与某些数据关联起来,类似于简化版面向对象编程
- 相同代码每次生成的闭包,其延伸的作用域都彼此独立(计数器,注册表)
- 函数的一部分行为在编写时无法预知,需要动态实现,同时又想保持接口一致性
- 较低的内存开销:类的生命周期远大于闭包
- 实现装饰器
代码
print("Class方式:")
class Averager:
def __init__(self):
self.series = []
def __call__(self,new_val):
self.series.append(new_val)
return sum(self.series)/len(self.series)
avg1 = Averager()
print(avg1(3))
print(avg1(2))
print("闭包方式:")
def Avg_1():
count = 0
total = 0
def Avg_2(val):
nonlocal total,count
total+=val
count+=1
return total/count
return Avg_2
avg = Avg_1()
print(avg(9))
print(avg(10))
'''
输出
Class方式:
3.0
2.5
闭包方式:
9.0
9.5
'''
理解
功能说明
这段代码需要解决的是,定义一个average函数,我们每次传进去一个值,都可以得到这个值和之前传入值的平均值。比如第一次传入1,均值就是1,第二次传入2,均值就是1.5,以此类推。
类方式
第一种方式是通过构建一个类,这也是我们通常容易想到的方式,利用在类里面创建一个list,每次调用都将新值append进去,这是一种解决方法。
函数闭包方式
这里跟前面提到的作用域是有关的,函数闭包延伸了作用域,我们可以看到,我们的外层函数只是为了存储两个变量而已,没有其他作用了。其实这时候应该有个疑惑,在调用Avg_1()之后,由于count和total是在Loval层,函数调用结束了应该销毁了才是,但实际上并没有,我们在Avg_2中通过nolocal关键字延长了两个变量的生命周期,使得我们在每次调用的时候都能访问都两个变量,达到效果。
装饰器
为什么会出现装饰器这个东西
- 名称管理
- 显示调用
- 就近原则
- 充分复用
@语法糖
'@' 用做函数的修饰符,可以在模块或者类的定义层内对函数进行修饰,出现在函数定义的前一行,不允许和函数定义在同一行。
什么是装饰器
- 装饰器是一个可调用的对象,以某种方式增强函数的功能
- 装饰器是一个语法糖,在源码中标记函数(此源码指编译后的源码)
- 解释器解析源码的时候将被装饰的函数作为第一个位置参数传给装饰器
- 装饰器可能会直接处理被装饰函数,然后返回它(一般仅修改属性,不修改代码)
- 装饰器也可能用一个新的函数或可调用对象替换被装饰函数(但核心功能一般不变)
- 装饰器仅仅看着像闭包,其实功能的定位与闭包有重合也有很大区别
- 装饰器模式的本质是元编程:在运行时改变程序行为
- 装饰器的一个不可忽视的特性:在模块加载时立即执行
- 装饰器是可以堆叠的,自底向上逐个装饰
- 装饰器是可以带参数的,但此时至少要写两个装饰器
- 装饰器的更加Pythonic的实现方式其实是在类中实现 call() 方法
代码
def check_param(**kw):
flag = kw.get('flag')
def check_p(func):
def decorate(*args):
if flag:
if not all([isinstance(arg,int) for arg in args]):
raise TypeError("{} only accepts integers as argument".format(func.__name__))
return func(*args)
return decorate
return check_p
@check_param(flag=True)
def my_sum(a,b):
return a+b
if __name__=='__main__':
print(my_sum(1,2))
print(my_sum(1,2.0))
'''
输出
3
Traceback (most recent call last):
File "/home/xueaoru/文档/pydemo/blog.py", line 18, in
print(my_sum(1,2.0))
File "/home/xueaoru/文档/pydemo/blog.py", line 7, in decorate
raise TypeError("{} only accepts integers as argument".format(func.__name__))
TypeError: my_sum only accepts integers as argument
'''
解释
上面的代码是为了完成一个检查my_sum函数的参数是否是整数的功能的装饰器,同时增加了开关功能,我们将flag设置为true就是开启检查功能,如果不是整数,则直接报错。
理解
装饰器是对函数在不改变原有函数内部实现的情况下,对原有函数进行功能增强。而@语法糖是对原函数进行修饰的修饰符,当调用@语法糖进行修饰的时候,即使该函数并不调用,也会执行修饰语句,因为触发了运行装饰器。这时候该函数作为参数传给修饰函数的外部函数,然后该函数作为引用赋值给内部函数的函数名,也就是说我们真正的操作是在内部函数中进行的,因此可以在内部函数中对原函数进行功能增强。其中,有参数的时候呢,内部函数也需要通过*和**拆包得到参数,原函数有返回值的时候呢,我们在调用完原函数的时候也应该给出返回值。而装饰器本身带参数的时候呢,可以在装饰函数外再加一层接收参数的包装得到参数。
OOP In Python
概念
一切都是对象
从语言设计层面理解Python中的数据类型:一切都是对象,都是从Object继承过来的。Object由三部分组成:identity、type、value。
identity
理解
当Objects创建之后呢,identity也不会改变,直到被销毁。我觉得跟c++里的地址差不多吧,当然也不能完全这么理解,也有不同的方。
要点
- 变量存储的是创建的Object的identity
- 创建出来的不同Object有不同的identity
- 变量的id变了不是因为Object的identity变了,而是对应的Object变了
- 对于不可变对象而言,计算结果如果已经存在,可以直接返回相同的identity
type
要点
- 当Object创建后,其type不会改变
- type决定了一个Object可以支持那些运算,可能的值在哪些范围
value
要点
- 有些Object的value可以改变:可变对象
- 有些Object的value不可以改变:不可变对象
每一个class在定义的时候如果没有继承的话,那么他继承的就是Object这个超级class,而每一个自定义的class在python中都是一个type object。
@classmethod
理解
可以把类中的某个方法变成这个类的方法而不依赖于对象,也就是说,在对象没有创建的时候,我们也可以调用这个类的方法执行一定的操作。这就有点像C++中的静态成员函数。
代码
class Student:
teacher_name = "Omg"
def __init__(self,name):
self._name = name
@classmethod
def Teacher(cls):
print(cls.teacher_name)
def Me(self):
print(self._name)
if __name__== '__main__':
aa = Student("xue")
aa.Teacher()
aa.Me()
Student.Teacher()
'''
Omg
xue
Omg
'''
解释
这段代码是通过调用Teacher方法得到老师的名字,可以看出,我们就算不通过对象直接调用,也可以输出老师的名字。classmethod就起这个作用。
@property
理解
本质上这是一个装饰器,可以省去写get、set函数的对外绑定。
property
函数原型为
property(fget=None, fset=None, fdel=None, doc=None)
代码
@property与下面的代码效果是一样的
class Teacher:
def __init__(self,name,subject):
self._name = name
self._subject = subject
def setName(self,name):
self._name = name
def getName(self):
return self._name
def setSubject(self,subject):
self._subject = subject
def getSubject(self):
return self._subject
def show(self):
print("name is:{} and subject is:{}".format(self._name,self._subject))
name = property(getName,setName)
subject = property(getSubject,setSubject)
if __name__ == '__main__':
t = Teacher("A","math")
t.show()
t.name = "B"
t.subject = "English"
t.show()
'''
name is:A and subject is:math
name is:B and subject is:English
'''
使用@property之后的更加优美的版本
class Teacher:
def __init__(self,name,subject):
self._name = name
self._subject = subject
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
@property
def subject(self):
return self._subject
@subject.setter
def subject(self,subject):
self._subject = subject
def show(self):
print("name is:{} and subject is:{}".format(self._name,self._subject))
if __name__ == '__main__':
t = Teacher("A","math")
t.show()
t.name = "B"
t.subject = "English"
t.show()
注意
这里@property必须在setter前面初始化,这个应该很容易理解,因为解释器读程序的时候是从上往下读的。
Special method
要点
- 之所有要实现special method,是为了让自定义的class与python中的内置函数无缝衔接
- python中有大量的内置函数,而这些函数中绝大部分是special method
- python中的special method :https://rszalski.github.io/magicmethods/
代码
class A:
def __init__(self):
pass
def __str__(self):
return "I am str"
def __len__(self):
return 15
def __bool__(self):
return False
if __name__ == '__main__':
a = A()
print(a,len(a),a==True)