2012-02-06 61 views
13

UPDATE:抑制治療作爲迭代

的想法,使內置的字符串非迭代是proposed on python.org in 2006。我的問題有所不同,因爲我試圖僅在一段時間內抑制這些功能;仍然這整個線程是非常相關的。

這裏是誰試行實施非迭代str關鍵comments by Guido

[...]我實現了這個(這是非常簡單的 做),但後來發現,我不得不修復噸的地方迭代 字符串。例如:

  • 對SRE解析器和編譯器使用的東西等集(「」),並且還遍歷輸入的regexp的字符來解析它。

  • difflib具有用於串中的任兩個列表中定義的API(典型的行由行DIFF的文件),或者兩個串(一個典型 幀內線差異),或任何事物甚至兩個列表(對於一般化的 序列差異)。

  • optparse.py,textwrap.py,string.py中的小改動。

而且我不是即使在的地步regrtest.py框架甚至 作品(由於difflib問題)。

我放棄了這個項目;該補丁是SF補丁1471291.我不是 更贊成這個想法;這是不實際的,前提 幾乎沒有很好的理由來重複一個字符串已被 駁斥了我在sre和difflib中找到的用例。

原題:

雖然這是一個字符串是可迭代的語言的實用的功能,當與鴨子類型相結合,可能會導致災難:

# record has to support [] operation to set/retrieve values 
# fields has to be an iterable that contains the fields to be set 
def set_fields(record, fields, value): 
    for f in fields: 
    record[f] = value 

set_fields(weapon1, ('Name', 'ShortName'), 'Dagger') 
set_fields(weapon2, ('Name',), 'Katana') 
set_fields(weapon3, 'Name', 'Wand') # I was tired and forgot to put parentheses 

沒有例外會發生,除了在無數地方測試isinstance(fields, str)之外,沒有簡單的方法來解決這個問題。在某些情況下,這個錯誤需要很長時間才能找到。

我想禁用字符串作爲完全在我的項目中被迭代處理。這是個好主意嗎?它可以輕鬆安全地完成嗎?

也許我可以繼承內置的str,這樣我需要明確地調用get_iter()如果我希望它的對象被當作一個迭代對待。然後,無論何時我需要一個字符串文字,我都會創建這個類的一個對象。

這裏有一些切向相關的問題:

How can I tell if a python variable is a string or a list?

how to tell a variable is iterable but not a string

+0

我想你基本上已經回答了你自己的問題。如果你必須這樣做,你的兩種方法是最好的方法,但最好的答案是確保它不會發生。 – 2012-02-06 23:35:11

+2

我只是堅持'isinstance(fields,str)'檢查 - 你不可能永遠需要能夠讓你自己的類型像一個字符串一樣嘎嘎。或者,將'fields'作爲最後的可變參數。 (儘管如果你感到疲憊,這樣做不會起作用,忘記你是否應該在其周圍放置圓括號。) – millimoose 2012-02-06 23:52:12

+0

任何將字符串定義爲字符通用列表的庫/語言都會遇到這個問題。它看起來不像Python的東西。 – Apalala 2012-02-12 21:49:32

回答

8

不幸的是,沒有任何方法可以自動執行此操作。你提出的解決方案(一個str子類是不可迭代的)遇到與isinstance()相同的問題...即,你必須記住在你使用字符串的任何地方使用它,因爲沒有辦法讓Python在適當的地方使用它的原生類。當然,你不能猴子修補內置的對象。

我可能會建議如果你發現自己編寫的函數需要一個可迭代的容器一個字符串,那麼你的設計可能有問題。雖然有時你不能避免它。

在我看來,最不干擾的事情是把檢查放入函數中,並在進入循環時調用它。這至少可以將行爲改變放在你最有可能看到的地方:在for聲明中,不會在課堂上某處被埋沒。

def iterate_no_strings(item): 
    if issubclass(item, str): # issubclass(item, basestring) for Py 2.x 
     return iter([item]) 
    else: 
     return iter(item) 

for thing in iterate_no_strings(things): 
    # do something... 
+0

+1。如果你有*做這個,這是一個很好的答案。不過,我仍然建議不要這樣做。 – 2012-02-06 23:41:00

+0

作爲一個例子,我所做的功能如何?你會說這是「錯誤的設計」還是「無法避免」? – max 2012-02-06 23:43:17

+0

我有點搖擺不定。有時候我想說「在你接受的事情上是自由的」,並且「如果可能的話,試着去做用戶明顯想要的東西。」然而,在你的特定情況下,可能首先將值和你想要設置的名稱設爲'* args'?然後你總會得到一個迭代器,調用者只需指定儘可能多的名稱。如果他們已經有一個元組,那麼他們在打電話給你時就解開它。 – kindall 2012-02-06 23:47:46

6

擴大,並回答了它:

