php實現裝飾器(裝飾器的實現)

本文目錄一覽:

「低門檻 手把手」python 裝飾器(Decorators)原理說明

本文目的是由淺入深地介紹python裝飾器原理

裝飾器(Decorators)是 Python 的一個重要部分

其功能是, 在不修改原函數(類)定義代碼的情況下,增加新的功能

為了理解和實現裝飾器,我們先引入2個核心操作:

在這個例子中,函數hi的形參name,默認為’world’

在函數內部,又定義了另一個函數 howdoyoudo,定義這個函數時,將形參name作為新函數的形參name2的默認值。

因此,在函數內部調用howdoyoudo()時,將以調用hi時的實參為默認值,但也可以給howdoyoudo輸入其他參數。

上面的例子運行後輸出結果為:

這裡新定義的howdoyoudo可以稱作一個“閉包”。不少關於裝飾器的blog都提到了這個概念,但其實沒必要給它取一個多專業的名字。我們知道閉包是 函數內的函數 就可以了

當我們進行 def 的時候,我們在做什麼?

這時,hi函數,打印一個字符串,同時返回一個字符串。

但hi函數本身也是一個對象,一個可以執行的對象。執行的方式是hi()。

這裡hi和hi()有本質區別,

hi 代表了這個函數對象本身

hi() 則是運行了函數,得到函數的返回值。

作為對比,可以想象以下代碼

此時也是b存在,可以正常使用。

我們定義2個函數,分別實現自加1, 自乘2,

再定義一個函數double_exec,內容是將某個函數調用2次

在調用double_exec時,可以將函數作為輸入傳進來

輸出結果就是

7

27

同樣,也可以將函數作為輸出

輸出結果為

6

10

有了以上兩個核心操作,我們可以嘗試構造裝飾器了。

裝飾器的目的: 在不修改原函數(類)定義代碼的情況下,增加新的功能

試想一下,現在有一個原函數

在不修改原函數定義代碼的情況下,如果想進行函數內容的添加,可以將這個函數作為一個整體,添加到這樣的包裹中:

我們定義了一個my_decorator函數,這個函數進行了一種操作:

對傳入的f,添加操作(運行前後增加打印),並把添加操作後的內容連同運行原函數的內容,一起傳出

這個my_decorator,定義了一種增加前後打印內容的行為

調用my_decorator時,對這個行為進行了操作。

因此,new_function是一個在original_function上增加了前後打印行為的新函數

這個過程被可以被稱作裝飾。

這裡已經可以發現,裝飾器本身對於被裝飾的函數是什麼,是不需要考慮的。裝飾器本身只定義了一種裝飾行為,這個行為是通過裝飾器內部的閉包函數()進行定義的。

運行裝飾前後的函數,可以清晰看到裝飾的效果

我們復現一下實際要用裝飾器的情況,我們往往有一種裝飾器,想應用於很多個函數,比如

此時,如果我們想給3個print函數都加上裝飾器,需要這麼做

實際調用的時候,就需要調用添加裝飾器的函數名了

當然,也可以賦值給原函數名

這樣至少不需要管理一系列裝飾前後的函數。

同時,在不需要進行裝飾的時候,需要把

全部刪掉。

事實上,這樣並不方便,尤其對於更複雜的裝飾器來說

為此,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框架開發

