寫在前面
早上剛到公司,被羣裏這樣一個問題所吸引,如下圖所示:
後來自己就又想了想,感覺自己說的也不全對,於是想從程序角度來做一個佐證。
當然,在寫這篇文章之前,也是查閱了很多文章,關於這個問題的一些觀點,彙總如下:
- Java 註解也叫元數據,一種代碼級別的說明。Python 裝飾器是一種語法糖。
- 註解是給別人看的,功能不僅僅由註解決定;裝飾器直接攔截,直接改變被裝飾對象的行爲!
- 註解(Annotation):僅提供附加元數據支持,並不能實現任何操作。需要另外的 Scanner 根據元數據執行相應操作。
- 裝飾器(Decorator):僅提供定義劫持,能夠對類及其方法的定義並沒有提供任何附加元數據的功能。
講真這些概念性的東西,我是真的看的雲裏霧裏的,建議還是看維基百科或者教材吧。
我個人觀點,肯定是註解和裝飾器不是一回事的。
話不多說,還是直接上代碼,用實際案例來說話吧!
一、表現形式上看
@zhujie('參數')
1、相同點:
都是@開頭,註解、裝飾器都可以自定義、都可以帶參數、都可以被標註代碼塊之前執行。
2、不同點:
- java註解可以寫在類、方法、變量頭上;
- python裝飾器可以寫在類、方法頭上。
二、實例對比
1、Java註解
示例代碼如下:
@Override
public String toString() {
return "PlaylistInfo{" +
"curtime='" + curtime + '\'' +
", issmarter='" + issmarter + '\'' +
", xmusicnum='" + xmusicnum + '\'' +
", picurl=" + picurl +
", playlist=" + playlist +
", systemtime=" + systemtime +
'}';
}
@Override: 重寫的意思,不滿意父類的可以自己在實現下,一般在編譯階段會對方法進行檢查。
很明顯,註解放在方法上方,僅負責編譯、檢查,並未對方法中的內容和該方法的功能做出改變。
2、python裝飾器
實例代碼如下:
class TestClass():
@property
def myValue(self):
return self.value
if __name__=="__main__":
TestClass.myValue = '裝飾器呀!'
print (TestClass.myValue)
@property: 作爲屬性的意思
明顯看出,裝飾器直接改變了函數的功能。
3、結論
由上得出,註解和裝飾器的不同:
- 1、註解對只是幹了檢查、校驗的事,不會修改所標註的代碼。
- 2、裝飾器可以在方法標註,並改變所修飾的代碼功能。
到這裏,你是不是會覺得,他倆根本就不是一回事,因爲根本不是一樣的呀。
其實,在java中的註解和反射可以實現python裏裝飾器的效果。
是不是又蒙了?別急,我們接着往後看!
二、註解實現上看
註解的好處:在不改動源代碼的基礎上,對源代碼實現新功能。如果有大面積代碼需要改動同樣功能,可以在方法或者類上面使用註解實現
1、實現的註解場景
分別用python與Java方式,實現對程序計算的校驗,把異常結果寫到error.log文件中
2、Python方式實現
實例代碼如下:
# 此時就是作爲寫入錯誤結果使用
def check(func):
def wrapper(*args, **kwargs):
try:
res = func(*args, **kwargs)
return res
except Exception as err:
with open("error.log", mode="at", encoding='utf-8') as f:
f.write("start".center(50, '*'))
f.write('\n')
f.write(str(func))
f.write('\n')
f.write(str(err.__class__))
f.write('\n')
f.write("end".center(50, '*'))
f.write('\n')
return wrapper
@check
def add(a, b):
print(a + b)
@check
def divide(a, b):
print(a / b)
add(50, 50)
divide(100, 0)
3、Java方式實現
示例代碼如下:
public class Calculator {
@Check
public void add() {
System.out.println(50 + 50);
}
@Check
public void divide() {
System.out.println(100 / 0);
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
public class CheckDemo {
public static void main(String[] args) throws IOException {
Calculator test = new Calculator();
Class<? extends Calculator> c = test.getClass();
Method[] methods = c.getMethods();
BufferedWriter bw = new BufferedWriter(new FileWriter("input.txt"));
for (Method m :
methods) {
if (m.isAnnotationPresent(Check.class)) {
try {
m.invoke(test);
} catch (Exception e) {
bw.newLine();
bw.write(e.getCause().getMessage()+"----");
bw.newLine();
bw.write(String.valueOf(e.getCause().getClass()));
}
}
}
bw.flush();
bw.close();
}
}
分別運行各自編譯器,結果如下圖所示:
4、結論
由上可知,Java中的註解和反射可以實現python裏裝飾器的效果。
三、來個接口開發的例子
1、需求場景
輸入用戶密碼,返回用戶信息接口
2、Python方式實現
示例代碼如下:
from flask import Flask
import json
from flask import request
app = Flask(__name__) #啓動
@app.route('/rongrong/getUserInfo',methods=['GET']) #請求路徑、請求方式
def login():
username = request.args.get("username") #獲取url中參數“username”的值
password = request.args.get("password") #獲取url中參數“password”的值
if username and password: #如果傳入了值爲真打印下面信息
data = json.dumps({
"username":username,
"password":password,
"code":"200",
"message":"成功"
},ensure_ascii=False) #解決中文亂碼問題
else: #如果傳參爲空打印下面信息
data = json.dumps({
"message":"請傳遞參數"
},ensure_ascii=False)
return data
if __name__ == "__main__":
app.run() #運行
3、Java方式實現
示例代碼如下:
@RequestMapping(value = "/rongrong/getUserInfo", method = RequestMethod.GET)
public Result<List<Student>> findStudentByName(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
JsonRootBean jsonRootBean = new JsonRootBean();
jsonRootBean.setUsername(username);
jsonRootBean.setPassword(password);
jsonRootBean.setCode("200");
jsonRootBean.setMessage("成功");
Result result = ResultUtils.success(jsonRootBean);
return result;
}
4、啓動各自服務,運行結果如下:
python結果:
Java結果:
5、結論
Python的裝飾器很單一,就是通過一層殼對一個函數的行爲進行修飾,而@decorator_func 只是一個語法糖,用以美化裝飾器的寫法。
Java中的註解則不同,它是從語言層面爲代碼中的類,函數,字段增加一些運行時可以讀到的元數據,而註解的提供者要在運行時對這些元數據進行讀取,並做相應的處理。
四、寫在最後
筆者才疏學淺,寫這篇文正完全是出於技癢,自然也是查閱了大量文章,纔有此文。
以下內容僅代表個人觀點:
- 長得像,但卻是兩個物種,不過可以讓他們表現得近似;
- Python 的裝飾器正如他的名稱,很直白,就是實現了裝飾器模式(的一個語法糖)。@部分對應一個返回爲函數的函數,可以對目標函數進行輸入、輸出過濾,以及其他干預、包裝;
- Java 的註解有好幾種,按作用期劃分有編譯期、運行期等,僅僅是給類或方法做個標記,在對應的時期你的程序可以通過反射讀到它們;
- Java 的註解表面看似乎沒啥子用,但少就是多,稍微包裝一下就可以實現與 Python 裝飾器等同的作用,前提是通過什麼方式調用目標類和方法,只要調用的包裝內對註解進行了解釋,就 OK 了;
通過各種手段可以讓他們變成一回事兒,所以就結果而言,沒錯,可以當成是一回事兒。
換句話說,有時候感覺裝飾器更像是Java的一種設計模式。
你覺得呢?歡迎評論區留言討論!