2014-11-21 149 views
0

我有称为宋一个对象,作为被定义为变量的引用:遍历在Python

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 

这可怕的失败,因为我不能修改在运行时的元组。任何人都可以提供一个关于我如何解决这个问题的建议吗?

+1

如果你只对第一场比赛感兴趣,你为什么要用'findall'? – 2014-11-21 22:21:58

+0

@MarkRansom懒惰。 – v3gard 2014-11-21 22:50:46

回答

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 

你也可以使用​​那里,因为那会遍历所有的按键(以任意顺序,但你可能不关心订单)。

如果您确实需要self.title,那么您遇到了一个常见问题。通常,数据(应由运行时字符串引用)和属性(应该不引用)之间存在明显区别。但有时候,没有。所以你必须以某种方式在他们之间搭桥。您可以创建四个@property字段return self.properties['title'],或者您可以使用setattr(self, thing, …)而不是self.properties[thing]或其他各种可能性。哪一个最好归结为它们是更像数据还是更像属性。

+0

哇。感谢他们提出了一个解决方案,同时也指出了一些我怀疑但未完全意识到的Python(为变量创建引用)。 – v3gard 2014-11-21 22:46:56

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 

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

+1

这不起作用。你正在制作一个字典,将之前的'self.title'值映射到找到的标题等等。由于所有这些属性的初始值都是'None',所以最终只有一个映射到None的字典映射到None类别。 – abarnert 2014-11-21 22:28:43

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

1

abarnert的回答很好地解释了你的代码出了什么问题,但我想提供一个替代解决方案。不要使用循环来分配每个变量,而是尝试从解析的文件中创建不同值的迭代,然后使用单个拆箱分配来将它们放入各种变量中。

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

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运算符的短路行为将完全按照我们的要求进行。

+0

我认为将第一行变成两行更具可读性:将生成器存储在'finds =(regex.findall ...)'中,然后'vals = [x [0] if x else None for x in found] '。 (它使得代码缩短了一行,同时仍然保持在80列以内,所以没有真正的成本。)但是,无论如何+1;这里没有太多的重复,这很清楚。 – abarnert 2014-11-22 00:08:49