不,你不應該這樣做。

  1. 它改變了人們對字符串期望的功能。
  2. 這意味着在整個程序中額外的開銷。
  3. 這在很大程度上是不必要的。
  4. 檢查類型非常和諧。

你可以做到這一點,並且已經給出的方法可能是最好的方式(備案,我覺得子類是更好的選擇如果你必須這樣做,看到@ kindall的方法)但它不值得這樣做,它不是pythonic。首先避免錯誤。在你的例子中,你可能想問問自己,這是否更加明確你的論點的問題,以及命名參數或splat可能是更好的解決方案。

例如:改變排序。

def set_fields(record, value, *fields): 
    for f in fields: 
    record[f] = value 

set_fields(weapon1, 'Dagger', *('Name', 'ShortName')) #If you had a tuple you wanted to use. 
set_fields(weapon2, 'Katana', 'Name') 
set_fields(weapon3, 'Wand', 'Name') 

例如:命名參數。

def set_fields(record, fields, value): 
    for f in fields: 
    record[f] = value 

set_fields(record=weapon1, fields=('Name', 'ShortName'), value='Dagger') 
set_fields(record=weapon2, fields=('Name'), value='Katana') 
set_fields(record=weapon3, fields='Name', value='Wand') #I find this easier to spot. 

如果你真的想要的順序相同,但不認爲命名參數的想法是再清楚不過,那怎麼樣使每一個記錄類似字典的項目,而不是一個字典的(如果不是話),並具有:

class Record: 
    ... 
    def set_fields(self, *fields, value): 
     for f in fileds: 
      self[f] = value 

weapon1.set_fields("Name", "ShortName", value="Dagger") 

這裏唯一的問題是引進類和值的參數必須與關鍵字做的事實,儘管它保持清楚。

另外,如果你正在使用Python 3,你總是有使用擴展的元組拆包的選項:

def set_fields(*args): 
     record, *fields, value = args 
     for f in fields: 
     record[f] = value 

set_fields(weapon1, 'Name', 'ShortName', 'Dagger') 
set_fields(weapon2, 'Name', 'Katana') 
set_fields(weapon3, 'Name', 'Wand') 

或者,我的最後一個例子:

class Record: 
    ... 
    def set_fields(self, *args): 
     *fields, value = args 
     for f in fileds: 
      self[f] = value 

weapon1.set_fields("Name", "ShortName", "Dagger") 

然而,這些並離開在閱讀函數調用時會出現一些奇怪現象,因爲人們通常認爲參數不會以這種方式處理。

+2

我知道這是不和諧的,這就是爲什麼我這樣做不好......但我怎樣才能避免這些錯誤?我們在談論字面上錯過了一對括號......幾乎不可能在一段時間內避免,不是嗎? – max 2012-02-06 23:41:00

+1

@max正如我所說,我認爲這是一個問題,你如何在你的方法中構造你的參數,而不是字符串迭代的問題。 – 2012-02-06 23:43:06

1

您對創建非可迭代字符串有何看法?

class non_iter_str(str): 
    def __iter__(self): 
     yield self 

>>> my_str = non_iter_str('stackoverflow') 
>>> my_str 
'stackoverflow' 
>>> my_str[5:] 
'overflow' 
>>> for s in my_str: 
... print s 
... 
stackoverflow 
+0

這就是我最初的想法;但@kindall提到了這個缺點,除其他外:「你必須記得在你使用字符串的任何地方使用它」,包括我的代碼的其他用戶。 – max 2012-02-07 02:09:33

0

,而不是試圖讓你的琴絃非迭代,切換你看問題的方式:你的一個參數,或者是一個迭代,或...

  • INT
  • 定製類

當你編寫你的函數時,你要做的第一件事就是驗證你的參數,對吧?

def set_fields(record, fields, value): 
    if isinstance(fields, str): 
     fields = (fields,) # tuple-ize it! 
    for f in fields: 
     record[f] = value 

爲你處理等功能和參數可以是單數,或使用複數這將滿足你的需要。

+0

這是非常和諧的。考慮你想使用一個列表,或者任何其他迭代器而不是元組? Python是一種鴨子式的語言,它不是一個好的主意,但它不符合語言的理想。 – 2012-02-07 04:28:39

+0

不要檢查它是一個元組。檢查它不是一個字符串或字節。 – 2012-02-07 12:27:48

+0

@LennartRegebro:謝謝 - 聽到它不同的方式讓我點擊。答案已更新。 – 2012-02-07 16:27:10

3

在這種情況下,類型檢查不是unpythonic或壞的。只是做一個:

if isinstance(var, (str, bytes)): 
    var = [var] 

在通話的開始。或者,如果你想教育來電者:

if isinstance(var, (str, bytes)): 
    raise TypeError("Var should be an iterable, not str or bytes")