2012-02-06 65 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")