探討 Python 的 Annotation 註解 - tsungjung411/python-study GitHub Wiki

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

 

參考資料