探討 Python 的 Annotation 註解 - tsungjung411/python-study GitHub Wiki
Annotation
-
中文翻譯
- 註解(18800項) / 註釋(7880項) / 標註(4190項)
- 但講「註解」會分不清楚是程式的註解(comment),還是程式的註解(annotation),我會傾向使用「標註」
-
標註的功能
- 就像一般程式註解(comment)一樣,但具有可程式化功能
- 也就是,程式可對「標註(Annotation)」加以管理,但無法管理一般「文字註解(comment)」
- 而可程式化功能,主要用途在於「靜態/動態管理相關的程式碼」
-
標註的情境
- 可以透過標註,協助檢查 class, method 的繼承, method 的參數, 或是成員變數(看程式語言的支援程度)
- 像 Java 的 @Override 就可以檢查父類別是否具有該 method
- 若一個 method 標註了 @Override,但該 method 不存在於父類別以上,則會回報編譯錯誤
- (method 可指為 方法/函數)
-
以下實作有點複雜...
簡單版1-Case1: 無參數
def my_decoration(target):
# target:
# Case1: 若「標註」沒有參數,內容值為「被標註者本身」
# Case2: 若「標註」有參數,內容值為「該標註所傳入的參數」(如同一般函數的參數定義與處理)
print('my_decoration: target:', target)
def my_overrider(target):
# 同上,
# Case1: 若「標註」沒有參數,此函數無作用
# Case2: 若「標註」有參數,內容值為「被標註者本身」
print('my_decoration: my_overrider: target: ', target)
# end-of-def
return my_overrider # 回傳 method 指標
# end-of-def
class MyInterface:
@my_decoration
def say_hello_world(self):
print('Hello, World!')
# end-of-def
# end-of-class
執行結果:
my_decoration: target: <function MyInterface.say_hello_world at 0x7f51c85737b8>
簡單版2-Case2: 有參數
def my_decoration(target):
# target:
# Case1: 若「標註」沒有參數,內容值為「被標註者本身」
# Case2: 若「標註」有參數,內容值為「該標註所傳入的參數」(如同一般函數的參數定義與處理)
print('my_decoration: target:', target)
def my_overrider(target):
# 同上,
# Case1: 若「標註」沒有參數,此函數無作用
# Case2: 若「標註」有參數,內容值為「被標註者本身」
print('my_decoration: my_overrider: target: ', target)
# end-of-def
return my_overrider # 回傳 method 指標
# end-of-def
class MyInterface:
@my_decoration('my_python')
def say_hello_world(self):
print('Hello, World!')
# end-of-def
# end-of-class
執行結果:
my_decoration: target: my_python
my_decoration: my_overrider: target: <function MyInterface.say_hello_world at 0x7f51c8597378>
實作 Java 的 abstract 修飾子
- 屬於 Case1 的範例
def abstract(target=None):
print('abstract: target:', target)
if hasattr(target, '__annotations__'):
target.__annotations__['abstract'] = True
else:
target.__annotations__ = {'abstract': True}
# end-of-if
return target # 回傳 method 指標
# end-of-def
def isabstract(target):
if target and hasattr(target, '__annotations__'):
return True
# end-of-if
return False
# end-of-if
@abstract
class MyInterface:
@abstract
def say_hello_world(self):
raise Exception("need to implement 'say_hello_world'")
# end-of-def
# end-of-class
class MyObject(MyInterface):
def say_hello_world(self):
print("Hello, World!")
# end-of-def
# end-of-class
o = MyObject()
o.say_hello_world()
print("isabstract(MyInterface):", isabstract(MyInterface))
print("isabstract(MyObject):", isabstract(MyObject),
"(需要透過 @override 去檢查 class 的 method 是否已經都實作,若已經都實作,就可以將 class 的 abstarct 取消掉)")
執行結果:
abstract: target: <function MyInterface.say_hello_world at 0x7f5c3836df28>
abstract: target: <class '__main__.MyInterface'>
Hello, World!
isabstract(MyInterface): True
isabstract(MyObject): True (需要透過 @override 去檢查 class 的 method 是否已經都實作,若已經都實作,就可以將 class 的 abstarct 取消掉)
補充說明:
- 實作 abstract 不簡單,需要檢查所有父類別的 abstract method,是否已經都被實作
- 簡單方法處理,直接在 class 上加一個底線(自己斷定是否可被實體化),如
class _xxx
,來區分是否是 abstarct class (base class),以滿足 @abstract 的需求
實作 Java 的 @Override
- 屬於 Case2 的範例
# 在 python 裡, annotation 一般都是用全小寫表示,如 @staticmethod, @classmethod
def override(interface):
print('override:interface:', interface)
def overrider(method):
print('override:overrider:method:', method)
# https://goo.gl/GWWfwm The assert statement
if (method.__name__ in dir(interface)) == False:
raise AssertionError(
"the method '{}' does not exist in {}".format(
method, interface.__name__))
# end-ofif
return method # optional
# end-of-def
return overrider # 回傳 method 指標
# end-of-def
class MyInterface:
def say_hello_world(self):
raise Exception("need to implement 'say_hello_world'")
# end-of-def
# end-of-class
class MyObject(MyInterface):
@override(MyInterface)
def say_hello_world(self):
print("Hello, World!")
# end-of-def
@override(MyInterface)
def print_hello_world(self): # compilation error
print("Hello, World!")
# end-of-def
# end-of-class
執行結果:
override:interface: <class '__main__.MyInterface'>
override:overrider:method: <function MyObject.say_hello_world at 0x7f51c85c1620>
override:interface: <class '__main__.MyInterface'>
override:overrider:method: <function MyObject.print_hello_world at 0x7f51c8573268>
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
...
AssertionError: the method '<function MyObject.print_hello_world at 0x7f51c859b158>' does not exist in MyInterface