Skip to content

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 讀書筆記(五)

Fluent Python 讀書筆記(四)

  • Python

介面:從協定到 ABC

  • 「抽象類別代表介面」
  • Python 自 2.6 版本之後加入 ABC (abstact base class),大多被定義在 collections.abc 模組
  • 當你需要實作介面時,第一步是將它們當成超類別 (superclasses),ABC 會檢查具體子類別是否符合這個介面
  • ABC 與描述器 (descriptors)、中繼類別(metaclasses)一樣,是建構框架的工具,過度使用 ABC 的風險是非常高的
  • 可以把介面想成「某個物件的公用方法的子集合(subsets)」,這個子集合可以在系統中發揮具體的作用(常在文件看到 “a file-like object”、”an iterable” 的字眼都是在指涉這件事)
  • 協定(protocal) 是非正式的介面,只由文件與慣例定義,無法被強制實施,例如:選擇只實作序列協定的某些方法如 __getitem__,而不是繼承 abc.Sequence
  • Python 資料模型的哲學,就是盡可能地與基本協定合作
  • isinstance(obj, cls) 沒有那麼糟,只要 cls 是一個 ABC
  • 所謂的 goose typing ,是相對於協定的 duck typing,鼓勵我們可以去實作 ABC 的介面(透過繼承而非自造輪子)
  • Python ABC 有類別方法 register 可以讓使用者「宣告」某個類別是 ABC 的一個「虛擬子類別 (virtual subclasses)」,而不用實際的繼承,簡單來說就是讓 Python 相信我們會實作介面而不實際檢查(如果有任何問題,就讓在執行階段拋出例外吧)
  • 除了透過函式呼叫來註冊,在 Python 3.4 之後提供了類別修飾器 @<ABC classname>.register
  • 有些子類別不一定要明確的註冊或繼承,也可以成為特定 ABC 的子類別,例如 __len__ 之於 abc.Sized(背後是透過 __subclasshook__ 來實現的,類似的實作少之又少)
  • 不要在程式中自訂 ABC 或 metaclass」—— 從 ABC 繼承方法比實作需要的方法還要好,ABC 的目的是封裝因為框架而產生的一般性、抽象概念,例如這是一個「序列」與「確切的數字」
  • 「ABC 的流行可能是個災難,它對語言施加過度的儀式」
  • numbers 裡面定義了數值的 ABC,最頂層的超類別是 numbers.Number

image

  • IndexErrorKeyError 都是 LookupError 的子類別
  • 宣告 ABC 有兩種方式: 1. 繼承 abc.ABC(3.4 之後才加入) 2. 指定 metaclass=abc.ABCMeta (3~3.4 的限定作法)
  • 諸如 @abstractclassmethod 的冗員裝飾器已被 ABC 棄用,要用的話,只要單純疊加 @classmethod@abc.abstractmethod 即可(要注意順序)
  • 「雖然 ABC 有助於型態檢查,但不應該過度使用它。Python 的核心是動態語言,到處限制型態,可能會讓程式變成沒必要的複雜」
  • 型態提示 (type hints) 是註釋的一種,可以在函式定義中指名參數的型態及回傳何種型態,沒有強致力

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

以其人之道還其人之身:如何用分治法對付 LeetCode / 刷題心得跟題庫分享

  • Misc

Intro

是的,當你看到這篇文章的時候,我還沒找到工作。因此,最近的幾個月,我如果不是在刷題,就是在沒網路的路上。

截至目前為止,我嘗試了將近 300 種不同題形的解法,有效題數是 209,約 1000 多次 commit。

刷題時間久了,對 LeetCode 這個東西不禁感到又愛又恨。甚至是恨的層次居多,為什麼?我認為有以下幾個原因:

真的用得到嗎?

當然,像 146. LRU Cache253. Meeting Rooms II 這種貼近實務的題目是再好不過了,但你不得不承認,大部分的題目還是有那麼派不上用場的意味在,那種感覺就像你在高中學了艱澀的數學理論,但並不知道怎麼在真實世界中應用它們一樣。

試想一下,你拿刷題的時間去嗑一本有關程式設計的書,或去想辦法弄些什麼專案,學到的「馬上可用的知識點」應該都比刷題多得更多。

應徵的公司在場上覆蓋三張陷阱卡,其中一張是神的宣告

