python进阶04 装饰器、描述器、常用内置装饰器

一、装饰器

  作用:能够给现有的函数增加功能

  如何给一个现有的函数增加执行计数的功能

  首先用类来添加新功能

def fun(): #首先我们定义一个函数
print('func running') #看到封装,我们首先想到的是函数
class MyFunc():
def __init__(self,f): #把函数和变量封装在一起
self.f=f
self.count=0 def run(self): #间接的调用了封装的函数
self.count+=1
return self.f() mf=MyFunc(func)#实例化
mf.run()#输出func running
print(mf.count) #输出1
mf.run()#输出func running
print(mf.count)#输出2
mf.run()#输出func running
print(mf.count)#输出3
#在python中,有一个__call__函数可以让这个对象可以调用
class MyFunc():
def __init__(self,f): #把函数和变量封装在一起
self.f=f
self.count=0 def __call__(self): #让这个对象可以调用
self.count+=1
return self.f() mf =MyFunc(func)
mf() #当你调用一个实例的时候,其实就是调用它的__call__方法,虽然mf()是一个实例,但也同时也是一个函数
func() ——>func.__call__() mf() #func running
print(mf.count)#
mf() #func running
print(mf.count)#
mf() #func running
print(mf.count)#
mf() #func running
print(mf.count)#
def func():
print('func running') class MyFunc():
def __init__(self,f): #把函数和变量封装在一起
self.f=f
self.count=0 def __call__(self,*arg,**kwarg): #为保证所有参数都能够传入,让这个对象可以调用
self.count+=1
return self.f(*arg,**kwarg)#调用封装进来的函数,并拿到他的返回值 func = MyFunc(func) #这便是装饰,右边的实例由于有call方法,他便是一个函数右边的实例封装了原函数和计数器;然后又将这个实例赋值个func函数,让func不指向原函数,指向一个新的函数;看上去功能和原来一样,其实已经增加了功能
#关于装饰,函数名还是原来的函数名,指向的却是新的函数,这个新的函数会调用原来的函数,也可以做些别的事情

 

 也可以用函数来封装

#思路:装饰----一个新的函数,看上去功能和原来一样,其实已经增加了功能,增加了计数器

#首先先定义一个函数:
def my_func(f):
return f() #我在调用my_func的时候,其实已经调用了f() #然后就是添加功能,添加计数器
def my_func(f):
def new_func():
new_func.count+=1 #计数器加1
return f() #调用并返回f的内容
new_func.count=0
return new_func func=my_func(func)#装饰
#装饰,不过是指向了一个新的函数,只不过这个函数除了调用原来的函数以外,还会做点别的事

def my_func(func):
def new_func():
new_func.count+=1#把计数器加1
return func() #返回并调用原来的函数func
new_func.count=0
return new_func#只是返回,不调用 @my_func #在定义的同时便装饰了
def mimc(a,b):
  return a-b

@MyFunc #在定义的同时便装饰了
def add(a,b):
return a+b #总结:将新的功能和老的功能都绑定在一个新的函数里面

二、描述器

  管理一个类属性的访问,修改,删减

class MyAttribute:#将来他的实例会是一个类的属性(注意!是类的属性,不是实例的属性)
def __get__(self,instance,owner):#instance是实例,owner是类
print('get',instance,owner)
def __set__(self,instance,value):#value是赋的值
print('set',instance,value)
def __delete__(self,instance,value):
print('delete',instance)
def __str__(self):
print('') #重点来了
class MyClass:
attr=MyAttribute() #实例化 mc=MyClass()
print(mc.attr) #如果没有get set delete三种,就会输出字符串表示str的内容;如果够get,get就会被调用,就会替代字符串表示;set就是修改,delete就是删除
#基于描述器的装饰器 Property
class Property:
def __init__(self,fget=None):
self.fget=fget def __get__(self,obj,objtype=None):
if obj is None:return selg
if self.fget is None: raise AttributeError('无法访问这个属性')
returnself.fget(obj) #这个东西不用理解
#他是一个装饰器,也是一个描述器
#例子:是否匿名
class Person:
def __init__(self,name):
self.name=name
self.is_anonymous=False #是否是匿名 @Property
def get_name(self):#方法,所以若要拿到name的,肯定需要调用,如果不想调用,在上面加个@Property这个装饰器即可
if not self.is_anonymous:
return self.name
else:
return 'anonymous' p= Person('Tuple')
p.is_anonymous=False
print(p.get_name)#这样就可以直接取值,不需要print(p.get_name())

