勇闖新世界︰ 《 Kernel 4.X 》之整裝蓄勢‧設備管理及應用‧三上

假使從『裝置的程式介面』 API 來觀察『裝置』是什麼?『裝置』不過就是透過那個『 API 規範』存取的 □□○○!這可引發了一個『大哉問』?如果說某種『物理裝置』事實上沒有連上系統,但是它的『API 介面』卻『存在』且能『運作』,那麼我們『能不能』知道那個『裝置』實際上不存在的呢??這個問題其實比表面深沈 的多,一直延伸到『知識論』的哲學爭議之上。比方說,什麼是『蘋果樹 』?如何判定『某個水果』是『蘋果』?最終總容易落入用『外在屬性』來作『歸類』的事實裡。更不要講過去的『接枝』到現今的『基改』,所作的『跨界』之事!也許當『界線』越來越模糊,『什麼是什麼』的問題終將越來越不清楚的耶!!

這『何謂鴨子』之事也發生在『物件導向』的語言中。維基百科的『鴨子型別』講︰

當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。

Duck typing

In computer programming with object-oriented programming languages, duck typing is a layer of programming language and design rules on top of typing. Typing is concerned with assigning a type to any object. Duck typing is concerned with establishing the suitability of an object for some purpose. With normal typing, suitability is assumed to be determined by an object’s type only. In duck typing, an object’s suitability is determined by the presence of certain methods and properties (with appropriate meaning), rather than the actual type of the object.

The name of the concept refers to the duck test, attributed to James Whitcomb Riley, which may be phrased as follows:

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.[1]

In duck typing, a programmer is only concerned with ensuring that objects behave as demanded of them in a given context, rather than ensuring that they are of a specific type. For example, in a non-duck-typed language, one would create a function that requires that the object passed into it be of type Duck, in order to ensure that that function can then use the object’s walk and quack methods. In a duck-typed language, the function would take an object of any type and simply call its walk and quack methods, producing a run-time error if they are not defined. Instead of specifying types formally, duck typing practices rely on documentation, clear code, and testing to ensure correct use.

───

Criticism

One issue with duck typing is that it forces programmers to have a much wider understanding of the code they are working with at any given time. For instance, in Python, one could easily create a class called Wine, which expects a class implementing the “press” attribute as an ingredient. However, a class called Trousers might also implement the press() method. With duck typing, in order to prevent strange, hard-to-detect errors, the developer needs to be aware of each potential use of the method “press”, even when it’s conceptually unrelated to what they are working on. By way of contrast, in a strongly and statically typed language that uses type hierarchies and parameter type checking, it’s much harder to supply an unexpected object type to a class. For example, in a language like Java, the ambiguity in the above reuse of the method name press() would not be a problem unless one of the two classes was deliberately defined as a child of the other.

Proponents of duck typing, such as Guido van Rossum, argue that the issue is handled by testing, and the necessary knowledge of the codebase required to maintain it.[4][5]

Criticisms around duck typing tend to be special cases of broader points of contention regarding dynamically typed versus statically typed programming language semantics.

 

所以,什麼是『電腦滑鼠』的呢?若是以『核心輸入子系統』以及『python-evdev 程式庫』的觀點而言,就是能『產生』某些『事件型別』的東東罷了!

【滑鼠參考說明】