$this-setDecorators(array(? ?? ?? ? \’FormElements\’,? ?? ?? ? array(array(\’HtmlTag\’, array(\’tag\’=\’div\’, \’style\’=\’border:1px solid #d0d0d0\’)),? ?? ?? ? \’Form\’));這段代碼會在Form標籤後增加div,如:#something#

PHP中幾種常見的開發模式

單例模式

$_instance必須聲明為靜態的私有變量

構造函數和析構函數必須聲明為私有,防止外部程序new 類從而失去單例模式的意義

getInstance()方法必須設置為公有的,必須調用此方法 以返回實例的一個引用

::操作符只能訪問靜態變量和靜態函數

new對象都會消耗內存

使用場景:最常用的地方是數據庫連接。

使用單例模式生成一個對象後, 該對象可以被其它眾多對象所使用。

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

單例模式,使某個類的對象僅允許創建一個。構造函數private修飾, 

申明一個static getInstance方法,在該方法里創建該對象的實例。如果該實例已經存在,則不創建。比如只需要創建一個數據庫連接。

工廠模式

工廠模式,工廠方法或者類生成對象,而不是在代碼中直接new。 

使用工廠模式,可以避免當改變某個類的名字或者方法之後,在調用這個類的所有的代碼中都修改它的名字或者參數。

    * 如果某個類在很多的文件中都new ClassName(),那麼萬一這個類的名字

    * 發生變更或者參數發生變化,如果不使用工廠模式,就需要修改每一個PHP

    * 代碼,使用了工廠模式之後,只需要修改工廠類或者方法就可以了。

註冊模式

註冊模式,解決全局共享和交換對象。已經創建好的對象,掛在到某個全局可以使用的數組上,在需要使用的時候,直接從該數組上獲取即可。將對象註冊到全局的樹上。任何地方直接去訪問。

策略模式

策略模式,將一組特定的行為和算法封裝成類,以適應某些特定的上下文環境。 

eg:假如有一個電商網站系統,針對男性女性用戶要各自跳轉到不同的商品類目,並且所有的廣告位展示不同的廣告。在傳統的代碼中,都是在系統中加入各種if else的判斷,硬編碼的方式。如果有一天增加了一種用戶,就需要改寫代碼。使用策略模式,如果新增加一種用戶類型,只需要增加一種策略就可以。其他所有的地方只需要使用不同的策略就可以。 

首先聲明策略的接口文件,約定了策略的包含的行為。然後,定義各個具體的策略實現類。

執行結果圖: 

 

總結: 

通過以上方式,可以發現,在不同用戶登錄時顯示不同的內容,但是解決了在顯示時的硬編碼的問題。如果要增加一種策略,只需要增加一種策略實現類,然後在入口文件中執行判斷,傳入這個類即可。實現了解耦。 

實現依賴倒置和控制反轉 (有待理解) 

通過接口的方式,使得類和類之間不直接依賴。在使用該類的時候,才動態的傳入該接口的一個實現類。如果要替換某個類,只需要提供一個實現了該接口的實現類,通過修改一行代碼即可完成替換。

觀察者模式

1:觀察者模式(Observer),當一個對象狀態發生變化時,依賴它的對象全部會收到通知,並自動更新。 

2:場景:一個事件發生後,要執行一連串更新操作。傳統的編程方式,就是在事件的代碼之後直接加入處理的邏輯。當更新的邏輯增多之後,代碼會變得難以維護。這種方式是耦合的,侵入式的,增加新的邏輯需要修改事件的主體代碼。 

3:觀察者模式實現了低耦合,非侵入式的通知與更新機制。 

定義一個事件觸發抽象類。

當某個事件發生後,需要執行的邏輯增多時,可以以松耦合的方式去增刪邏輯。也就是代碼中的紅色部分,只需要定義一個實現了觀察者接口的類,實現複雜的邏輯,然後在紅色的部分加上一行代碼即可。這樣實現了低耦合。

裝飾器模式

1:裝飾器模式,可以動態的添加修改類的功能 

2:一個類提供了一項功能,如果要在修改並添加額外的功能,傳統的編程模式,需要寫一個子類繼承它,並重寫實現類的方法 

3:使用裝飾器模式,僅需要在運行時添加一個裝飾器對象即可實現,可以實現最大額靈活性。

php一些簡單功能的實現…高手進來看看

1.可以用地址傳值的方式獲得你的鏈接的值,也可以直接弄一個靜態的

2.用while循環輸出那個div就可以了,

例:

?php

$q=mysql_query(“select * from news”);

while($rs=mysql_fetch_array($q)){ //$rs是一個數組

?

div?php echo $rs[’tilte’];?/div

。。。。。。

?php

}

?

原創文章,作者:CMYF5,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/128821.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
CMYF5的頭像CMYF5
上一篇 2024-10-03 23:25
下一篇 2024-10-03 23:25

相關推薦

  • PHP和Python哪個好找工作?

    PHP和Python都是非常流行的編程語言,它們被廣泛應用於不同領域的開發中。但是,在考慮擇業方向的時候,很多人都會有一個問題:PHP和Python哪個好找工作?這篇文章將從多個方…

    編程 2025-04-29
  • PHP怎麼接幣

    想要在自己的網站或應用中接受比特幣等加密貨幣的支付,就需要對該加密貨幣擁有一定的了解,並使用對應的API進行開發。本文將從多個方面詳細闡述如何使用PHP接受加密貨幣的支付。 一、環…

    編程 2025-04-29
  • 使用PHP foreach遍歷有相同屬性的值

    本篇文章將介紹如何使用PHP foreach遍歷具有相同屬性的值,並給出相應的代碼示例。 一、基礎概念 在講解如何使用PHP foreach遍歷有相同屬性的值之前,我們需要先了解幾…

    編程 2025-04-28
  • PHP獲取301跳轉後的地址

    本文將為大家介紹如何使用PHP獲取301跳轉後的地址。301重定向是什麼呢?當我們訪問一個網頁A,但是它已經被遷移到了另一個地址B,此時若服務器端做了301重定向,那麼你的瀏覽器在…

    編程 2025-04-27
  • PHP登錄頁面代碼實現

    本文將從多個方面詳細闡述如何使用PHP編寫一個簡單的登錄頁面。 1. PHP登錄頁面基本架構 在PHP登錄頁面中,需要包含HTML表單,用戶在表單中輸入賬號密碼等信息,提交表單後服…

    編程 2025-04-27
  • PHP與Python的比較

    本文將會對PHP與Python進行比較和對比分析,包括語法特性、優缺點等方面。幫助讀者更好地理解和使用這兩種語言。 一、語法特性 PHP語法特性: <?php // 簡單的P…

    編程 2025-04-27
  • PHP版本管理工具phpenv詳解

    在PHP項目開發過程中,我們可能需要用到不同版本的PHP環境來試驗不同的功能或避免不同版本的兼容性問題。或者我們需要在同一台服務器上同時運行多個不同版本的PHP語言。但是每次手動安…

    編程 2025-04-24
  • PHP數組去重詳解

    一、array_unique函數 array_unique是php中常用的數組去重函數,它基於值來判斷元素是否重複,具體使用方法如下: $array = array(‘a’, ‘b…

    編程 2025-04-24
  • PHP導出Excel文件

    一、PHP導出Excel文件列寬調整 當我們使用PHP導出Excel文件時,有時需要調整單元格的列寬。可以使用PHPExcel類庫中的setWidth方法來設置單元格的列寬。下面是…

    編程 2025-04-24
  • php擴展庫初探

    一、什麼是php擴展庫? PHP擴展庫(PHP extension)是一些用C語言編寫的動態鏈接庫,用於擴展PHP的功能。PHP擴展庫使得PHP可以與各種數據庫系統相連、SMTP、…

    編程 2025-04-23

發表回復

登錄後才能評論