这是我用来抽象unicode和str之间.translate
的差异的实现:
import types
from string import maketrans
def str_translate(txt, inchars, outchars, deletechars):
if inchars : transtab = maketrans(inchars, outchars)
else : transtab = None
return txt.translate(transtab, deletechars)
def maketrans_u(inchars, outchars, deletechars):
'''Create a translation table for unicode. We assume that we
want to map one inchar to one outchar (but the actual unicode.translate function
is more powerful: it can also map one inchar to a unicode string)
We assume deletechars and inchars do not overlap (no checking done!)'''
if inchars : transtab = dict((ord(inchar), ord(outchar)) for inchar, outchar in zip(inchars, outchars))
else : transtab = { }
# Now map the deletechars to None
for char in deletechars:
transtab[ord(char)] = None
return transtab
def unicode_translate(txt, inchars, outchars, deletechars):
transtab = maketrans_u(inchars, outchars, deletechars)
return txt.translate(transtab)
def translate(txt, inchars=None, outchars=None, deletechars=None):
t = type(txt)
if t == types.StringType : return str_translate(txt, inchars, outchars, deletechars)
elif t == types.UnicodeType : return unicode_translate(txt, inchars, outchars, deletechars)
else : raise Exception('Not supported type %s' % (t))
if __name__ == '__main__' :
a = 'abc%=def'
deletechars = '=%'
print translate(a, deletechars=deletechars)
这里我失去了unicode.translate
的一些功能(即将一个字符翻译成字符串),但至少我有一个统一的接口,我可以用它来翻译unicode和普通字符串,而不需要关心这种类型。
我不喜欢的是:
txt.translate(...)
(我必须做translate(txt, ...)
,这意味着我无法链接函数调用,如txt[:50].translate(...)
有没有更好的方法来实现透明.translate
?
答案 0 :(得分:1)
此实现依赖于检查字符串的类型以调用正确的函数
那么可以呢?你想为不同的类型做不同的事情,你不能用点式语法OO风格对它们进行monkeypatch这样的类型,那么如何自动调度类型呢?您正在寻找的是外部调度。 Python可以在3.4+中执行此操作(仅调度第一个参数,而不是所有参数,如CLOS或Dylan ......虽然PyPI上有多个调度库)singledispatch
,并且PyPI上有a backport回到2.6。所以,你可以这样做:
from singledispatch import singledispatch
@singledispatch
def translate(txt, inchars=None, outchars=None, deletechars=None):
raise Exception('Not supported type %s' % (t))
@translate.register(str)
def translate(txt, inchars=None, outchars=None, deletechars=None):
return str_translate(txt, inchars, outchars, deletechars)
@translate.register(unicode)
def translate(txt, inchars=None, outchars=None, deletechars=None):
return unicode_translate(txt, inchars, outchars, deletechars)
另请注意,我刚使用str
和unicode
代替types.StringType
和types.UnicodeType
。正如文档所说,这些类型只是别名,并不是真正必要的。他们所做的就是让你的代码不那么向后兼容。 (并且他们没有帮助向前兼容3.x; 3.0只删除了不必要的别名,而不是为StringType
添加UnicodeType
和str
两个别名并添加{{1} } ...)
如果你不想在PyPI上使用库或者自己实现相同的东西,而是想要手动切换类型,你可能需要BytesType
而不是isinstance
。
我不能做txt.translate(...)(我必须做翻译(txt,...)
这是真的;你不能monkeypatch type(x) ==
和str
。那么呢?
这意味着我无法链接函数调用,如txt [:50] .translate(...)
当然,但你可以链接像unicode
这样的函数调用。虽然在Java或Ruby这样的“一切都是方法”的语言中看起来可能是反惯用语,但它在Python中完全没问题。特别是因为无论如何,用Python连接超过2或3个调用是非常罕见的。毕竟,translate(txt[:50], …).rstrip().split(':')
之后的下一件事将是split
调用或理解,而这些不是由Python中的方法完成的。
这里我失去了
map
的一些力量(即将一个字符翻译成字符串)
是的,这在最低共同点设计中几乎是固有的。一些性能损失也是如此。 unicode.translate
和str.translate
并没有真正做同样的事情。前者是基于表格的翻译,因为当你只有256个可能的值时,这是一个很好的优化,但它确实意味着你放弃了一些灵活性和力量。后者是基于字典的翻译,因为表格对于110万个值来说是悲观的,但这意味着你获得了一些额外的灵活性和力量。
所以,在这里,你放弃了unicode.translate
的表现(特别是因为你必须为每次翻译动态构建str.translate
),以及transtab
的灵活性,以获得两个世界中最糟糕的。
如果您确实知道unicode.translate
字符串的编码(并且它们实际上确实代表了文本 - str
对二进制数据也很有用...),您可以通过以下方式编写此代码str.translate
。但是,如果您知道编码,那么您最初可能只需s.decode(encoding).translate(…).encode(encoding)
而不是unicode
。
但我认为更好的解决方案可能是以str
的方式返回一个由maketrans
组成的两个表的元组,以及str
的一个元组的元组。然后,您可以调用原生unicode
,而不是包裹s.translate(*transtab)
。
不幸的是,你不能使用translate
,因为任何参数都可能是singledispatch
,这意味着我们又回到显式类型切换。
None
现在你可以这样做:
def maketrans(inchars, outchars, deletechars):
if isinstance(inchars, str) or isinstance(deletechars, str):
return maketrans_s(inchars, outchars, deletechars)
elif isinstance(inchars, unicode) or isinstance(deletechars, unicode):
return maketrans_u(inchars, outchars, deletechars)
raise Exception('Not supported type %s' % (t))
def maketrans_s(inchars, outchars, deletechars):
if inchars: transtab = maketrans(inchars, outchars)
else: transtab = None
return transtab, deletechars
def maketrans_u(inchars, outchars, deletechars):
# The if was unnecessary here; if inchars is empty, the zip
# will be too, so you'll get {} as the result. Also notice
# no ord(outchar); this means you _can_ use Unicode strings
# when you know the string is Unicode.
transtab = dict((ord(inchar), outchar) for inchar, outchar in zip(inchars, outchars))
for char in deletechars:
transtab[ord(char)] = None
return transtab,
但实际上,我不确定这首先在哪里有用。如果不知道您的transtab = maketrans(inchars, outchars, deletechars)
return s.translate(*transtab).rstrip().split(':')
和maketrans
是translate
还是inchars
,您怎么能打电话给deletechars
或str
?