pi@raspberrypi ~ python3 Python 3.2.3 (default, Mar  1 2013, 11:53:50)  [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information.  >>> from evdev import InputDevice, list_devices  # 取得系統所有的輸入設備 >>> 輸入裝置列 =  [InputDevice(fn) for fn in list_devices()] >>> for 裝置 in 輸入裝置列: ...     print(裝置.fn, 裝置.name, 裝置.phys) ...  /dev/input/event3 Logitech USB Receiver usb-bcm2708_usb-1.5/input1 /dev/input/event2 Logitech USB Receiver usb-bcm2708_usb-1.5/input0 /dev/input/event1 Logitech USB Receiver usb-bcm2708_usb-1.4/input1 /dev/input/event0 Logitech USB Receiver usb-bcm2708_usb-1.4/input0 >>>   # 選擇電腦滑鼠 >>> 裝置 = InputDevice('/dev/input/event2') >>> print(裝置) device /dev/input/event2, name "Logitech USB Receiver", phys "usb-bcm2708_usb-1.5/input0"  # 裝置詳細功能 >>> 裝置.capabilities(verbose=True) {('EV_MSC', 4): [('MSC_SCAN', 4)], ('EV_KEY', 1): [(['BTN_LEFT', 'BTN_MOUSE'], 272), ('BTN_RIGHT', 273), ('BTN_MIDDLE', 274), ('BTN_SIDE', 275), ('BTN_EXTRA', 276), ('BTN_FORWARD', 277), ('BTN_BACK', 278), ('BTN_TASK', 279), ('?', 280), ('?', 281), ('?', 282), ('?', 283), ('?', 284), ('?', 285), ('?', 286), ('?', 287)], ('EV_REL', 2): [('REL_X', 0), ('REL_Y', 1), ('REL_HWHEEL', 6), ('REL_WHEEL', 8)], ('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('SYN_MT_REPORT', 2), ('?', 4)]} >>> from evdev import categorize, ecodes >>>  >>> 裝置.capabilities() {0: [0, 1, 2, 4], 1: [272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287], 2: [0, 1, 6, 8], 4: [4]} >>>   # 電腦滑鼠功能測試 >>> 滑鼠裝置 = 裝置  # 產生相對移動事件 >>> for 事件 in 滑鼠裝置.read_loop(): ...     if 事件.type == ecodes.EV_REL: ...         print(categorize(事件)) ...         print(事件) ...  relative axis event at 1439779280.080989, REL_WHEEL  event at 1439779280.080989, code 08, type 02, val -1 relative axis event at 1439779283.809490, REL_WHEEL  event at 1439779283.809490, code 08, type 02, val 01 relative axis event at 1439779287.810024, REL_X  event at 1439779287.810024, code 00, type 02, val -1 relative axis event at 1439779287.810024, REL_Y  event at 1439779287.810024, code 01, type 02, val 01 relative axis event at 1439779287.954040, REL_X  event at 1439779287.954040, code 00, type 02, val -1 relative axis event at 1439779287.986048, REL_X  event at 1439779287.986048, code 00, type 02, val -1 relative axis event at 1439779288.002045, REL_X  event at 1439779288.002045, code 00, type 02, val -1 relative axis event at 1439779288.018041, REL_X  event at 1439779288.018041, code 00, type 02, val -3 relative axis event at 1439779288.018041, REL_Y  event at 1439779288.018041, code 01, type 02, val 01 relative axis event at 1439779288.034044, REL_X  event at 1439779288.034044, code 00, type 02, val -1 relative axis event at 1439779288.114064, REL_Y  event at 1439779288.114064, code 01, type 02, val 01 relative axis event at 1439779307.764695, REL_X  event at 1439779307.764695, code 00, type 02, val 01 ^CTraceback (most recent call last):   File "<stdin>", line 1, in <module>   File "/usr/local/lib/python3.2/dist-packages/evdev/device.py", line 280, in read_loop     r, w, x = select([self.fd], [], []) KeyboardInterrupt >>>   # 產生按鍵事件 >>> for 事件 in 滑鼠裝置.read_loop(): ...     if 事件.type == ecodes.EV_KEY: ...         print(categorize(事件)) ...         print(事件) ...  key event at 1439779469.418293, 272 (['BTN_LEFT', 'BTN_MOUSE']), down event at 1439779469.418293, code 272, type 01, val 01 key event at 1439779469.626321, 272 (['BTN_LEFT', 'BTN_MOUSE']), up event at 1439779469.626321, code 272, type 01, val 00 key event at 1439779472.474709, 273 (BTN_RIGHT), down event at 1439779472.474709, code 273, type 01, val 01 key event at 1439779472.762738, 273 (BTN_RIGHT), up event at 1439779472.762738, code 273, type 01, val 00 key event at 1439779477.195335, 274 (BTN_MIDDLE), down event at 1439779477.195335, code 274, type 01, val 01 key event at 1439779477.627399, 274 (BTN_MIDDLE), up event at 1439779477.627399, code 274, type 01, val 00 ^CTraceback (most recent call last):   File "<stdin>", line 1, in <module>   File "/usr/local/lib/python3.2/dist-packages/evdev/device.py", line 280, in read_loop     r, w, x = select([self.fd], [], []) KeyboardInterrupt >>>  </pre>    <span style="color: #666699;">因此所謂用『python-evdev』來寫程式,處理的就是『裝置事件』的『解讀』與『產生』而已!!</span>  <span style="color: #666699;">【鍵盤補充範例】</span> <pre class="lang:sh decode:true   ">pi@raspberrypi ~ python3
Python 3.2.3 (default, Mar  1 2013, 11:53:50) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from evdev import InputDevice, list_devices, categorize, ecodes

>>> 鍵盤 = InputDevice('/dev/input/event0')

>>> 鍵盤.capabilities(verbose=True)
{('EV_LED', 17): [('LED_NUML', 0), ('LED_CAPSL', 1), ('LED_SCROLLL', 2), ('LED_COMPOSE', 3), ('LED_KANA', 4)], ('EV_MSC', 4): [('MSC_SCAN', 4)], ('EV_KEY', 1): [('KEY_ESC', 1), ('KEY_1', 2), ('KEY_2', 3), ('KEY_3', 4), ('KEY_4', 5), ('KEY_5', 6), ('KEY_6', 7), ('KEY_7', 8), ('KEY_8', 9), ('KEY_9', 10), ('KEY_0', 11), ('KEY_MINUS', 12), ('KEY_EQUAL', 13), ('KEY_BACKSPACE', 14), ('KEY_TAB', 15), ('KEY_Q', 16), ('KEY_W', 17), ('KEY_E', 18), ('KEY_R', 19), ('KEY_T', 20), ('KEY_Y', 21), ('KEY_U', 22), ('KEY_I', 23), ('KEY_O', 24), ('KEY_P', 25), ('KEY_LEFTBRACE', 26), ('KEY_RIGHTBRACE', 27), ('KEY_ENTER', 28), ('KEY_LEFTCTRL', 29), ('KEY_A', 30), ('KEY_S', 31), ('KEY_D', 32), ('KEY_F', 33), ('KEY_G', 34), ('KEY_H', 35), ('KEY_J', 36), ('KEY_K', 37), ('KEY_L', 38), ('KEY_SEMICOLON', 39), ('KEY_APOSTROPHE', 40), ('KEY_GRAVE', 41), ('KEY_LEFTSHIFT', 42), ('KEY_BACKSLASH', 43), ('KEY_Z', 44), ('KEY_X', 45), ('KEY_C', 46), ('KEY_V', 47), ('KEY_B', 48), ('KEY_N', 49), ('KEY_M', 50), ('KEY_COMMA', 51), ('KEY_DOT', 52), ('KEY_SLASH', 53), ('KEY_RIGHTSHIFT', 54), ('KEY_KPASTERISK', 55), ('KEY_LEFTALT', 56), ('KEY_SPACE', 57), ('KEY_CAPSLOCK', 58), ('KEY_F1', 59), ('KEY_F2', 60), ('KEY_F3', 61), ('KEY_F4', 62), ('KEY_F5', 63), ('KEY_F6', 64), ('KEY_F7', 65), ('KEY_F8', 66), ('KEY_F9', 67), ('KEY_F10', 68), ('KEY_NUMLOCK', 69), ('KEY_SCROLLLOCK', 70), ('KEY_KP7', 71), ('KEY_KP8', 72), ('KEY_KP9', 73), ('KEY_KPMINUS', 74), ('KEY_KP4', 75), ('KEY_KP5', 76), ('KEY_KP6', 77), ('KEY_KPPLUS', 78), ('KEY_KP1', 79), ('KEY_KP2', 80), ('KEY_KP3', 81), ('KEY_KP0', 82), ('KEY_KPDOT', 83), ('KEY_ZENKAKUHANKAKU', 85), ('KEY_102ND', 86), ('KEY_F11', 87), ('KEY_F12', 88), ('KEY_RO', 89), ('KEY_KATAKANA', 90), ('KEY_HIRAGANA', 91), ('KEY_HENKAN', 92), ('KEY_KATAKANAHIRAGANA', 93), ('KEY_MUHENKAN', 94), ('KEY_KPJPCOMMA', 95), ('KEY_KPENTER', 96), ('KEY_RIGHTCTRL', 97), ('KEY_KPSLASH', 98), ('KEY_SYSRQ', 99), ('KEY_RIGHTALT', 100), ('KEY_HOME', 102), ('KEY_UP', 103), ('KEY_PAGEUP', 104), ('KEY_LEFT', 105), ('KEY_RIGHT', 106), ('KEY_END', 107), ('KEY_DOWN', 108), ('KEY_PAGEDOWN', 109), ('KEY_INSERT', 110), ('KEY_DELETE', 111), (['KEY_MIN_INTERESTING', 'KEY_MUTE'], 113), ('KEY_VOLUMEDOWN', 114), ('KEY_VOLUMEUP', 115), ('KEY_POWER', 116), ('KEY_KPEQUAL', 117), ('KEY_PAUSE', 119), ('KEY_KPCOMMA', 121), (['KEY_HANGEUL', 'KEY_HANGUEL'], 122), ('KEY_HANJA', 123), ('KEY_YEN', 124), ('KEY_LEFTMETA', 125), ('KEY_RIGHTMETA', 126), ('KEY_COMPOSE', 127), ('KEY_STOP', 128), ('KEY_AGAIN', 129), ('KEY_PROPS', 130), ('KEY_UNDO', 131), ('KEY_FRONT', 132), ('KEY_COPY', 133), ('KEY_OPEN', 134), ('KEY_PASTE', 135), ('KEY_FIND', 136), ('KEY_CUT', 137), ('KEY_HELP', 138), ('KEY_F13', 183), ('KEY_F14', 184), ('KEY_F15', 185), ('KEY_F16', 186), ('KEY_F17', 187), ('KEY_F18', 188), ('KEY_F19', 189), ('KEY_F20', 190), ('KEY_F21', 191), ('KEY_F22', 192), ('KEY_F23', 193), ('KEY_F24', 194), ('KEY_UNKNOWN', 240)], ('EV_SYN', 0): [('SYN_REPORT', 0), ('SYN_CONFIG', 1), ('?', 4), ('?', 17), ('?', 20)]}

>>> 鍵盤.capabilities()
{0: [0, 1, 4, 17, 20], 1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 113, 114, 115, 116, 117, 119, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 240], 4: [4], 17: [0, 1, 2, 3, 4]}

