我有一个名为Song的对象,定义为:
class Song(object):
def __init__(self):
self.title = None
self.songauthor = None
self.textauthor = None
self.categories = None
在这个类中,我有一个解析该对象的运行时属性的方法,“元数据”,它基本上只是一个带有一些格式化文本的文本文件,我用正则表达式解析。在这个过程中,我提出了以下代码,我非常肯定可以将其简化为循环。
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
由于我无法在运行时修改元组,因此失败了。任何人都可以提出如何解决这个问题的建议吗?
答案 0 :(得分:3)
您的代码存在两个问题。
最重要的一点是,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
您也可以在那里使用for thing in res:
,因为这会迭代所有键(按任意顺序,但您可能不关心订单)。
如果您确实需要self.title
,那么您遇到了一个常见问题。通常,数据(应该由运行时字符串引用)和属性(不应该引用)之间有明确的区别。但有时候,没有。所以你必须以某种方式在它们之间架起桥梁。您可以创建@property
的四个return self.properties['title']
字段,也可以使用setattr(self, thing, …)
代替self.properties[thing]
,或其他各种可能性。哪一个最好归结为它们是更像数据还是更像属性。
答案 1 :(得分:2)
不是分配给元组,而是直接更新类成员:
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
。
答案 2 :(得分:1)
abarnert的回答很好地解释了您的代码出了什么问题,但我想提供一个替代解决方案。不要使用循环来分配每个变量,而是尝试从解析的文件中创建不同值的可迭代,然后使用单个解包赋值将它们放入各种变量中。
这是一个使用列表推导的双语句解决方案,由于您需要在findall
/ if
中两次引用else
的结果,这有点棘手表达式(因此嵌套的生成器表达式):
vals = [x[0] if len(x) > 0 else None for x in (regex.findall(self.metadata) for regex in
[re_title, re_textauthor,
re_songauthor, re_categories])]
self.title, self.textauthor, self.songauthor, self.categories = vals
你可以在列表理解的第一部分中简化一些事情。首先,您只需测试if x
而不是if len(x) > 0
。或者,如果您不太习惯使用findall
,则可以使用search
,然后使用x and x.group(0)
代替整个if
/ else
位。如果未找到匹配项,则search
方法会返回None
,因此and
运算符的短路行为将完全符合我们的要求。
答案 3 :(得分:0)
一个例子是使用这样的字典:
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
这可能是一个可能的解决方案吗?