Skip to content

步入中年的轉職馬拉松 / 一個低端工程師的自白

  • Misc

Intro

在 2019 年底,還在上個公司就職的我,應一個面試邀約拜訪了 91APP。因為拜社群之福,我在早期就有「持續面試很重要」這個概念,不管你當下有沒有工作。

當時的我剛完成一個機器學習的基礎設施,讓整個流程得以自動化,頗為自得意滿。我天真地以為,別人會找我去面試,單純是因為我值得。

也是在這個面試中,我踩了人生第一個面試地雷——那就是抱怨當前的工作。我說:公司專案太多又太雜了,又都只有一兩個人負責。

結果可想而知,在隔天收到了感謝函。不過幸運的是,邀約我的前輩順帶給了一些很客氣的提點。他在信裡這麼說:「你的硬實力是不錯的,但你缺乏軟實力。」

同一個時期,想要離職的念頭開始萌芽。當然,我並非討厭這個工作,反而更像是漸漸找不到工作的意義了。上下班的時候,腦中不停發出例外,可以說它是一種 ValueError ,而當時的我並不知道如何去處理這個例外。

造成這個例外的可能情形:

  • 潛意識地知道自己知識量不足(軟實力),但沒花足夠的時間來彌補,大部分時間都花在拼湊和硬幹上面(硬實力)
  • 腦海中有「標準」的藍圖,但不是很清楚,而公司的標準接近能動就好,因此無所適從
  • 開始懷疑自己,懷疑專案的發展性
  • 一直以來的單打獨鬥,嚮往可以激盪討論合作的環境,說白一點就是希望有人帶
  • 不想再扮黑臉(沒錯我是 INTP)

終於,某天公司的舊系統因為太舊出問題了(單純就是因為太舊),雖然我沒接觸過該系統,但身為後端人員,多少還是覺得有點責任。當下去了一趟 Legacy 後,發覺再也忍受不了了。我喪失了所有自信。我只想讓腦袋重新開機。

我必須馬上去談這件事。當天,我跟主管提出離職的想法。我只知道自己該做出一些改變,或者更深入地說,我害怕因為長時間忽略這些內心感受,進而潛移默化成自己不想變成的那個人,再也做不出正確的決定。我也想起有人說三十歲是工程師生涯重要的臨界點。那麼與其不上不下的,不如去尋求一個長遠的突破。

借托爾斯泰之筆,我們可以這樣隱晦描述──「幸福的 RD 總是幸福的,不幸的 RD 各有各的不幸。」

也就是說,即使在別人眼裡,工程師看起來是那樣吃得好穿得暖,但事實是每個工程師和你們一樣,都有各自難以解決的問題。就如同我的 ValueError

Read More »步入中年的轉職馬拉松 / 一個低端工程師的自白

Fluent Python 讀書筆記(八)

  • Python

類別中繼編程

  • 「中繼類別是比 99% 使用者鎖想的還要艱深的魔法。如果你想知道自己是否須要他們,其實你不需要。」
  • 類別中繼邊程是在執行階段建立/自訂類別的技巧
  • 類別是一級物件,無論什麼時候,都可以用函式來建立新的類別(諸如類別修飾器),不需要 class 關鍵字
  • 中繼類別很強大,但很難正確使用,事實上,很難在實際的程式中使用
  • 編寫中繼程式的先決條件是了解匯入階段與執行階段的差異
  • 「如果你不在製作框架,就不該編寫中繼類別」

自訂一個類似 collections.namedtuple 的簡易紀錄型類別工廠

對這種紀錄型的類別而言,我們希望屬性群都是相同的,而且擁有相同順序

此範例建立的類別有一個限制:無法被序列化,即無法與 picle 模組的 dumpload 函式一起使用

在類別中定義 __slots__,等於告訴解譯器「它們都是這個類別的實例屬性」,這些屬性會被存在一個類 tuple 結構,避免每一個實例的 __dict__ 產生記憶體開銷

當類別指定 __slots__ 時,它的實例無法使用任何指定之外的屬性(這是一種副作用/缺點),單純為了做這種屬性限制而使用 __slots__ 並不是一個好的作法。__slots__ 的目標是最佳化,而不是限制開發者的行為


  • 我們通常會把 type 當成函式來用,但它同時是個類別,如果你用三個引數來呼叫,它的行為就像類別,會實體化一個新的類別
  • 避免使用 execeval 來編寫中繼程式是一種好的習慣。如果不受信任的來源傳送字串/段落給這些函式,會造成嚴重的後果;Python 提供足夠的自我檢查工具,execeval 在大部分情況下沒必要使用
  • 類別描述器有一個重大的缺點在於,它們只能在直接套用的類別上動作——被修飾類別的子類別可能不會繼承修飾器所作的改變
  • 「匯入階段(import time)」、「執行階段(runtime)」這些用語沒有經過嚴謹定義,而且它們之間有灰色地帶
  • 在匯入階段,解譯器會完整解析 .py 模組的程式碼,並產生執行的 bytecode——這就是可能會發生語法錯誤的地方(如果在本地的 __pycache__ 有最新的 .pyc,這個步驟會被跳過)
  • 雖然編譯(compiling)的確是匯入階段的動作,但這個階段也會發生其他事情——特別是 import 陳述式,它並非只是一個宣告(對比 Java 的 import)——當程序首次匯入模組時(尚無快取時),它會執行被匯入模組的所有最高層級的程式碼,包含執行階段的行為
  • 最高層級的程式碼特指類別的內文(包含嵌套的類別),解譯器會在匯入階段執行它們;相反地,對函式而言,解譯器只會編譯內文、將函式物件加到全域名稱,但不會執行函式內文
    • 類別若是有使用類別修飾器,該修飾器函式也會被執行
    • 類別的中繼類別的方法 __init__ 也會被執行