>>> 鍵盤.leds(verbose=True)
[]

>>> for 事件 in 鍵盤.read_loop():
...     if 事件.type == ecodes.EV_KEY:
...         print(categorize(事件))
...         print(事件)
... 
key event at 1439784566.338788, 20 (KEY_T), down
event at 1439784566.338788, code 20, type 01, val 01
key event at 1439784566.418811, 20 (KEY_T), up
event at 1439784566.418811, code 20, type 01, val 00
key event at 1439784568.283038, 18 (KEY_E), down
event at 1439784568.283038, code 18, type 01, val 01
key event at 1439784568.387043, 18 (KEY_E), up
event at 1439784568.387043, code 18, type 01, val 00
key event at 1439784569.291174, 31 (KEY_S), down
event at 1439784569.291174, code 31, type 01, val 01
key event at 1439784569.395174, 31 (KEY_S), up
event at 1439784569.395174, code 31, type 01, val 00
key event at 1439784569.659215, 20 (KEY_T), down
event at 1439784569.659215, code 20, type 01, val 01
key event at 1439784569.763225, 20 (KEY_T), up
event at 1439784569.763225, code 20, type 01, val 00
key event at 1439784575.187941, 58 (KEY_CAPSLOCK), down
event at 1439784575.187941, code 58, type 01, val 01
key event at 1439784575.291938, 58 (KEY_CAPSLOCK), up
event at 1439784575.291938, code 58, type 01, val 00
key event at 1439784582.908932, 58 (KEY_CAPSLOCK), down
event at 1439784582.908932, code 58, type 01, val 01
key event at 1439784583.044951, 58 (KEY_CAPSLOCK), up
event at 1439784583.044951, code 58, type 01, val 00
key event at 1439784585.613291, 69 (KEY_NUMLOCK), down
event at 1439784585.613291, code 69, type 01, val 01
key event at 1439784585.709302, 69 (KEY_NUMLOCK), up
event at 1439784585.709302, code 69, type 01, val 00
key event at 1439784588.661688, 69 (KEY_NUMLOCK), down
event at 1439784588.661688, code 69, type 01, val 01
key event at 1439784588.765693, 69 (KEY_NUMLOCK), up
event at 1439784588.765693, code 69, type 01, val 00
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.2/dist-packages/evdev/device.py", line 280, in read_loop
    r, w, x = select([self.fd], [], [])
KeyboardInterrupt
>>>