三、常用内置装饰器

  property

class Person:
def __init__(self,name):
self.name=name @property
def name(self):
print('通过property来获取name')
if hasattr(self,'name'):
return self.name
else:
raise AttributeError('没有name这个属性') @name.setter #控制赋值
def name(self,value):
print('通过property来设置name')
self.name=value @name.deleter
def name(self):
print('通过property来删除name')
del self.name #property 就是把一个方法变成一个属性的样子

  静态方法和类方法

class A:
@staticmethod #静态方法能够让类和实例,看他都是一个普通函数
def meth():
print('xxx') a=A()
a.meth() #实例调用的时候——>A.meth(a),两者看起来都是函数 class A:
@classmethod #类方法:第一个参数,回传进去的都是类,不是实例
def meth(cls): #cls表示类
print('xxx') a=A()
a.meth() #实例调用的时候——>A.meth(a) #正常情况下我们都应该传实例,但是以后做项目的时候会用到类方法
#以后做数据库接入的时候,有一个ORM,在查询的时候,可以是由类方法,来简化你的代码

四、关于一些内容的补充

  1、关于运行后进入交互环境的说明

#IDLE执行以后,会进入shell模式(交互模式),此时程序还没有执行完,等待你输入下一段代码

  2、函数返回值的理解

#区分func和func()
#func是一个函数对象
#func()分为两步:1、执行这个函数里面的内容。2、用return出来的返回值,替代这个调用
def func():
print('xxx')
return 1 print(func()) #相当于print(1) def other():#定义函数,是不会执行这个函数的
print('other')
return 10 def func():
print('xxx')
return other() print(func())#输出 xxx other 10
#文件--第一个执行的是func()
#func--先打印出func
#func--调用other
#other--先打印出other
#other--返回值10
#func--在返回other()的值
#文件--print要输出func()的值

  3、装饰后必须赋值变量

def myfunc(func):
def wrapper(*args,**kwargs):
wrapper.count+=1
return func(*args,**kwargs)
wrapper.count=0
return wrapper def func():
print('xxx') f1=myfunc(func)
f2=myfunc(func)
print(f1 is f2) #两次装饰,都是得到了加强版的函数,但是两次装饰的函数时两个,因此他们拥有两个计数器,所以装饰完必须要有一个变量接收它,装饰以后必须赋变量

  4、装饰器

#装饰,首先是加功能;碧玺,原来的是得做,还得加东西
#原来的函数,功能一定要有;其次应该还有一个新的功能和她深度的绑定在一起 #思考:我们装饰完以后,得到的必须是一个加强版的函数(新的函数) def func():
print('func')
return 1
#如果我们要模仿这个函数,我们得实现什么
#1、你需要打印出func 2、你也要返回出1 #假如,我要写一个装饰器,这个装饰器什么功能都不加,只是单纯模仿
def myfunc(f):
return f f2=myfunc(func)#f2和func是一个函数
#希望,我返回的不是原来的那个函数,而是新函数,但是做的事和原来一样 def myfunc(f):
def wrapper():#这个是定义,不是调用。定义完成以后会得到一个函数对象wrapper
wrapper.count+=1#把我自己这个函数里面的count+1
return f() #就是运行f,并且把它的返回值给返回出来(因为我要返回f(),就是返回f里面的return)
wrapper.count=0 #由于是一个对象,所以可以加.count
return wrapper#我返回的是wrapper这个函数对象(不是wrapper的调用)
def myfunc(f)
def wrapper():#这个封装的新函数:1、增加计数器 2、调用原函数,并返回其返回值
wrapper.count+=1
return f()
wrapper.count=0
return wrapper 入门版:
def myfunc(f):
count=0
def wrapper():
nonlocal count #作用域的问题,全局变量和局部变量
count+=1
return f()
return wrapper

  