Read More »Fluent Python 讀書筆記(八)

Fluent Python 讀書筆記(七)

動態屬性與特性

  • 方法(method)只是一種可以被呼叫的屬性(attribute)
  • 特性(property)可以用來代替公開的資料屬性,不會變動到類別介面
  • 編寫動態屬性,是框架作者會採取的一種中繼編程(metaprogramming)
  • 從任意來源產生或模擬動態屬性名稱,都要處理一個問題;原始資料中的 key 可能不適合當成屬性名稱,例如 key 是關鍵字(keyword.iskeyword())或非法的識別符(s.isidentifier()

範例:使用動態屬性來探索 JSON 格式的資料

透過遞迴來建構,可自動處理嵌套的映射與串列

留意這裡沒有對查詢進行任何快取或轉換


建構實例的特殊方法是類別方法 __new__,他可以回傳完全不同的實例,在這種情況下解譯器不會呼叫 __init__

以下為建構實例的虛擬程式

Read More »Fluent Python 讀書筆記(七)

Mindhunter: Inside the FBI’s Elite Serial Crime Unit

  • Quote

值法人員的辛酸很難跟人說,甚至無法和伴侶討論。當你看了一整天的屍體和截肢,如何能把這話題帶回家?尤其當死者是小孩時更是不堪。你無法在吃飯時說:「我今天辦了一件強暴謀殺案。我把這個案件情節說給你們聽聽。」這也是為何警察常和護理人員結婚的原因,反之亦然,這樣他們才能體會彼此的工作。

依我之見,這些心理治療師、心理學家和社工人員都很年輕,又充滿理想主義,覺得可以改變他們。其實這些人常常不明白一點,那就是當他們在評估這些罪犯時,他們所評估的正是一群評估他人的專家。

差別在於,從事心理工作的專業人員是從人格開始,從這個角度推衍出行為;我和我的同事卻是先從行為開始,然後從行為的角度推衍其人格。

犯罪行為與其說是心理疾病,不如說是個性上的缺失。

「心神喪失」(insanity)是一個法律觀念,而非醫學或心理治療上的術語,它並不表示某人是不是「有病」,而只是表示某人能不能為其行為負責。

美國人有種認為事情總會變好的特質,永遠都有機會改善,只要我們去做,任何事情都可以做到。但是我看得愈多,對於某些類型犯人的更生就愈是悲觀。他們童年所經歷的通常都是很可怕的事,這表示他們遭受的傷害難以在日後復原。和法官、辯護律師、精神專業人士願意相信的正好相反,獄中行為良好不見得就表示出了監獄會有正當的行為。

我常開玩笑:如果一名人犯說他有多重人格,那麼只要我抓到犯罪的那個人格,我就會把無辜的那個人格放走。

任何做得出如此駭人可怕的事的人,一定是真正「有病」——我不會反對這種看法。但是他知道他正在做的事是錯的嗎?他是不是仍然選擇要去做?對我而言,這才是重要的問題。

從沒有一個連續殺人犯會「身不由己」到敢當著警察的面去殺人。

別人認為他們很怪,也不會認真看待他們,於是問題隨著時間惡化。他們會把注意力放在一項任務上,使他們的生命有些意義。這是他們頭一次感受到自己有控制力,而他們喜歡這種感覺,這一點會使他去冒更大的險,而愛冒險的人都具有危險性。

如果我看到信中的語氣愈來愈嚴厲,急迫(「你沒有回我的信!」),我就會正視這件事。持續這種妄想且強迫式的嚴厲,不論在心理上或心靈上都是耗盡心力的事。要不了多久,這人就會開始崩潰。

我們只能以一種實驗的權威性說:女性似乎將所有感受內化了。她們不會去攻擊他人,而比較容易藉酗酒、吸毒、賣淫及自殺來懲罰自己。有些人則會在自己的家庭裡重複所受到的身心凌虐……從心靈健康的角度來看,這是非常有傷害力的。但是女性殺人的方式和行兇的人數與男性大相逕庭,這確實是個事實。

如果你認為解決之道在於學校,你也要求太多了。

更多警察、更多法院、更多監獄、更加的調查技巧誠然不錯,但是要減少犯罪的唯一方法,是要問我們全部人,是不是都能夠不再接受、容忍發生在我們家庭、朋友、同僚身上的犯罪。這是得自犯罪數遠比我們國家低許多的其他國家的教訓。以我的觀點,只有這種根本的解決之道才有效。犯罪是一個道德問題,只有從道德層面才能夠解決。

Fluent Python 讀書筆記(六)

此筆記適用 Python 3.4,部份已不相容後續版本的套件與 API

以 futures 撰寫並行

  • futures —— 非同步執行某項操作的物件
  • 網路 I/O 涉及高度延遲,須要用到並行來有效處理
  • 在公用 Web 測試並行 HTTP 用戶端,你可能會無意中啟動一個阻斷服務攻擊(DOS),或被懷疑在做這件事…要測試複雜的用戶端,你應該設定自己的測試伺服器(作法說明
  • requests 已經被視為 Python 式 API 的典範,它比標準函式庫 urllib.request 還要強大
  • concurrent.futures 主要功能是能讓你分別提交(submit)在不同執行緒/程序執行的可呼叫物(callables),實作於 ThreadPoolExecutorProcessPoolExecutor 的介面(分別在內部管理一個 worker thread pool 及 process pool)

多執行緒並行起手勢

使用單純迴圈來取代 ThreadPoolExecuter.map

使用手動呼叫 result 取代隱式的 next 呼叫(阻塞式):

使用 as_completed 取代阻塞式的結果查詢(完成後才產出),必須放在 Context Manager 區塊內,因為預設的 __exit__ 會阻塞:

Read More »Fluent Python 讀書筆記(六)

Fluent Python 讀書筆記(五)

  • Python

控制流程

  • 在大部分情況下,Python 社群將 Iterator 與 Generator 視為同義詞
  • Python 所有集合都是可迭代的
  • 內部的 for 迴圈、集合生成式、變數和引數的 Unpacking 都會用到 Iterator
  • iter() 會先參考 __iter__,其次才參考 __getitem__,都沒有的話,發出 TypeError 代表「該物件不可迭代」(此處 __getitem__ 的參考在以後可能被棄用)
  • 承上,可迭代物件不一定滿足 isinstance(C, abc.Iterable)(在未實作 __iter__ 的情況下),為了避免這個誤區,要判斷物件是否可迭代,最準確的方式是呼叫 iter() 看看
  • 如果 iter() 會過,那物件是「Iterable」;實作 __iter__ ,須回傳一個「Iterator 實體」—— Python 會跟 Iterable 索取 Iterator
  • Iterator 類別的標準介面:__iter____next____next__ 負責回傳下一個項目或發起 StopIteration,__iter__ 則單純回傳 self
  • 不要把 Iterable 跟 Iterator 混為一談,「Iterable 有一個 __iter__ 方法,這個方法每次都會實例化一個新的 Iterator」
  • Iterator 也是 Iterable,但 Iterable 不是 Iterator。Iterable 永遠不該扮演自己的 Iterator
  • Iterator 獨立出來的用意是「每一個迭代器都能保存它自己的內部狀態」
  • 除了回傳獨立的 Iterator 實體,也可以將 __iter__ 變成一個「Generator 函式」,藉由回傳一個「Generator 實體」,以介面而言,Generator 是 Iterator,它會在內文結束時發出 StopIteration
  • 用一個「 lazy 的產生器」取代一個「儲存所有資料的迭代器實體」是更好的,因為只要在必要時(最後一刻)才產生值,可以節省大量記憶體
  • Iterator 的另一個功能是「延緩工作」、「一次只產生一個項目」
  • 「當你在用 Python3 想著『有更 lazy 的作法嗎?』的時候,答案通常都是『有』」
  • yield from 不只是一個糖衣語法,除了取代迴圈之外,它也是一個管道,連接外部產生器,接收外部產生器的值
  • allany 有一種重要的優化是 reduce 無法作到的,那就是 short-circuit,確定結果後就停止
  • sorted 可以接收任意的 Iterable
  • iter() 的另一個功能:傳入一個 Callable 及一個標記值(sentinel),當回傳值等同此標記時,停止迭代
  • 無論資料大小為何,Generator 提供一種有彈性的解決方案,把大型資料集當做資料流來處理
  • .send() 同樣會讓產生器進入下一個 yield,但是它也可以用來傳入資料,相較於 next() 單純接收資料,.send() 可讓使用者與產生器雙向交換資料——變成協同程序 (coroutines)
  • 「在內文埋入一個 yield,不足以提醒那一個語意有如此不同」(但 Guido 討厭使用新的關鍵字)
  • 以實作而言,Generator 是一種語言結構,以函式或表達式編寫,呼叫時回傳 GeneratorType
  • 以概念而言,不管 Iterator 內部有多複雜(例如是一個樹狀資料結構),它的資料永遠只有一個來源(自己本身);至於產生器,則不一定只產生集合裡面的項目
  • 「Iterator 最簡單的介面是由 First、Next、IsDone、CurrentItem 的操作組成」,在 Python 它的介面更精簡:next()StopIteration

Read More »Fluent Python 讀書筆記(五)

Bitnami