【问题标题】:Variable assignment from unknown length list in PythonPython中未知长度列表的变量赋值
【发布时间】:2015-11-14 01:51:13
【问题描述】:

我正在抓取一个网站,以便将数据存储在具有 3 列的数据库中。我正在抓取的网站部分看起来像下面三个示例之一

# Example 1:
<div>
<a href="sample1">text1</a>
</div>

# Example 2:
<div>
<a href="sample1">text1</a>
<a href="sample2">text2</a>
</div>

# Example 3:
<div>
<a href="sample1">text1</a>
<a href="sample2">text2</a>
<a href="sample3">text3</a>
</div>

我正在尝试分配

  • “text1”到 var1,
  • 空字符串或“text2”到 var2,
  • 空字符串或“text3”到 var3。

最好的方法是什么?

我尝试过的一些事情是

### FIRST ATTEMPT
var1, var2, var3 = '','',''
# could also do var1, var2, var3 = ('',)*3
all = soup.find_all('a')

var1 = all[0].text

try:
    var2 = all[1].text
except:
    pass

try:
    var3 = all[3].text
except:
    pass

#### SECOND ATTEMPT
all = [s.text for s in soup.find_all('a')]
# This is where i get stuck... This could return a list of length 1, 2, or 3, and I need the output to be a list of length 3 so i can use the following line to assign variables
var1, var2, var3 = all

#### THIRD ATTEMPT
all = [s.text for s in soup.find_all('a')]
var1, var2, var3 = '','',''
n = len(all)
var1 = all[0].text
if n = 2:
    var2 = all[1].text
else:
    var2 = all[1].text
    var3 = all[2].text

编辑: 我试图在我的数据库中包含三个字段的原因是因为我希望能够按这些不同的变量中的每一个进行过滤。 var1 是最准确的标签,var2 稍微准确一些,var3 准确度很高。把它想象成衣服...... var1 可能是灰色休闲裤,var2 可能是商务休闲裤,var3 可能是裤子。

【问题讨论】:

  • 我能问一下为什么你要这样做吗?相反,只需将find_all 的结果分配给一个列表,然后您就可以使用列表的对象。
  • 我正在尝试建立一个数据库。数据库最终将有三列,因为这部分代码最多可以有三个值,我想将它们全部捕获。在某些时候,我必须将该字段设置为空白,所以我试图找出一种巧妙的方法来做到这一点,而不是蛮力 if...elif... 方法。有时我可能会遇到 > 3 个我想捕获的字段,并且在这种情况下我也希望有一种更顺畅的方式来完成。
  • 好的,您可以全部捕获它们,并且仍然使用其他逻辑来控制它们如何/何时写入您的数据库。如果可以的话,我会在下面写一些答案以提供帮助。

标签: python list variable-assignment


【解决方案1】:

您的第二次尝试可能更符合 Python 风格。当然,你事先并不知道.find_all 的结果是否会是一个长度==3(或更多,或更少)的列表。因此,您应该使用 try/except 或其他逻辑来控制将结果写入数据库的方式/时间。

# create a dictionary of your database column names:
dbColumns = {0:'column1', 1:'column2', 2:'column3'}

# get all the results; there might be 0 or 3 or any number really, 
#     we'll deal with that later
results = [s.text if s.text else "" for s in soup.find_all('a')]

# iterate the items in the list, and put in corresponding DB
for col in range(len(results)):
    # use the dbColumns dict to insert to the desired column

    query = "Insert INTO [db_name].[" + dbColumns[col] + "]"
    query += "VALUES '" + results[i] + '"

    """
    db.insert(query)  # assumes a db object that has an "insert" function; modify as needed
    """

这种方法的要点是,从技术上讲,似乎不需要硬编码三个对象(var1、var2、var3)并尝试分配给这些对象。相反,只需返回 find_all 的结果并通过它们在结果列表中的索引来处理它们。

【讨论】:

    【解决方案2】:

    你可以使用一些简单的列表乘法:

    # use a constant at the top of your script in case the number of columns
    # change in the future
    COLUMNS = 3
    
    # ... other code ...
    
    all = [s.text for s in soup.find_all('a')]
    all.extend(['']*(COLUMNS-len(all))) # append 1 empty string for each missing text field
    var1, var2, var3 = all 
    

    但正如 David Zemens 在 cmets 中提到的那样,必须有更好的方法来做到这一点。如果没有看到使用您的文本变量的代码,我无法提出任何具体建议,但您应该认真重新考虑您的设计。即使您像我建议的那样使用常量,拥有var1, var2, var3 = all 仍然会使以后维护和修改此脚本变得困难。


    根据您的编辑,我建议您改用字典。这将允许您按名称引用特定数据,就像引用变量一样,但保留列表的灵活性,而不是限制您硬编码的变量数量。

    例如:

    all = [s.text for s in soup.find_all('a')]
    
    d = {}
    for i, field in enumerate(all): 
        d['var{}'.format(i)] = field
    
    # later in your code that consumes this dictionary...
    
    try:
        foo(d['var1']) # function to do something with the scraped string corresponding
                       # to var1
    except KeyError:
        # do something else or pass when the expected data doesn't exist
    

    如果all['a', 'b'],那么这段代码会产生这样的结果:

    {'var1': 'b', 'var0': 'a'}

    变量赋值实际上只不过是一个映射——你的代码知道变量名,它可以查找相应的值。字典可以让您的代码动态构建映射,而不必硬编码。现在我们已经构建了一个字典,其中varX 变量是动态构造的。如果您决定添加另一列,您根本不必更改此代码。您只需添加将使用var4 的代码,并准备好在字典中不存在var4 时捕获异常。不再添加空字符串 - 您的代码已准备好处理它正在寻找的数据不存在的情况。

    注意事项:

    1. enumerate() 函数迭代一个可迭代对象并为您增加一个计数器。在我的代码中,i 是计数器(因此我们可以构造 'var1'、'var2'... 字符串),field 是列表中的每一项。

    【讨论】:

    • 这看起来不错,解决了眼前的问题,当然为了可维护性,硬编码元素的数量可能并不理想;列数(因此“变量”的数量将来可能需要更改,等等)
    • @DavidZemens 我非常同意。我也同意你之前的 cmets 必须有更好的方法来做到这一点。我已经调整了我的答案,建议至少使用一个常量,如果 OP 决定坚持使用这种方法,该常量将更容易更改。
    • extend 在这种情况下是完美的。在考虑数据库功能时,为了快速过滤数据库,我可能会创建 4 列.... var_all、var1、var2 和 var3。由于 var1 是最合适的标签,而其他标签是次要标签(想想衣服……var1 可能是灰色休闲裤,var2 可能是商务休闲裤,var3 可能是裤子),因此能够快速过滤所有标签可能是也很有用。
    • @exhoosier10 大卫和我的意思是,可能有比将列表拆分为显式变量更好的方法。这些变量正在某处被其他代码消耗,对吧?如果您可以使该代码在列表而不是单个变量上运行,那么您的脚本将以后更容易修改。您故意从列表旨在提供的灵活性中减去。
    • @skrrgwasme 我明白你在说什么。假设“灰色休闲裤,商务休闲裤,裤子”的字段,我应该能够通过任何这些值或类似 where var_all = "grey-slacks%" 的东西过滤数据库......所以我开始认为拆分成变量可能会浪费资源
    【解决方案3】:

    怎么样:

    all = soup.find_all('a')
    var1 = all[0].text if len(all) > 0 else ""
    var2 = all[1].text if len(all) > 1 else ""
    var3 = all[2].text if len(all) > 2 else ""
    

    条件表达式x if y else z(通常称为三元运算符)使代码简单易读。不过它不会赢得任何设计奖项。

    【讨论】:

      【解决方案4】:

      你可以试试这个

      #if list1 has uncertain number of values and you want to give them each variable 
      
      #create random list2 with max number of possible veriables 
      list2 = ['var1', 'var2', 'var3', 'var4' , . . . ]
      
      for li1, li2 in zip(list1, list2):
          globals()[li2] = li1
          print(li2)
      

      我不是python专业人士,我只是自己想出来的,它可能不是很pythonic,但它解决了问题

      【讨论】:

        猜你喜欢
        • 2015-07-22
        • 1970-01-01
        • 1970-01-01
        • 2022-11-28
        • 2012-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-08
        相关资源
        最近更新 更多