Login
网站首页 > 文章中心 > 其它

php实现装饰器_php装饰器模式

作者:小编 更新时间:2023-09-26 13:49:19 浏览量:112人看过

「低门槛 手把手」python 装饰器(Decorators)原理说明

本文目的是由浅入深地介绍python装饰器原理

装饰器(Decorators)是 Python 的一个重要部分

其功能是, 在不修改原函数(类)定义代码的情况下,增加新的功能

在这个例子中,函数hi的形参name,默认为'world'

所以呢,在函数内部调用howdoyoudo()时,将以调用hi时的实参为默认值,但也可以给howdoyoudo输入其他参数.

上面的例子运行后输出结果为:

这里新定义的howdoyoudo可以称作一个"闭包".不少关于装饰器的blog都提到了这个概念,但其实没必要给它取一个多专业的名字.我们知道闭包是 函数内的函数 就可以了

当我们进行 def 的时候,我们在做什么?

这时,hi函数,打印一个字符串,同时返回一个字符串.

但hi函数本身也是一个对象,一个可以执行的对象.执行的方式是hi().

这里hi和hi()有本质区别,

hi 代表了这个函数对象本身

hi() 则是运行了函数,得到函数的返回值.

作为对比,可以想象以下代码

此时也是b存在,可以正常使用.

在调用double_exec时,可以将函数作为输入传进来

输出结果就是

同样,也可以将函数作为输出

输出结果为

①.0

有了以上两个核心操作,我们可以尝试构造装饰器了.

装饰器的目的: 在不修改原函数(类)定义代码的情况下,增加新的功能

试想一下,现在有一个原函数

在不修改原函数定义代码的情况下,如果想进行函数内容的添加,可以将这个函数作为一个整体,添加到这样的包裹中:

我们定义了一个my_decorator函数,这个函数进行了一种操作:

对传入的f,添加操作(运行前后增加打印),并把添加操作后的内容连同运行原函数的内容,一起传出

这个my_decorator,定义了一种增加前后打印内容的行为

调用my_decorator时,对这个行为进行了操作.

所以呢,new_function是一个在original_function上增加了前后打印行为的新函数

这个过程被可以被称作装饰.

这里已经可以发现,装饰器本身对于被装饰的函数是什么,是不需要考虑的.装饰器本身只定义了一种装饰行为,这个行为是通过装饰器内部的闭包函数()进行定义的.

运行装饰前后的函数,可以清晰看到装饰的效果

我们复现一下实际要用装饰器的情况,我们往往有一种装饰器,想应用于很多个函数,比如

实际调用的时候,就需要调用添加装饰器的函数名了

当然,也可以赋值给原函数名

这样至少不需要管理一系列装饰前后的函数.

同时,在不需要进行装饰的时候,需要把

全部删掉.

事实上,这样并不方便,尤其对于更复杂的装饰器来说

为此,python提供了一种简写方式

这个定义print1函数前的@my_decorator,相当于在定义完print1后,自动直接运行了

不论采用@my_decorator放在新函数前,还是显示地重写print1 = my_decorator(print1),都会存在一个问题:

装饰后的函数,名字改变了(其实不止名字,一系列的索引都改变了)

输出结果为:

这个现象的原因是,装饰行为本身,是通过构造了一个新的函数(例子中是wrap_func函数)来实现装饰这个行为的,然后把这个修改后的函数赋给了原函数名.

这样,会导致我们预期的被装饰函数的一些系统变量(比如__name__)发生了变化.

对此,python提供了解决方案:

经过这个行为后,被装饰函数的系统变量问题被解决了

刚才的例子都比较简单,被装饰的函数是没有参数的.如果被装饰的函数有参数,只需要在定义装饰行为时(事实上,这个才更通用),增加(*args, **kwargs)描述即可

之前的描述中可以感受到,对于例子中的装饰行为(前后加打印),函数被装饰后,本质上是调用了新的装饰函数wrap_func.

所以呢,如果原函数需要有输入参数传递,只需要在wrap_func(或其他任意名字的装饰函数)定义时,也增加参数输入(*args, **kwargs),并将这些参数,原封不动地传给待装饰函数f.

这种定义装饰行为的方式更具有普遍性,忘记之前的定义方式吧

我们试一下

输出

这里需要注意的是,如果按照以下的方式定义装饰器

那么以下语句将不会执行

因为装饰后实际的函数wrap_func(虽然名字被改成了原函数,系统参数也改成了原函数),运行到return f(*args, **kwargs) 的时候已经结束了

因为装饰器my_decorator本身也是可以输入的,所以呢,只需要在定义装饰器时,增加参数,并在后续函数中使用就可以了,比如

此时装饰器已经可以有输入参数了

你可能发现,为什么不用简写版的方法了

因为以上代码会报错!!

究其原因,虽然

等价于

但是,

并不等价于

这本身和@语法有关,使用@my_decorator时,是系统在应用一个以单个函数作为参数的闭包函数.即,@是不能带参数的.

但是你应该发现了,之前的@wraps(f)不是带参数了吗?请仔细观察以下代码

通过一层嵌套,my_decorator_with_parma本质上是返回了一个参数仅为一个函数的函数(my_decorator),但因为my_decorator对my_decorator_with_parma来说是一个闭包,my_decorator_with_parma是可以带参数的.(这句话真绕)

通过以上的定义,我们再来看

可以这么理解,my_decorator_with_parma(msg='yusheng')的结果是原来的my_decorator函数,同时,因为my_decorator_with_parma可以传参,参数实际上是参与了my_decorator的(因为my_decorator对my_decorator_with_parma是闭包), my_decorator_with_parma(msg='yusheng') 全等于 一个有参数参加的my_decorator

所以呢,以上代码等价于有参数msg传递的

比较绕,需要理解一下,或者干脆强记这种范式:

以上范式包含函数的输入输出、装饰器的输入,可以应对大部分情况了.

实验一下:

好了,全部的一个log装饰器,利用datetime统计了函数的耗时,

并且,装饰器可以进行输出文件操作,如果给出了文件路径,则输出文件,否则就打印.

利用这个装饰器,可以灵活地进行耗时统计

不设置输出文件地址,则打印.运行结果为:

也可以输出到文件

同时在当前目录生成了一个test.log 文件,内容为:

以上的装饰器都是以函数形式出现的,但我们可以稍做改写,将装饰器以类的形式实现.

这个装饰器类Log 上个例子里的装饰器函数log功能是一样的,同时,这个装饰器类还可以作为基类被其他继承,进一步增加功能.

原文

怎么给zend framework form表单添加装饰器,小弟谢一个先! - PHP框架开发

PHP中几种常见的开发模式

单例模式

$_instance必须声明为静态的私有变量

构造函数和析构函数必须声明为私有,防止外部程序new 类从而失去单例模式的意义

getInstance()方法必须设置为公有的,必须调用此方法 以返回实例的一个引用

::操作符只能访问静态变量和静态函数

new对象都会消耗内存

使用场景:最常用的地方是数据库连接.

使用单例模式生成一个对象后, 该对象可以被其它众多对象所使用.

私有的__clone()方法防止克隆对象

单例模式,使某个类的对象仅允许创建一个.构造函数private修饰,?

申明一个static getInstance方法,在该方法里创建该对象的实例.如果该实例已经存在,则不创建.比如只需要创建一个数据库连接.

工厂模式

工厂模式,工厂方法或者类生成对象,而不是在代码中直接new.?

使用工厂模式,可以避免当改变某个类的名字或者方法之后,在调用这个类的所有的代码中都修改它的名字或者参数.

* 如果某个类在很多的文件中都new ClassName(),那么万一这个类的名字

* 发生变更或者参数发生变化,如果不使用工厂模式,就需要修改每一个PHP

* 代码,使用了工厂模式之后,只需要修改工厂类或者方法就可以了.

注册模式

注册模式,解决全局共享和交换对象.已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候,直接从该数组上获取即可.将对象注册到全局的树上.任何地方直接去访问.

策略模式

策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境.?

eg:假如有一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告.在传统的代码中,都是在系统中加入各种if else的判断,硬编码的方式.如果有一天增加了一种用户,就需要改写代码.使用策略模式,如果新增加一种用户类型,只需要增加一种策略就可以.其他所有的地方只需要使用不同的策略就可以.?

首先声明策略的接口文件,约定了策略的包含的行为.然后,定义各个具体的策略实现类.

执行结果图:?

总结:?

通过以上方式,可以发现,在不同用户登录时显示不同的内容,但是解决了在显示时的硬编码的问题.如果要增加一种策略,只需要增加一种策略实现类,然后在入口文件中执行判断,传入这个类即可.实现了解耦.?

实现依赖倒置和控制反转?(有待理解)?

通过接口的方式,使得类和类之间不直接依赖.在使用该类的时候,才动态的传入该接口的一个实现类.如果要替换某个类,只需要提供一个实现了该接口的实现类,通过修改一行代码即可完成替换.

观察者模式

①.:观察者模式(Observer),当一个对象状态发生变化时,依赖它的对象全部会收到通知,并自动更新.?

定义一个事件触发抽象类.

当某个事件发生后,需要执行的逻辑增多时,可以以松耦合的方式去增删逻辑.也就是代码中的红色部分,只需要定义一个实现了观察者接口的类,实现复杂的逻辑,然后在红色的部分加上一行代码即可.这样实现了低耦合.

装饰器模式

①.:装饰器模式,可以动态的添加修改类的功能?

php一些简单功能的实现...高手进来看看

①可以用地址传值的方式获得你的链接的值,也可以直接弄一个静态的

例:

php

$q=mysql_query("select * from news");

while($rs=mysql_fetch_array($q)){ //$rs是一个数组

......

}

版权声明:倡导尊重与保护知识产权。未经许可,任何人不得复制、转载、或以其他方式使用本站《原创》内容,违者将追究其法律责任。本站文章内容,部分图片来源于网络,如有侵权,请联系我们修改或者删除处理。

编辑推荐

热门文章