2014-11-21 130 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