簡體   English   中英

使用內置函數跨模塊存儲全局變量

[英]Using builtins to store global variables across modules

我有一個Jupyter筆記本,我想簡單地使用它運行一堆在python'helper'文件中定義的函數。 但是筆記本確實有一些用戶可以更改的變量(所以它們就像我想的常量一樣)。 我也希望這些變量也可以從幫助文件中訪問。 我希望不必將這些變量傳遞給筆記本中​​的每個函數調用。

我發現在筆記本中定義這些變量時,以下工作有效:

import builtins

builtins.my_variable = my_value

變量“ my_variable”現在可以在Jupyter筆記本和幫助文件中使用。

注意:以這種方式定義變量后,如果我在筆記本中鍵入help(builtins)並一直滾動到底部,則在“數據”部分下找到列出的變量。

另一件事有效:

import helper
helper.my_variable = my_value

有人可以解釋為什么/這些東西如何工作,如果使用它們有任何問題,如果可以的話,這可能是一種更好的方法嗎?

首先,讓我說,我試圖避免出現全球狀態,因為有時候人們說這是不可避免的。 在Python中,創建全局狀態的最簡單方法是只包含一個帶有變量的模塊。 例如,

# constants.py
constant1 = 'A Constant'
constant2 = 'Another Constant'
constant3 = 'Absolutely last constant'

在Jupyter筆記本中,您可以執行以下操作:

import constants

在具有功能的模塊中,您可以執行相同的操作。

基本上,第二種方法是要走的路。

CAVEAT Python並沒有真正的常量概念,因為您可以執行諸如constants.constant1 = 'Some new value' ,這將具有更改為constants.constant1新分配的值的效果。

首先:我建議將變量傳遞給helper模塊函數,而不要依賴於全局狀態。 如果您需要某種全局狀態並且不想一遍又一遍地傳遞它,請考慮將一些函數分組到類中,然后將狀態傳遞給類的初始值設定項並存儲在實例中。 這樣,調用實例的方法隱式傳遞實例,從而以最小的重復傳遞所需的狀態。 我將在此答案的底部提供一個簡單的示例。

修改builtins的內容將意味着用您的價值膨脹對最后一個位置的查找。 可以想象,這會減慢所有地方的所有代碼的速度(尤其是如果這意味着調整builtins模塊的底層dict大小,可能使其不再適合緩存)。

從面向未來的角度來看, 偶爾一些建議基於其假定的靜態內容來優化builtins的查找 盡管大多數建議都處理修改builtins的情況,但是這些優化的效果可能會丟失(恢復為僅按需執行查找)。 這也有先例。 在CPython 3.3之前,建議在__init__完成之前創建實例的所有屬性,並且之后不應該刪除或將任何屬性添加到實例(給定屬性的值仍可以更改)。 但是在3.2和更早的版本中,忽略此建議並沒有真正的懲罰。 從3.3開始,遵循該建議的類在每個實例上的內存開銷大大減少了 沒有遵循建議的課程沒有任何收獲。

修改builtins還有其他問題,例如:

  1. 可能導致潛在的dictbuiltins擴大規模,降低內存訪問局部性
  2. 可能會創建其他沖突,以查找特定的內置函數(僅由於您的新屬性存在而使對有用的內置函數的訪問變慢)
  3. 可能會在其他模塊中隱藏錯誤,例如,一個模塊應該創建一個變量,其名稱與您在本地builtins模塊中推入的名稱相同,但這樣做要么失敗,並且靜默地使用您的定義,要么更糟,故意依賴不存在的名稱初始化自己的屬性,現在不再使用定義來初始化它
  4. 使您的代碼難以維護; 如果我在模塊中看到對名為foo的變量的引用,則希望能夠在模塊中找到定義,或通過查看from x import *來找到定義的源( from x import *語法可以看出,這是為什么靜態代碼檢查器經常from x import *報告錯誤)。 如果它是在其他不相關的模塊中秘密創建的,並且被builtins (或者更糟的是,從許多不相關的模塊中進行了變異)推到了我的builtins ,那么我將對犯下這種殘酷行為的人感到憤怒。

重點是,修改builtins函數不是一個好主意。 它可能會起作用,但不要這樣做。

您的helpers模塊方法並不完全糟糕,盡管在實踐中,我建議直接在aers.abdullah的建議下helpers.py的共享值定義為aqual.abdullah並將其視為常量,而不是在此處創建其他模塊(這會導致很多與修改builtins相同的問題,只是問題的范圍更有限)。

這些方法行之有效的原因在於,模塊大多只是圍繞字符串鍵Python dict的語法糖。 您可以在Python中的大多數(盡管不是全部)對象中添加新屬性,而模塊本身就是對象(而不是該通用規則的例外之一)。

helper.my_variable = my_value

真正歸結為:

helper.__dict__['my_variable'] = my_value

並且由於其所有導入程序都可以看到相同的helper程序(在第一次import緩存了一個模塊,而所有后續import都返回了對同一緩存副本的引用),因此他們所有人都看到了修改。


我在頂部提到的更好的解決方案是更改:

# helpers.py
a = 1
b = 2

def func1():
    return a + b

def func2():
    return a * b

def func3():
    return a / b

呼叫者在執行以下操作:

>>> import helper
>>> helper.a = 5
>>> helper.func1()

到基於類的設計:

# helpers.py
class Helper:
    def __init__(self, a=1, b=2):
        self.a = 1
        self.b = 2

    def func1(self):
        return self.a + self.b

    def func2(self):
        return self.a * self.b

    def func3(self):
        return self.a / self.b

用法是:

>>> import helpers
>>> h = helpers.Helper(a=5)
>>> h.func1()

或一次性使用一組給定的值:

>>> helpers.Helper(a=5).func1()

使用默認值將是:

>>> helpers.Helper().func1()

這避免了多個線程(或可重入代碼)對helpers的全局狀態進行相互不兼容更改的問題(因為該狀態存儲在實例中,這些實例是獨立擁有和管理的)。 使用帶有默認參數的初始化程序意味着您永遠不會丟失原始默認值。 您可以隨時制作一份新的副本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM