在Python中迭代对变量的引用

在Python中迭代对变量的引用,python,regex,foreach,Python,Regex,Foreach,我有一个名为Song的对象,它被定义为: class Song(object): def __init__(self): self.title = None self.songauthor = None self.textauthor = None self.categories = None 在这个类中,我有一个方法来解析该对象的运行时属性“metadata”,它基本上只是一个文本文件,其中包含一些我用正则表达式解析的格

我有一个名为Song的对象,它被定义为:

class Song(object):
    def __init__(self):
        self.title = None
        self.songauthor = None
        self.textauthor = None
        self.categories = None
在这个类中,我有一个方法来解析该对象的运行时属性“metadata”,它基本上只是一个文本文件,其中包含一些我用正则表达式解析的格式化文本。在这个过程中,我提出了以下代码,我非常确定这些代码可以简化为一个循环

re_title = re.compile("^title:(.*)$", re.MULTILINE)
re_textauthor = re.compile("^textauthor:(.*)$", re.MULTILINE)
re_songauthor = re.compile("^songauthor:(.*)$", re.MULTILINE)
re_categories = re.compile("^categories:(.*)$", re.MULTILINE)

#
# it must be possible to simplify the below code to a loop...
#
tmp = re_title.findall(self.metadata)
self.title = tmp[0] if len(tmp) > 0 else None

tmp = re_textauthor.findall(self.metadata)
self.textauthor = tmp[0] if len(tmp) > 0 else None

tmp = re_songauthor.findall(self.metadata)
self.songauthor = tmp[0] if len(tmp) > 0 else None

tmp = re_categories.findall(self.metadata)
self.categories = tmp[0] if len(tmp) > 0 else None
我猜这可以通过在数据类型(可能是元组)中封装对属性(例如self.title)和相应正则表达式(re_title)的引用,然后迭代这些数据类型的列表来实现

我曾经尝试过使用元组:

for x in ((self.title, re_title),
      (self.textauthor, re_textauthor),
      (self.songauthor, re_songauthor),
      (self.categories, re_categories)):
    data = x[1].findall(self.metadata)
    x[0] = data[0] if len(data) > 0 else None

这失败得可怕,因为我无法在运行时修改元组。有谁能给我一个建议,告诉我如何做到这一点吗?

一个例子是使用这样的词典:

things = {}

for x in ((self.title, re_title),
  (self.textauthor, re_textauthor),
  (self.songauthor, re_songauthor),
  (self.categories, re_categories)):
    if len(x[1].findall(self.metadata):
        things[x[0]] = x[1].findall(self.metadata)[1]
    else:
        things[x[0]] = None

这是一个可能的解决方案吗?

一个例子是使用这样的字典:

things = {}

for x in ((self.title, re_title),
  (self.textauthor, re_textauthor),
  (self.songauthor, re_songauthor),
  (self.categories, re_categories)):
    if len(x[1].findall(self.metadata):
        things[x[0]] = x[1].findall(self.metadata)[1]
    else:
        things[x[0]] = None

这可能是一种解决方案吗?

直接更新类成员,而不是分配给元组:

all_res = {'title':re_title,
           'textauthor': re_textauthor,
           'songauthor': re_song_author,
           'categories': re_categories}

for k, v in all_res.iteritems():
    tmp = v.findall(self.metadata)
    if tmp:
        setattr(self, k, tmp[0])
    else:
        setattr(self, k, None)

如果只关心第一个匹配,则不需要使用
findall

而不是分配给元组,直接更新类成员:

all_res = {'title':re_title,
           'textauthor': re_textauthor,
           'songauthor': re_song_author,
           'categories': re_categories}

for k, v in all_res.iteritems():
    tmp = v.findall(self.metadata)
    if tmp:
        setattr(self, k, tmp[0])
    else:
        setattr(self, k, None)

如果您只关心第一个匹配,则不需要使用
findall

您的代码有两个问题

重要的一点是,
x[0]
不是对
self.title
的引用,而是对
self.title
值的引用。换句话说,您只是将现有标题复制到一个元组中,然后将元组中的该标题替换为另一个,这对现有标题没有影响

较小的一个是不能替换元组中的元素。您可以通过使用列表而不是元组来解决这个问题,但是您仍然会遇到一个大问题

那么,如何在Python中创建对变量的引用呢?你不能。你需要想一个方法来重组事情。例如,您可以通过名称而不是引用来访问这些内容。在单个字典中存储包含四个变量的字典,而不是四个单独的变量:

res = {
    'title': re.compile("^title:(.*)$", re.MULTILINE),
    'textauthor': re.compile("^textauthor:(.*)$", re.MULTILINE)
    'songauthor': re.compile("^songauthor:(.*)$", re.MULTILINE)
    'categories': re.compile("^categories:(.*)$", re.MULTILINE)
}

class Song(object):
    def __init__(self):
        self.properties = {}

    def parsify(self, text):
        for thing in ('title', 'textauthor', 'songauthor', 'categories'):
            data = res[thing].findall(self.metadata)
            self.properties[thing] = data[0] if len(data) > 0 else None
您也可以在res:中使用
,因为这将迭代所有键(以任意顺序,但您可能不关心顺序)


如果你真的需要有
self.title
,你会遇到一个常见的问题。通常,应该由运行时字符串引用的数据和不应该由运行时字符串引用的属性之间有明确的区别。但有时,没有。所以你必须以某种方式在他们之间架起桥梁。您可以创建四个
@property
字段
返回self.properties['title']
,也可以使用
setattr(self,thing,…)
代替
self.properties[thing]
,或其他各种可能性。哪一个最好取决于它们是更像数据还是更像属性。

您的代码有两个问题

重要的一点是,
x[0]
不是对
self.title
的引用,而是对
self.title
值的引用。换句话说,您只是将现有标题复制到一个元组中,然后将元组中的该标题替换为另一个,这对现有标题没有影响

较小的一个是不能替换元组中的元素。您可以通过使用列表而不是元组来解决这个问题,但是您仍然会遇到一个大问题

那么,如何在Python中创建对变量的引用呢?你不能。你需要想一个方法来重组事情。例如,您可以通过名称而不是引用来访问这些内容。在单个字典中存储包含四个变量的字典,而不是四个单独的变量:

res = {
    'title': re.compile("^title:(.*)$", re.MULTILINE),
    'textauthor': re.compile("^textauthor:(.*)$", re.MULTILINE)
    'songauthor': re.compile("^songauthor:(.*)$", re.MULTILINE)
    'categories': re.compile("^categories:(.*)$", re.MULTILINE)
}

class Song(object):
    def __init__(self):
        self.properties = {}

    def parsify(self, text):
        for thing in ('title', 'textauthor', 'songauthor', 'categories'):
            data = res[thing].findall(self.metadata)
            self.properties[thing] = data[0] if len(data) > 0 else None
您也可以在res:
中使用
,因为这将迭代所有键(以任意顺序,但您可能不关心顺序)


如果你真的需要有
self.title
,你会遇到一个常见的问题。通常,应该由运行时字符串引用的数据和不应该由运行时字符串引用的属性之间有明确的区别。但有时,没有。所以你必须以某种方式在他们之间架起桥梁。您可以创建四个
@property
字段
返回self.properties['title']
,也可以使用
setattr(self,thing,…)
代替
self.properties[thing]
,或其他各种可能性。哪一个是最好的取决于它们是更像数据还是更像属性。

abarnert的回答很好地解释了代码的错误,但我想提供一个替代解决方案。与其使用循环来分配每个变量,不如尝试从已解析的文件中创建一个包含不同值的iterable,然后使用单个解包分配将它们放入各个变量中

下面是一个使用列表理解的双语句解决方案,由于需要在
if
/
else
表达式(因此是嵌套的生成器表达式)中引用
findall
的结果两次,这一点变得有点棘手:


在列表的第一部分,你可能会把事情简化一点。首先,您可以测试
if x
,而不是
if len(x)>0
。或者,如果您不太喜欢使用
findall
,您可以使用
search
,然后只使用
x和x.group(0)
而不是整个
if
/
else
位。如果没有找到匹配项,
search
方法将返回
None
,因此
操作符的短路行为将完全符合我们的要求。

abarnert的回答很好地解释了代码的错误,但我想提供一个替代解决方案。拉特