python进阶04 装饰器、描述器、常用内置装饰器的更多相关文章

  1. python中的运算符及表达式及常用内置函数

    知识内容: 1.运算符与表达式 2.for\while初步了解 3.常用内置函数 一.运算符与表达式 python与其他语言一样支持大多数算数运算符.关系运算符.逻辑运算符以及位运算符,并且有和大多数 ...

  2. python的学习笔记之——time模块常用内置函数

    1.Python time time()方法 Python time time() 返回当前时间的时间戳(1970纪元后经过的浮点秒数). time()方法语法: time.time() 举例: #! ...

  3. Python笔记_第四篇_高阶编程_再议装饰器和再议内置函数

    1. 概述: 我们在前面用了很多的装饰器这个工具的方法.这个位置要系统的讲一下装饰器. 1.2 为什么需要装饰器. 装饰器本质是一个Python函数,它可以让其他函数在不需要任何代码变动的前提下增加额 ...

  4. Elasticsearch(10) --- 内置分词器、中文分词器

    Elasticsearch(10) --- 内置分词器.中文分词器 这篇博客主要讲:分词器概念.ES内置分词器.ES中文分词器. 一.分词器概念 1.Analysis 和 Analyzer Analy ...

  5. python基础语法16 面向对象3 组合,封装,访问限制机制,内置装饰器property

    组合: 夺命三问: 1.什么是组合? 组合指的是一个对象中,包含另一个或多个对象. 2.为什么要用组合? 减少代码的冗余. 3.如何使用组合? 耦合度: 耦: 莲藕 ---> 藕断丝连 - 耦合 ...

  6. python内置装饰器

    前言 接着上一篇笔记,我们来看看内置装饰器property.staticmethod.classmethod 一.property装饰器 1. 普通方式修改属性值 code class Celsius ...

  7. python的内置下载器

    python有个内置下载器,有时候在内部提供文件下载很好用. 进入提供下载的目录 # ls abc.aaa chpw.py finance.py lsdir.py ping.py u2d-partia ...

  8. Sturts2几个常用内建拦截器的介绍

    Sturts2几个常用内建拦截器的介绍:1)conversation:这是一个处理类型转换错误的拦截器,它负责将类型转换错误从ActionContext中取出,并转换成Action的FieldErro ...

  9. classmethod、staticclassmethod内置装饰器函数

    # method 英文是方法的意思 # classmethod 类方法 # 当一个类中的方法中只涉及操作类的静态属性时,此时在逻辑上,我们想要直接通过类名就可以调用这个方法去修改类的静态属性,此时可以 ...

随机推荐

  1. oracle删除重复数据只保留一条

    -- 如表role_user的数据 ROLEID USERID -- 删除相同记录只剩下一条记录 根据两个字段查询重复数据 (roleid,userid) ) 删除重复数据只保留一条 delete f ...

  2. zabbix使用mysql模板监控mysql

    出现监控项访问拒绝的信息 解决方法是: 在 mysql的 my.cnf 配置中增加 [mysql] user=zabbix password=zabbix [mysqladmin] user=zabb ...

  3. 分享知识-快乐自己:oracle12c创建用户提示ORA-65096:公用用户名或角色无效

    今天在oracle12c上创建用户,报错了.如下图: 我很郁闷, 就打开了oracle官方网站找了下, 发现创建用户是有限制的. 2.解决方案 创建用户的时候用户名以c##或者C##开头即可. 错误写 ...

  4. Java并发模型(一)

    学习资料来自http://ifeve.com/java-concurrency-thread-directory/ 一.多线程 进程和线程的区别: 一个程序运行至少一个进程,一个进程至少包含一个线程. ...

  5. 在eclipse创建Maven工程修改默认JRE

    1. 打开Maven安装目录的setting.xml文件 2.找到profiles标签 3.加入下面配置即可 <profile>    <id>jdk-1.8</id&g ...

  6. leetcode 304. Range Sum Query 2D - Immutable(递推)

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

  7. Python IOError: [Errno 13] Permission denied:

    一般是代码写错了,比如我遇到的问题就是由于 os.listdir() 传参传错导致的. 本应该传入字符串路径名,但传入了一个文件对象(object)

  8. docker镜像管理基础

    [root@node01 ~]# docker pull quay.io/coreos/flannel:v0.10.0-amd64 v0.10.0-amd64: Pulling from coreos ...

  9. Vue cli项目开启Gzip

    目录 安装 compression-webpack-plugin 更改配置文件 服务器开启gzip功能 安装 compression-webpack-plugin 建议安装v1.1.11版本,最新版本 ...

  10. NancyFX 第二章 Rest框架

    正如你看到的,Nancy有两个主要用途. 其中第一项是作为一种通用的基于 REST 框架,可替代 ASP.NET Web API 或其他Rest工具包. 默认情况下,Nancy提供一流的路由和内容协商 ...