你有沒有覺得,現在好多公司都好愛考刷題,先是聯繫到你,告訴你說「我覺得你很可能就是我們要找的 XXX」,然後也不跟你約時間,就丟一個考試連結給你。

然後你可能沒辦法在 20 分鐘內「把零移到前面」或找不到「買賣股票的進出點」,然後就失去了面試機會,或者再也沒有下文。這代表了一件事——開發者的價值被 LeetCode 給過度簡單地歸類了——如果你不刷題,不管你曾經開發過什麼,都不再有意義,因為你連門票都拿不到。

這種面試體驗,我至少就遇到過三次。

是的,即使你知道什麼是陣列、什麼是 swap、indexing,你也知道時間複雜度跟空間複雜度是在講什麼,但你就是想不出來怎麼把該死的 0 給移到前面去。

還記得 Homebrew 的開發者 Max Howell 因為不會翻轉二元樹被 Google 拒絕了,他氣得發了一篇反諷推文。不過他事後表明:我某種程度來說也很爛,我還是搞不太懂二元樹是什麼,但我不怪 Google 了。

這個例子可以很好地概括我目前對面試考題的想法:Get Over It。

當刷題儼然成為找工作的「內建」、必要條件,那麼也許,我們就不要再 ㄍ一ㄥ 了吧。

Read More »以其人之道還其人之身:如何用分治法對付 LeetCode / 刷題心得跟題庫分享

Fluent Python 讀書筆記(三)

物件參考、可變性與重複使用

  • 「變數是標籤,不是盒子」
  • 使用參考變數 (reference variable) 時,說「變數被指派給一個物件」會比較合理,畢竟——物件是在賦值之前建立的
  • 兩個變數被指派到同一個物件時,這兩個變數互為「別名(alias)」
  • 「每一個物件都有一個身份(ID)、一個型態跟一個值」,在 CPython,這個身份是 id(),回傳物件的記憶體位置(不同解譯器可能會使用不同東西作為 ID)
  • == 比較物件的值;is 比較物件的 ID
  • is== 快,因為它無法多載(不需要尋找或呼叫特殊方法來演算出一個值)
  • 原始物件的 __eq__ 會比較 ID,但大多數覆寫 __eq__ 的情況通常會加入或使用別的比較
  • tuple 不可變的意思是「保存在它當中的物件參考 ID 不變」,即使 tuple 可能存了可變的物件
  • 淺複製 (shallow copy) 即容器本身會被複製,但新的容器裡面保存的是舊的參考,例如 arr[:]arr.copy()copy(arr)
  • 實作 deep copy 要小心物件可能會循環參考 (Ring),要判斷物件是否已經複製過
  • 覆寫 __copy____deepcopy__ 可以控制 copy.copy()copy.deepcopy() 的行為
  • Python 函式傳遞的是參考(call by sharing) —— 即函數的參數 (parameter) 會指向引數 (argument) 的參考,換句話說,「函式內的參數就是其實際引數的別名」
  • 同上,這也是為什麼「函式的預設參數不要使用可變型態」,簡單的改良:預設為 None,在函式中判斷是否初始化新的可變物件
  • del 刪除的是參考,而不是物件本身;物件只有在「參考數量變成零」的情況下才有可能被回收,這種銷毀可能不是立即性的
  • CPython 回收記憶體的演算法主要是計算參考數量,這個參考數量存在物件本身,但假若有循環參考時,容易發生 memory leak
  • 在 CPython 的實作下,對 tuplestrbytes而言 s[:] 不會製作複本,而是回傳物件的參考
  • 在使用執行緒時,修改可變物件很難得到正確的結果:無法適當同步的執行序,會導致資料損毀;過度同步的執行序,會造成 deadlock

弱參考 (Weak Reference)

  • 常用在使用快取的情境下,須要「參考一個不會被保存太久的物件」
  • 弱參考是一種可呼叫的物件,它會回傳參考的物件,或者 None
  • 使用弱參考而非賦值,就不會讓物件的「參考數量」增加
  • 考慮使用 WeakKeyDictionaryWeakValueDictionaryWeakSetfinalize 這些內部使用弱參考的高階界面,而非自己用 weakref.ref 實作
  • 因為實作的限制,listdict 的子類別可以被弱參考(原始型態不行),而 inttuple 則完全無法被弱參考


字串常值的共用,是一種優化技術,稱為 interning,Cpython 會對小型的整數使用相同的技術,來避免沒必要的重複

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