7

要生成輸出,函數通常只使用其參數的值。但是,也有一些情況下,函數會生成其輸出,從文件系統或數據庫或Web中讀取某些內容。我希望有一個簡單而可靠的方法來確保這樣的事情不會發生。如何確保python函數僅基於其輸入生成其輸出?

我看到的一種方法是創建一個可用於從文件系統,數據庫或Web讀取的Python庫的白名單。但是,如果要走的話,我在哪裏可以得到這個(可能是巨大的)列表。此外,我不想僅僅因爲它可以用於從文件系統中讀取而禁用整個庫。例如,我希望用戶能夠使用熊貓庫(用於存儲和操作表格數據)。我只是不希望他們能夠使用這個庫從文件系統讀取數據。

是否有一個解決這個問題?

+1

備份一步。 *爲什麼*你想阻止某人從外部來源閱讀? – chepner 2014-10-10 15:40:25

+0

有很多原因。首先,我想確保將來該功能將產生與今天相同的輸出。其次,總的來說,當某個功能讀取某些內容時,我認爲這是一個「醜陋」的解決方案。它應該只看到它作爲輸入顯式接收的內容。如果應該從文件或數據庫讀取某些內容,則應該在函數外部讀取它,並將其作爲其輸入之一傳遞給該函數。 – Roman 2014-10-10 15:44:39

+0

所以你想使用你不信任的代碼? – 2014-10-10 15:46:05

回答

8

對此的回答是。你正在尋找的是一個功能,測試functional purity。但是,正如本代碼所展示的那樣,沒有辦法保證沒有副作用被實際調用。

class Foo(object): 
    def __init__(self, x): 
     self.x = x 
    def __add__(self, y): 
     print("HAHAHA evil side effects here...") 
     # proceed to read a file and do stuff 
     return self 

# this looks pure... 
def f(x): return x + 1 

# but really... 
>>> f(Foo(1)) 
HAHAHA evil side effects here... 

由於全面對象可以重新定義他們的行爲(現場訪問,調用,操作符重載等),你可以隨時通過輸入使純函數不純。因此,唯一的純函數就是那些對它們的論點毫不起作用的函數......一類通常不太有用的函數。

當然,如果你可以指定其他限制,這變得更容易。

+0

在你的例子中,「邪惡效應」發生是因爲該函數的用戶做了一些「壞事」(用戶用「壞」參數稱爲「好」函數)。就我而言,我是該函數的用戶。所以,我不會以「壞」的方式調用函數。我只需要確定我使用的功能不是「壞」的。 – Roman 2014-10-14 07:32:07

+0

提前知道這一點很重要... – PythonNut 2014-10-14 18:01:03

+2

@羅曼:你的要求原本要強得多。您寫道:「我希望用戶能夠......我只是不希望他們能夠使用此庫從文件系統讀取數據。」現在你在寫「我不會以不好的方式調用函數」。這似乎不尋常。你相信用戶,但不相信已安裝的軟件? – hynekcer 2014-10-14 21:38:43

4

即使您刪除了所有模塊和所有功能,您所需的限制仍可能被破壞。如果代碼可以使用任意簡單對象的屬性,則該代碼可以獲得訪問文件,例如,數字爲零。

(0).__class__.__base__.__subclasses__()[40]('/etc/pas'+'swd') 

索引40是個人和用於Python 2.7非常典型的,但子類<type 'file'>的索引可以很容易地發現:

[x for x in (1).__class__.__base__.__subclasses__()if'fi'+'le'in'%s'%x][0](
'/etc/pas'+'swd') 

的白名單和黑名單中的任何組合或者是不安全的和/或太嚴格了。 的pypy sandbox是原則,穩健的毫不妥協:

...此子可運行任意不可信的Python代碼,但所有 其輸入/輸出序列化到一個標準輸入/輸出管,而不是被 直接執行。外進程讀取管道,並決定哪些 命令允許或不允許(沙盒),甚至重新詮釋他們 不同...

還可根據seccomp內核功能的解決方案可以足夠安全。 (blog


我想,以確保在未來的作用將產生 輸出今天一樣。

這是很容易寫出具有難以重現的結果的功能,它不能很容易預防:

class A(object): 
    "This can be any very simple class" 
    def __init__(self, x): 
     self.x = x 
    def __repr__(self): 
     return repr(self.x) 

def strange_function(): 
    # You get a different result probably everytimes. 
    return list(set(A(i) for i in range(20))) 

>>> strange_function() 
[1, 18, 12, 5, 16, 15, 8, 2, 14, 0, 6, 19, 13, 11, 10, 9, 17, 3, 7, 4] 
>>> strange_function() 
[0, 9, 14, 3, 17, 5, 6, 11, 8, 1, 15, 7, 12, 13, 2, 10, 16, 4, 19, 18] 

...即使你刪除everythng取決於時間,隨機數生成器,基於哈希函數的順序等,也很容易編寫一個有時超出可用內存或超時限制的函數,並且有時會給出結果。


編輯:
羅馬,你最近寫了,你就一定可以相信用戶。那麼現實的解決方案存在。它是由它記錄到文件中並verifing它在虛擬機上通過網絡運行的遠程IPython notebook(好的簡短的教程視頻,遠程計算的支持開箱即,後端服務的重新啓動,以驗證輸入和輸出功能文檔菜單從一秒鐘,而不會丟失筆記本(html文檔)中的數據(輸入/輸出),因爲它是通過觸發調用遠程後端的JavaScript的動作一步一步動態創建的)。

您不需要對內部調用感興趣,只需要全局輸入和輸出,直到找到差異。虛擬機應該能夠獨立驗證結果並且可重複。配置機器接受來自您的連接的防火牆,但不能啓動傳出連接。配置當前用戶不能保存數據的文件系統,因此它們不存在,除了軟件組件。禁用數據庫服務。以隨機順序驗證結果輸入/輸出或在不同端口上啓動兩個IPython筆記本服務,併爲筆記本上的每個命令行選擇一個隨機後端,或在重要之前經常重新啓動後端進程。如果您發現不同,請調試您的代碼並修復它。

在不需要交互性後,您可以在沒有「筆記本」的情況下最終在IPython遠程計算中自動執行該操作。

+1

應該指出的是,你也可以從一個對象的隨機存儲器地址獲得隨機數。 'A類:通過; str(A)' – PythonNut 2014-10-14 03:51:31

相關問題