2007-06-29

 

Python Cookbook 1.24 让字符串大小写不敏感

需求:

要处理一个字符串,需要让它对大小写不敏感,如字符串查找和比较等.而其它的字符串依然保持原来的特性.

讨论:

最好的办法就是从str继承一个子类来处理.

class iStr(str):
    """
    Case insensitive string class.
    Behaves just like str, except that all comparisons and lookups
    are case insensitive.
    """
    def _ _init_ _(self, *args):
        self._lowered = str.lower(self)
    def _ _repr_ _(self):
        return '%s(%s)' % (type(self)._ _name_ _, str._ _repr_ _(self))
    def _ _hash_ _(self):
        return hash(self._lowered)
    def lower(self):
        return self._lowered
def _make_case_insensitive(name):
    ''' wrap one method of str into an iStr one, case-insensitive '''
    str_meth = getattr(str, name)
    def x(self, other, *args):
        ''' try lowercasing 'other', which is typically a string, but
            be prepared to use it as-is if lowering gives problems,
            since strings CAN be correctly compared with non-strings.
        '''
        try: other = other.lower( )
        except (TypeError, AttributeError, ValueError): pass
        return str_meth(self._lowered, other, *args)
    # in Python 2.4, only, add the statement: x.func_name = name
    setattr(iStr, name, x)
# apply the _make_case_insensitive function to specified methods
for name in 'eq lt le gt gt ne cmp contains'.split( ):
    _make_case_insensitive('_ _%s_ _' % name)
for name in 'count endswith find index rfind rindex startswith'.split( ):
    _make_case_insensitive(name)
# note that we don't modify methods 'replace', 'split', 'strip', ...
# of course, you can add modifications to them, too, if you prefer that.
del _make_case_insensitive    # remove helper function, not needed any more

在类iStr中有一些要点需要注意.首先,我们在初始化的时候生成了需要处理字符串的小写版本,在__init__方法中这样做,是因为iStr对象要重复使用, 这样处理可以提高效率.我们使用一个下划线来标识这个变量,而不是两个,让它对子类可见,对外部不可见.
在这里并没有提供有些方法的大小写不敏感版本,因为很难在一般情况下确定具体的需求.在具体的应用中,子类可以根据自己的需求做特定的处理.举例说明,如果子类写了一个针对iStr的replace方法,而它的返回值是str类型,如果想要让所有的返回值也是iStr类型,需要写一个助手函数来处理:

def _make_return_iStr(name):
    str_meth = getattr(str, name)
    def x(*args):
        return iStr(str_meth(*args))
    setattr(iStr, name, x)

然后你需要针对特定的字符串方法调用这个函数:

for name in 'center ljust rjust strip lstrip rstrip'.split( ):
    _make_return_iStr(name)

String大约有20个相关方法(包括__add__和__mul__),有些是需要你封装的.如果你想封装更多的方法,要进行更多的处理,比如split和join,或者encode和decode,你需要从unicode继承子类,而不是str.
在上例中,我们尽量避免使用模板代码.只需要在特定应用的时候重写iStr的方法就可以了.自己重写一个类,也不会带来更多的好处.
在Python2.4以及以后的版本,你可以将一个方法赋值给一个函数对象.这样,就可以获得更好的可读性较高的代码,尤其是在函数叠加使用的时候.在Python2.3中,函数对象是只读的,在这里我们避免改变函数对象的值,已防止兼容性的问题.
大小写无关(并不改变大小写)有很多用处,比如在检查用户输入,或者有些文件系统上(windows和Macintosh), 文件名都是不区分大小写的.你可以很容易的为自己创建大小写无关的列表,字典,集合等.
举例说明,一个列表里面保存了一些字符串,而需要用一些大小写无关的方法来处理他们,如排序,索引等.可以构造iStr来处理它们:

class iList(list):
    def _ _init_ _(self, *args):
        list._ _init_ _(self, *args)
        # rely on _ _setitem_ _ to wrap each item into iStr...
        self[:] = self
    wrap_each_item = iStr
    def _ _setitem_ _(self, i, v):
        if isinstance(i, slice): v = map( self.wrap_each_item, v)
        else: v = self.wrap_each_item(v)
        list._ _setitem_ _(self, i, v)
    def append(self, item):
        list.append(self, self.wrap_each_item(item))
    def extend(self, seq):
        list.extend(self, map(self.wrap_each_item, seq))

基本上,我们确保了iList中的每一个对象都被iStr封装了.

标签:


2007-06-28

 

Pidgin又不能上qq了

今天早上登录qq,发现又提示密码错误.
打开TM,可以登录.
无语了,腾讯真TMD混蛋!!

 

Python Cookbook 1.23 为XML和HTML编码Unicode字符

需求:

需要为XML和HTML编码Unicode字符,比如UTF-8等.

讨论:

Python提供了一个异常处理器xmlcharrefreplace来处理编码异常,当遇到非指定编码的字符时,它使用xml的数字字符参考(XML numeric character references)来替换该字符.

def encode_for_xml(unicode_data, encoding='ascii'):
    return unicode_data.encode(encoding, 'xmlcharrefreplace')

你可以使用这种方法来处理html,但也许你更想使用HTML的符号实体参考(HTML's symbolic entity reference) .要达到这样的目的,你需要自己注册一个编码异常处理器,实现相应的接口,我们可以使用一个Python标准库提供的htmlentitydefs,它里面包含了HTML实体定义:

import codecs
from htmlentitydefs import codepoint2name
def html_replace(exc):
    if isinstance(exc, (UnicodeEncodeError, UnicodeTranslateError)):
        s = [ u'&%s;' % codepoint2name[ord(c)]
              for c in exc.object[exc.start:exc.end] ]
        return ''.join(s), exc.end
    else:
        raise TypeError("can't handle %s" % exc._ _name_ _)
codecs.register_error('html_replace', html_replace)


当注册完了异常处理器,可以用一个方法把它简单封装一下:

def encode_for_html(unicode_data, encoding='ascii'):
    return unicode_data.encode(encoding, 'html_replace')

一个好的Python模块,都包含一些使用样例,比如:if _ _name_ _ == '_ _main_ _'代码块:

if _ _name_ _ == '_ _main_ _':
    # demo
    data = u'''\
<html>
<head>
<title>Encoding Test</title>
</head>
<body>
<p>accented characters:
<ul>
<li>\xe0 (a + grave)
<li>\xe7 (c + cedilla)
<li>\xe9 (e + acute)
</ul>
<p>symbols:
<ul>
<li>\xa3 (British pound)
<li>\u20ac (Euro)
<li>\u221e (infinity)
</ul>
</body></html>
'''
    print encode_for_xml(data)
    print encode_for_html(data)


如果单独使用该模块,可以获得这样的输出(encode_for_xml):

<li>&#224; (a + grave)
<li>&#231; (c + cedilla)
<li>&#233; (e + acute)
   ...
<li>&#163; (British pound)
<li>&#8364; (Euro)
<li>&#8734; (infinity)


还有(encode_for_html):

<li>&agrave; (a + grave)
<li>&ccedil; (c + cedilla)
<li>&eacute; (e + acute)
   ...
<li>&pound; (British pound)
<li>&euro; (Euro)
<li>&infin; (infinity)

encode_for_xml方法是比较通用的,因为它适用于任何xml应用cheng程序,不仅仅是html.而encode_for_html 方法具有更好的可读性,如果你使用浏览器来阅读上面的输出,会发现结果是一样的.
请记住,unicode在输出到文件前,必须被编码,UTF-8是一个理想选择,因为它可以表示任何unicode字符.但对于一些欧美用户来说,更倾向于使用ASCII和Latin-1.然而,当遇到超出编码范围的字符时,这些编码不能处理它们.Python提供了一个内建的编码异常处理器xmlcharrefreplace,它将不可编码的字符替换为XML数字字符,如&#8734, 表示无穷大符号.本节也演示了如何自定义一个编码异常处理器,html_replace,用于处理编码范围外的字符.它使用了可读性较好的HTML符号实体来表示不可编码字符,比如&infin也表示无穷大符号.html_replace的通用性不如xmlcharrefreplace,因为它不能处理非html程序中的不可编码字符.

遇到非以上两种格式输出的文件,上面的方法都失效了.比如,TeX和其它标记语言都不能识别XML数字字符参考.当然 ,如果你能建立一种其它语言识别的字符参考,你可以使用本节提供的方法来注册编码异常处理器.
另一种方法(也是很高效的方法),使用Python标准库中的codecs模块:

outfile = codecs.open('out.html', mode='w', encoding='ascii',
                       errors='html_replace')

现在你可以使用outfile.write(unicode_data),所有指定编码外的unicode字符,都会被异常处理器处理.当然,处理完毕了不要忘记使用outfile.close ()方法.

相关说明;

codecs.register_error(...)
    register_error(errors, handler)
   
    Register the specified error handler under the name
    errors. handler must be a callable object, that
    will be called with an exception instance containing
    information about the location of the encoding/decoding
    error and must return a (replacement, new position) tuple.

codecs.open(filename, mode='rb', encoding=None, errors='strict', buffering=1)
    Open an encoded file using the given mode and return
    a wrapped version providing transparent encoding/decoding.

unicode.encode(...)
    S.encode([encoding[,errors]]) -> string or unicode
   
    Encodes S using the codec registered for encoding. encoding defaults
    to the default encoding. errors may be given to set a different error
    handling scheme. Default is 'strict' meaning that encoding errors raise
    a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and
    'xmlcharrefreplace' as well as any other name registered with
    codecs.register_error that can handle UnicodeEncodeErrors.
   
    Note: The wrapped version will only accept the object format
    defined by the codecs, i.e. Unicode objects for most builtin
    codecs. Output is also codec dependent and will usually be
    Unicode as well.
   
    Files are always opened in binary mode, even if no binary mode
    was specified. This is done to avoid data loss due to encodings
    using 8-bit values. The default file mode is 'rb' meaning to
    open the file in binary read mode.
   
    encoding specifies the encoding which is to be used for the
    file.
   
    errors may be given to define the error handling. It defaults
    to 'strict' which causes ValueErrors to be raised in case an
    encoding error occurs.
   
    buffering has the same meaning as for the builtin open() API.
    It defaults to line buffered.
   
    The returned wrapped file object provides an extra attribute
    .encoding which allows querying the used encoding. This
    attribute is only available if an encoding was specified as
    parameter.

标签:


2007-06-27

 

Python Cookbook 1.22 输出Unicode字符到标准输出

需求:

需要输出unicode字符串到标准输入(如:用于调试),可是默认的编码不能满足需求.

讨论:

用一个转换器来封装sys.stdout流,我们可以使用Python的codec类库.举例说明,我们希望输出编码是ISO-8859-1的unicode字符,可以这样写:

import codecs, sys
sys.stdout = codecs.lookup('iso8859-1')[-1]( sys.stdout)

unicode存在于一个很大的空间中,让它足以容纳世界上任何字符,由于unicode设计分离了内部表示和用户使用方式,所以我们可以很方便的使用它.然而,对于文件流来说,比如sys.stdout,需要考虑编码的每一个字节.你可以改变它默认的编码方式,通过修改模块site.可以这样的话,你就修改了整个Python的安装,会影响到别的Python程序,也许它们需要使用Python的默认编码格式(ASCII),因此,这种方式的修改是不可取的.
本节给出了一个方法:重新绑定sys.stdout流,让它接收Unicode,而提供ISO-8859-1输出.这里,我们首先保存原来的sys.stdout引用:

>>> old = sys.stdout

接下来,我们创建一个Unicode字符串,在正常情况下,它是不能被正确输出的:

>>> char = u"\N{LATIN SMALL LETTER A WITH DIAERESIS}"
>>> print char
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeError: ASCII encoding error: ordinal not in range(128)

如果在这一步你没有获得这样的错误,那是因为Python认为它自己知道你使用的终端的编码类型.可是如果你得到了这样的错误,或者得到的结果不是你期望的,比如你的终端使用UTF-8而Python并不知道这一点.遇到这种情况,我们用codec里面的UTF-8来封装sys.stdout:

>>> sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)
>>> print char
ä

当然,还需要你使用的终端支持utf-8编码,如果没有的话,可以从网上免费下载一个.
Python使用sys.stdout.encoding来判断终端的编码,因为IDLE和交互式Python界面,它们总是封装sys.stdout的,所以你可以输出unicode字符.

相关说明:

codecs.lookup(...)
    lookup(encoding) -> (encoder, decoder, stream_reader, stream_writer)
   
    Looks up a codec tuple in the Python codec registry and returns
    a tuple of functions.

标签:


2007-06-26

 

Python Cookbook 1.21 在Unicode和纯文本间转换

需求:

需要处理文本,里面包含非Ascii字符集的字符.

讨论:

unicode字符可以以多种形式编码,你可以选则自己需要的方式:

unicodestring = u"Hello world"
# Convert Unicode to plain Python string: "encode"
utf8string = unicodestring.encode("utf-8")
asciistring = unicodestring.encode("ascii")
isostring = unicodestring.encode("ISO-8859-1")
utf16string = unicodestring.encode("utf-16")
# Convert plain Python string to Unicode: "decode"
plainstring1 = unicode(utf8string, "utf-8")
plainstring2 = unicode(asciistring, "ascii")
plainstring3 = unicode(isostring, "ISO-8859-1")
plainstring4 = unicode(utf16string, "utf-16")
assert plainstring1 == plainstring2 == plainstring3 == plainstring4

如果你在自己要处理的文本中发现了非Ascii字符,那你需要了解什么是unicode,它如何工作,以及在Python中是如何处理它们的.可以参考上一节的内容.这一节会更多的讨论这个问题.
在处理真实世界的问题的时候,你并不需要知道关于unicode的所有知识,可是理解一些概念还是没有坏处的.首先需要了解的是,字节和字符是不同的.在过去的ascii时代,字节和字符是按同样的方式对待的.一个字节可以表示256个不同的数字 ,也就是说它能表示256个不同的字符.而unicode能表示成千上万的字符,所以一个字符不能等同于一个字节了.
标准的Python字符串是字节流,而Python字符是长度为1的字符串,也就是一个字节.标准Python就是处理简单字符串和字符的.
一个Python的unicode对象能表示任意大的字符,我们可以不考虑它的内部实现.只有当涉及的方法(如文件操作的write方法,网络操作的send方法等)需要处理unicode的字节顺序时, 我们才考虑具体如何表示unicode.将unicode转换为字节流称为编码(encoding),同样的,当你从文件,网络或者面向字节的对象时,需要解码(decode)来获得字符.
将unicode对象转换为字节流有很多方法,每种方法称为一种编码.由于历史原因,不仅仅存在一种编码,而是很多种.下面是一些常见的编码:

UTF-8编码可以处理任意unicode字符,并且向下和ascii兼容,所以一个纯ascii文本文件也可以看成一个utf-8文件,因为这种良好的向下兼容性,使得utf-8成为unix平台上的主要编码格式, 而且也是xml文件的默认编码格式.utf-8的缺点就是在处理东亚字符时效率不高.

UTF-16在MS平台和Java环境下经常使用,它虽然在表示西方字符时缺乏效率,然而表示东亚字符时更有效率.有时UTF-16也被成为USC-2

ISO-8859是ascii的超集,可以处理256个不同的字符.这些字符不能处理所有的unicode字符,只能处理一些流行的字符集.如:ISO-8859-1,也被称为'Latin-1',覆盖了大部分西欧和非洲字符,除了阿拉伯(Arabic)文.ISO-8859-2,也被称为'Latin-2',包含了很多东欧字符,如匈牙利(Hungarian)文和波兰(Polish)文.ISO-8859-15,目前在欧洲很流行,和ISO-8859-1 类似,只是包含了一些欧洲货币符号.

如果你希望能编码所有的字符,那么就使用utf-8吧,在处理别的编码的时候,只需要在输入时做转换就可以了.在下一节,我们会讨论输出时应该怎样处理.

相关说明:

参考上一节和下一节.


标签:


 

Python Cookbook 1.20 处理包含Unicode字符的文本

需求:

需要处理文本,而且里面包含非Ascii字符.

讨论:

Python提供了unicode类来替代使用str类,可以使用它来进行字符格式的转换:

>>> german_ae = unicode('\xc3\xa4', 'utf8')

在这里,german_ae表示了一个德语特殊字符,'\xc3\xa4'是它的utf-8编码.虽然有很多中编码格式,但utf-8是国际上通用的格式,而且可以很好的处理unicode字符和ascii字符.
在Python中,unicode和str也具有很好的兼容性.

>>> sentence = "This is a " + german_ae
>>> sentence2 = "Easy!"
>>> para = ". ".join([sentence, sentence2])

因为如果表达式中存在unicode对象的话,返回值总是unicode对象,所以上面的表达式尽管和str的使用方法完全一直,可是最终的结果para是一个unicode对象.
如果表达式中有非法字符,则会抛出异常:

>>> bytestring = '\xc3\xa4'     # Uuh, some non-ASCII bytestring!
>>> german_ae += bytestring
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in
position 0: ordinal not in range(128)

如果你掌握了一些基本的处理问题的方法,在Python中使用Unicode是比较方便的,但是,这并不等于说处理问题一定是高效的.
正如上面的例子中体现的,str和unicode是有一些区别的.在使用unicode的时候 ,首选要注意的问题是需要用一个字符序列和编码格式来构造一个unicode字符串.
其次,在Python中处理unicode字符还会经常遇到编码转换的问题,因为Python总是隐式转换str为unicode即使仅仅有个别字符是unicode字符.Python总是默认字符串是ascii的,如果出现了非ascii字符,会报出UnicodeDecodeError异常.
一些做过大型Python项目的开发人员用一句话总结出处理这类问题的方案: 在IO数组中进行转换.下面详细解释一下这句话的含义:

当程序从外界接收到数据的时候(从文件,网络,或者用户输入),立刻构造unicode字符,并判断正确的编码.比如,在处理http文件头的时候,可以根据里面的信息判断.
当程序向外界发送数据的时候(到文件,网络,或用户输出),判断正确的编码,然后转换为字节流输出.否则,Python会把它们转换为ascii字节流,就会出现UnicodeDecodeError异常.

根据这两个规则,能够处理大部分的unicode问题了.如果还出现了UnicodeDecodeError,看一下是否忘记构造unicode对象,或者没有正确的转换编码.
将unicode转换为特定编码的字节流,使用下面的代码:

>>> bytestring = german_ae.decode('latin1')
>>> bytestring
'\xe4'

现在butestring就是一个用latin1编码的德文字符了.它和前面的 '\xc3\xa4'表示同一个字符,只是编码不同.
在Python控制台中输入import this,会有更多有用的信息.

相关说明:

decode(...)
    S.decode([encoding[,errors]]) -> string or unicode
   
    Decodes S using the codec registered for encoding. encoding defaults
    to the default encoding. errors may be given to set a different error
    handling scheme. Default is 'strict' meaning that encoding errors raise
    a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'
    as well as any other name registerd with codecs.register_error that is
    able to handle UnicodeDecodeErrors.

import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

标签:


2007-06-25

 

Python Cookbook 1.19 判断一个字符串是否以指定字符串序列中的字符串结尾

需求:

给定一个字符串s和一个字符串序列set,判断该s是否以set中的字符串结尾,即s.endwith(str1)或s.endwith(str2)等等.其中str1和str2属于set.

讨论:

在Python中, itertools.imap方法可以很好的实现这个需求 :

import itertools
def anyTrue(predicate, sequence):
    return True in itertools.imap(predicate, sequence)
def endsWith(s, *endings):
    return anyTrue(s.endswith, endings)

对endWith的典型应用可能就是列出当前目录的图片文件了:

import os
for filename in os.listdir('.'):
    if endsWith(filename, '.jpg', '.jpeg', '.gif'):
       print filename

类似的,可以用同样的方法来处理同一类问题,anyTrue既方便又具有通用性.可以传递给它绑定方法来做字符串处理,如:
s.startwith或s.__contains__,当然,也可以直接编码:

if anyTrue(filename.endswith, (".jpg", ".gif", ".png")):

这样的可读性已经很好了.

相关说明:

绑定方法:

在Python中,如果对象具有方法,可以直接以对象的形式操作这个方法(比如,赋值,做为参数,做为返回值等)如:

L = ['fee', 'fie', 'foo']
x = L.append

这样x指向了L的一个绑定方法.调用x('fum')就等价于调用L.append('fum').
如果使用这个方法来取得类方法而不是实例的方法, 会获得非绑定方法.这样,当你调用时,需要传递对象做为第一个参数.比如:y=list.append
不能直接使用y.append('test'),因为Python并不知道要对哪一个对象进行操作.需要使用y(L,'test')来操作.



标签:


2007-06-22

 

在windows下构造".project"等文件的方法

在windows下,是不能直接创建".project"等文件的.下面提供一个方法:
1.打开notepad
2.文件->另存为...
3.输入".project"带引号
4.保存.

另外的方法就是用程序来写了.

 

Python Cookbook 1.18 在单一字符串中进行多模式替换

需求:

需要对一个字符串进行多个字串的替换.

讨论:

在某些情况下,正则式是处理字符串问题最快的方法.re库的sub方法也使得在使用正则式处理字符串替换时变得异常强大.下面给出的代码可以在一个字符串内替换参数中的字典所包含的所有字符串:

import re
def multiple_replace(text, adict):
    rx = re.compile('|'.join(map(re.escape, adict)))
    def one_xlat(match):
        return adict[match.group(0)]
    return rx.sub(one_xlat, text)

这个例子演示了如果使用正则式来进行单一字符串的多模式替换.假设有一个字典,里面的键是要替换的字符串,值是要替换为的字符串.可以使用多次replace来实现这个需求.显然 ,使用上面的代码更简便一些.
首先,我们根据要替换的键构造一个正则表达式.在上例中,使用"|"来分隔不同的模式.接下来,我们没有将要替换的字符串传递给re.sub方法,而是交给了一个回调方法one_xlat.这样,re.sub会对每一次匹配调用这个方法,并传递一个re.MatchObject实例做为它的参数.在我们的例子中,这个方法返回了匹配字符串在字典中对应的字符串.
在上例中,每一次调用multiple_replcace方法,都要重新构造正则式和定义one_xlat,有时,我们想一次做好预处理工作,进行多次替换,那可以使用下面的方法:

import re
def make_xlat(*args, **kwds):
    adict = dict(*args, **kwds)
    rx = re.compile('|'.join(map(re.escape, adict)))
    def one_xlat(match):
        return adict[match.group(0)]
    def xlat(text):
        return rx.sub(one_xlat, text)
    return xlat

调用make_xlat方法,可以传入一个字典做参数,也可以传入和dict()类似的参数来构造一个字典.make_xlat类似一个方法工厂.返回了构造好的满足我们需要的方法.下面是这个方法的使用示例,我们使用Python常用的测试方法:

if _ _name_ _ == "_ _main_ _":
    text = "Larry Wall is the creator of Perl"
    adict = {
      "Larry Wall" : "Guido van Rossum",
      "creator" : "Benevolent Dictator for Life",
      "Perl" : "Python",
    }
    print multiple_replace(text, adict)
    translate = make_xlat(adict)
    print translate(text)

有事替换是针对单词的,而不是简单的字符串替换,为了满足这个需求,我们可以使用正则式的r'\b'语法,把我们上面构造正则式的语句替换为:

rx = re.compile(r'\b%s\b' % r'\b|\b'.join(map( re.escape, adict)))

其余的代码不用改变.但这也不是一个好消息,假如我们每次要自定义正则式,有很多代码都是要"复制粘贴"的,这样对代码的重用很不好,日后也不好维护.
写"好代码"的关键是:"一次,仅仅一次!",既然我们发现上面代码有很多重复的,我们需要调整一下.将方法工厂改写成一个类,这样每次构造新的替换方法时,只要重载就可以了:

class make_xlat:
    def _ _init_ _(self, *args, **kwds):
        self.adict = dict(*args, **kwds)
        self.rx = self.make_rx( )
    def make_rx(self):
        return re.compile('|'.join(map(re.escape, self.adict)))
    def one_xlat(self, match):
        return self.adict[match.group(0)]
    def _ _call_ _(self, text):
        return self.rx.sub(self.one_xlat, text)

这样写完之后,我们前面的测试代码一点都不需要改变,而灵活性却大大增强了,要构造一个针对单词替换的方法,我们可以这样写:

class make_xlat_by_whole_words(make_xlat):
    def make_rx(self):
        return re.compile(r'\b%s\b' % r'\b|\b'.join(map(re.escape, self.adict)))

使用类替代方法,可以有效的减少代码的"复制粘贴",也更好的使用了面向对象方法,当然,代码的重用并不是简简单单的将方法封装成类,还有很多高级的特征,有兴趣的话,可以参考面向对象相关章节.

相关说明:

sub(pattern, repl, string, count=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a callable, it's passed the match object and must return
    a replacement string to be used.

dict(mapping) -> new dictionary initialized from a mapping object's
       (key, value) pairs.

compile(pattern, flags=0)
    Compile a regular expression pattern, returning a pattern object.

标签:


2007-06-21

 

换回2003

无法忍受Office2007吞噬虚拟内存的能力,开了两个文档,20分钟后,虚拟内存就使用了230M,即使关掉一个文档依然.难怪前几天只开了3个文档,一会就说虚拟内存耗尽了.
换回2003了,从没有感觉到2003居然这么快,呵呵.
BTW:安装完2003,也不会从输入法列表中删除google拼音.
服了MS.真的.

 

Python Cookbook 1.17 在Python2.4(2.5)中进行字符串替换

需求:

和上一节一样,给定一个字符串和一个字典,需要在字符串中替换指定字串,并用字典中的字符串进行替换.

讨论:

在Python2.4中,提供了string.Template类来实现这个功能.下面是一个例子:

import string
# make a template from a string where some identifiers are marked with $
new_style = string.Template('this is $thing')
# use the substitute method of the template with a dictionary argument:
print new_style.substitute({'thing':5})      # emits: this is 5
print new_style.substitute({'thing':'test'}) # emits: this is test
# alternatively, you can pass keyword-arguments to 'substitute':
print new_style.substitute(thing=5)          # emits: this is 5
print new_style.substitute(thing='test')     # emits: this is test

在Python2.3中,实现上面的需求稍微有些复杂:

old_style = 'this is %(thing)s'

需要将变量用小括号括起来,在进行替换的时候,使用%,后面跟着要替换的字典.

print old_style % {'thing':5}      # emits: this is 5
print old_style % {'thing':'test'} # emits: this is test

这样的代码在2.4中依然能够工作,而且2.4提供了更简便的方法:string.Template.
当构造template实例的时候,需要用$标识要替换的变量,后面可以直接跟上变量名,或者数字,还有大括号括起来的变量.下面是一个例子:

form_letter = '''Dear $customer,
I hope you are having a great time.
If you do not find Room $room to your satisfaction,
let us know. Please accept this $$5 coupon.
            Sincerely,
            $manager
            ${name}Inn'''
letter_template = string.Template(form_letter)
print letter_template.substitute({'name':'Sleepy', 'customer':'Fred Smith',
                                  'manager':'Barney Mills', 'room':307,
                                 })

下面是输出:

Dear Fred Smith,
I hope you are having a great time.
If you do not find Room 307 to your satisfaction,
let us know. Please accept this $5 coupon.
            Sincerely,
            Barney Mills
            SleepyInn

有时,最方便的方法是使用substitute提供的locals()方式,可以直接用本地变量字典中的变量进行替换:

msg = string.Template('the square of $number is $square')
for number in range(10):
    square = number * number
    print msg.substitute(locals( ))

另外一种方式是将关键字做为参数,而不是使用字典:

msg = string.Template ('the square of $number is $square')
for i in range(10):
    print msg.substitute(number=i, square=i*i)

当然,也可以同时使用字典和参数:

msg = string.Template('the square of $number is $square')
for number in range(10):
    print msg.substitute(locals( ), square=number*number)

为了防止参数形式和字典形式冲突,参数形式的优先级更高一些:

msg = string.Template('an $adj $msg')
adj = 'interesting'
print msg.substitute(locals( ), msg='message')
# emits an interesting message

相关说明:

substitute(self, *args, **kws) unbound string.Template method

标签:


2007-06-20

 

Python Cookbook 1.16 在字符串中进行插值和替换

需求:

给定一个字符串和一个字符字典,返回这个字符串的副本,要求其中的特定字串替换为字典中对应的字符串.

讨论:

在Python2.3以后,可以使用以下的代码来实现这个功能:

def expand(format, d, marker='"', safe=False):
    if safe:
        def lookup(w): return d.get(w, w.join(marker*2))
    else:
        def lookup(w): return d[w]
    parts = format.split(marker)
    parts[1::2] = map(lookup, parts[1::2])
    return ''.join(parts)
if _ _name_ _ == '_ _main_ _':
    print expand('just "a" test', {'a': 'one'})
# emits: just one test

当safe参数为False时,给定的marker必须在字典d中存在,否则要抛出KeyError异常.当safe参数的值为True时,marker如果在字典d中不存在,依然保留原值.

在上面的expand方法中,有几点是要注意的.它定义了两个内嵌函数,而且取决于safe的值是否为True.如果safe为false,它只是返回了字典的中的项,如果不存在,就会抛出KeyError异常.如果safe为True ,使用字典的get方法,如果键不存在,就返回原值.如果想用别的形式返回,用mark+w+mark也是mark*2的可选替换方案.

在上面的代码中,我们也遵循了Python字符串处理的"分割/处理/合并"原则,在处理过程中,它总是处理偶数索引的片段.这样保证是处于""中间的字串.

另外,也可以使用string.Template来实现这个需求,可以参考相关章节.

相关说明:

dict.get(...)
    D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.


标签:


2007-06-19

 

Python Cookbook 1.15 控制和转换Tab字符

需求:

需要将字符串中指定数量的空格和Tab相互转换.

讨论:

空格和Tab的相互转换在文本处理中是很常见的需求,在Python中,可以方便的使用expandtabs方法来实现这个需求.因为字符串是不可以修改的,所以该方法返回了源字符串的一个副本.当然 ,我们可以将源字符串的名称绑定到修改后的字符串上.显然两个对象是不同的,如:

mystring = 'hello\tworld'
old = mystring
mystring = mystring.expandtabs()
print id(old)
print id(mystring)

>>> 30896000
30896048

这样并不改变源字符串所指向的对象,而是mystring指向了新构造的字符串.默认的情况下,expandtabs使用8个空格替换Tab,你可以传递希望的数字做为expandtabs的参数:

把空格转换为Tab是比较少见的需求,所以在Python中,并没有提供这样的方法.我们可以自己来写代码完成这个需求 ,在Python中,使用"分割/处理/合并"这样的流程处理和转换字符串是最有效的.

def unexpand(astring, tablen=8):
    import re
    # split into alternating space and non-space sequences
    pieces = re.split(r'( +)', astring.expandtabs(tablen))
    # keep track of the total length of the string so far
    lensofar = 0
    for i, piece in enumerate(pieces):
        thislen = len(piece)
        lensofar += thislen
        if piece.isspace( ):
            # change each space sequences into tabs+spaces
            numblanks = lensofar % tablen
            numtabs = (thislen-numblanks+tablen-1)/tablen
            pieces[i] = '\t'*numtabs + ' '*numblanks
    return ''.join(pieces)

这里写的方法只适用于当行字符串,如果多行字符串,请使用
' '.join([ unexpand(s) for s in astring.splitlines(True) ]).

上面的方法使用正则式来处理字符串,在很多语言中, 正则式都是处理字符串的利器.在上面的方法中,使用re的split方法来完成需求,它返回了一个序列,里面是用原先用空白字符分隔的字符串和空白字符.接下来,我们对每一个空白字符进行处理,尽可能多的把它们替换成Tab.

在编程中可能遇到另外的需求,比如处理行首的缩进.在Python中,需要检查源码中的缩进是否一致,是否有空格和Tab混用的情况,然后可以把它们统一换成空格.使用上面的方法会遇到一个问题,就是也会替换行中的Tab或空格,而这也许不是想要的结果,遇到这样的情况,我们需要修改一下正则式:

def expand_at_linestart(P, tablen=8):
    import re
    def exp(mo):
        return mo.group( ).expand(tablen)
    return ''.join([ re.sub(r'^\s+', exp, s) for s in P.splitlines(True) ])

上面的方法利用了re的sub方法,它在处理字符串时,每遇到一个匹配项,会调用参数中的方法来处理,可以看出,上面的方法是针对多行字符串来写的,当然,它也同样适用与单行字符串.


相关说明:

re.split(pattern, string, maxsplit=0)
    Split the source string by the occurrences of the pattern,
    returning a list containing the resulting substrings.

re.sub(pattern, repl, string, count=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a callable, it's passed the match object and must return
    a replacement string to be used.

标签:


 

Python Cookbook 1.14 控制多行字符串的缩进

需求:

现有一个多行字符串,要从它衍生出另一个字符串,只是要处理每行前面的空白字符,或者调整缩进.

讨论:

使用string本身提供的方法很方便的就可以实现这个需求:
 def reindent(s, numSpaces):
leading_space = numSpaces * ' '
lines = [ leading_space + line.strip( )
for line in s.splitlines( ) ]
return '\n'.join(lines)

当你处理文本的时候,可能要控制每行前的缩进,上面的方法可以控制一段字符串都使用相同的缩进.如:

>>> x = """  line one
...     line two
...  and line three
... """
>>> print x
  line one
    line two
 and line three
>>> print reindent(x, 4)
    line one
    line two
    and line three

即使原先的行缩进都不同,上面的方法都会使它们保持一致的缩进,这样虽然很方便,但有时候并不满足我们的需求:
比如我们想根据原先的缩进增加或减少空格,当然,如果是减少空格,我们需要判断原先的空格是否够减少的数字.我们用下面的代码就可以实现需要的功能:

def addSpaces(s, numAdd):
    white = " "*numAdd
    return white + white.join(s.splitlines (True))
def numSpaces(s):
    return [len(line)-len(line.lstrip( )) for line in s.splitlines( )]
def delSpaces(s, numDel):
    if numDel > min(numSpaces(s)):
        raise ValueError, "removing more spaces than there are!"
    return '\n'.join([ line[numDel:] for line in s.splitlines( ) ])

当然,上面的方法之所以可以这样简洁,都得益于str.splitlines方法,这个方法相当于split('\n').而splitlines还有一个特点,它能保留分割后行尾的换行符,如果你使用splitlines(True)就可以实现这个功能.

当然,我们也可以用上面的方法来构造更多的方法,比如:

def unIndentBlock(s):
    return delSpaces(s, min(numSpaces(s)))

相关说明:

splitlines(...)
    S.splitlines([keepends]) -> list of strings
   
    Return a list of the lines in S, breaking at line boundaries.
    Line breaks are not included in the resulting list unless keepends
    is given and true.

标签:


2007-06-18

 

Python Cookbook 1.13 处理字符串的字串

需求:

要访问字符串的字串,比如,读取了固定长度的字符串,要获取其中的固定字段.

讨论:

最简单的方法是使用切片功能:

afield = theline[3:8]

可是这种方法一次只能处理一个字符串.
如果以字段的长度来考虑,可以使用struct.unpack来处理:

import struct
# Get a 5-byte string, skip 3, get two 8-byte strings, then all the rest:
baseformat = "5s 3x 8s 8s"
# by how many bytes does theline exceed the length implied by this
# base-format (24 bytes in this case, but struct.calcsize is general)
numremain = len(theline) - struct.calcsize(baseformat)
# complete the format with the appropriate 's' field, then unpack
format = "%s %ds" % (baseformat, numremain)
l, s1, s2, t = struct.unpack(format, theline)

如果想忽略剩下的而不是保留,只要unpack需要的长度就可以了.

l, s1, s2 = struct.unpack(baseformat, theline[:struct.calcsize(baseformat)])

如果想以5个字节为单位切分,可以使用切片列表:

fivers = [theline[k:k+5] for k in xrange(0, len(theline), 5)]

把字符串分割成字符会更简单一些:

chars = list(theline)

如果想按照特定的宽度序列来切分,就使用切片列表来实现:

cuts = [8, 14, 20, 26, 30]
pieces = [ theline[i:j] for i, j in zip([0]+cuts, cuts+[None]) ]

在切片列表中调用zip方法返回了 (cuts[k], cuts[k+1])值对形式的列表 ,第一个值对是(0, cuts[0]), 而最后一个值对是(cuts[len(cuts)-1], None). 换句话说,每一个值对表示了切片的起始位置和中止位置.如上面的代码,第一片是[0-7],然后是[8-13],[14-19],[20-25],[26-29]最后是[30-...]及字符串的结尾.
用这样的方法,可以灵活的控制切片的宽度.

在Python中,struct.unpack方法是经常被使用的 ,甚至有些时候可以做为切分字符串的不错的选择.我们可以把上面的代码写成一个方法,这个方法还带了一个参数lastField表示是否要处理最后剩下的字串.

def fields(baseformat, theline, lastfield=False):
# by how many bytes does theline exceed the length implied by
# base-format (struct.calcsize computes exactly that length)
    numremain = len(theline)-struct.calcsize(baseformat)
# complete the format with the appropriate 's' or 'x' field, then unpack
    format = "%s %d%s" % (baseformat, numremain, lastfield and "s" or "x")
    return struct.unpack(format, theline)

这里需要注意的就是lastField参数的处理,我们使用了lastfield and "s" or "x"表示是否保留切分后剩下的字符串.

因为field方法可能在循环中调用,我们将元组(baseformat,len(theline),lastField)缓存起来会更高效一些,下面我们提供一个版本,使用自动缓存的代码:

def fields(baseformat, theline, lastfield=False, _cache={ }):
# build the key and try getting the cached format string
    key = baseformat, len(theline), lastfield
    format = _cache.get(key)
    if format is None:
    # no format string was cached, build and cache it
        numremain = len(theline)-struct.calcsize(baseformat)
        _cache[key] = format = "%s %d%s" % ( baseformat, numremain, lastfield and "s" or "x")
    return struct.unpack(format, theline)

这里缓存了与处理的结构,并将它存在_catch字典里面,通过这样来提高效率.经过测试,这种方法能提高30%到40%的效率,如果field不是程序中映像效率的核心代码,可以不采用这种方法.

下面是两个和需求相关的问题的解决方案:

以n的字节为单位切分字符串:

def split_by(theline, n, lastfield=False):
# cut up all the needed pieces
    pieces = [theline[k:k+n] for k in xrange(0, len(theline), n)]
    # drop the last piece if too short and not required
    if not lastfield and len(pieces[-1]) < n:
        pieces.pop( )
    return pieces

以cuts中描述的字段宽度来切分:

def split_at(theline, cuts, lastfield=False):
# cut up all the needed pieces
    pieces = [ theline[i:j] for i, j in zip([0]+cuts, cuts+[None]) ]
    # drop the last piece if not required
    if not lastfield:
        pieces.pop( )
    return pieces

另一个版本是使用generator方式:

def split_at(the_line, cuts, lastfield=False):
    last = 0
    for cut in cuts:
        yield the_line[last:cut]
        last = cut
    if lastfield:
        yield the_line[last:]

def split_by(the_line, n, lastfield=False):
    return split_at(the_line, xrange(n, len(the_line), n), lastfield)

基于generator方式的方法适用于对一个序列中的元素进行直接或间接的循环操作使用,如' '.join,如果采用了这样的方式,可以用下面的形式来调用:

list_of_fields = list(split_by(the_line, 5))

相关说明:

zip(...)
    zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
   
    Return a list of tuples, where each tuple contains the i-th element
    from each of the argument sequences.  The returned list is truncated
    in length to the length of the shortest argument sequence.

calcsize(fmt)
    Return size of C struct described by format string fmt.
    See struct.__doc__ for more on format strings.


标签:


 

Python Cookbook 1.12 转换单词的大小写

需求:

转换单词的大小写.

讨论:

string的upper和lower方法等实现这样的功能.它们都不带参数,返回转换后字符串的副本.
如:
>>>print 'BIG'.lower()
big
>>>print 'little'.upper()
LITTLE

如果要单词的首字母大写,其余字母小写,要使用capitalize方法.而如果要一个字符串中几个单词的首字母都大写,要使用title方法.如:

>>>print 'hello world'.captalize()
Hello world
>>>print 'hello world'.title()
Hello World

大小写控制是字符串处理中经常遇到的需求,所以提供了几个处理大小写的方法.另外,也提供了一些判断大小写的方法:
islower,isupper和istitle,可是没有提供iscaptalize方法,不过我们可以自己来实现一个简单版本:

def iscaptalize(s):
    return s==s.captalize()

这个方法有一个缺陷, 不能处理空字符或者空白字符.下面是一个比较严格的版本:

import string
notrans = string.maketrans ('', '')  # identity "translation"
def containsAny(str, strset):
    return len(strset) != len(strset.translate(notrans, str))
def iscapitalized(s):
    return s == s.capitalize( ) and containsAny(s, string.letters)

其中的containsAny方法,用于判断在字符串str中,是否包含指定字符集strset中的字符.使用string.letters来获得字母.这样就能处理空字符和空白字符的情况了.

相关说明;

lower(...)

    S.lower() -> string
    
    Return a copy of the string S converted to lowercase.

upper(...)
    S.upper() -> string
    
    Return a copy of the string S converted to uppercase.
title(...)
    S.title() -> string
    
    Return a titlecased version of S, i.e. words start with uppercase
    characters, all remaining cased characters have lowercase.

islower(...)
    S.islower() -> bool
    
    Return True if all cased characters in S are lowercase and there is
    at least one cased character in S, False otherwis

isupper(...)
    S.isupper() -> bool
    
    Return True if all cased characters in S are uppercase and there is
    at least one cased character in S, False otherwise.

istitle(...)
    S.istitle() -> bool
    
    Return True if S is a titlecased string and there is at least one
    character in S, i.e. uppercase characters may only follow uncased
    characters and lowercase characters only cased ones. Return False
    otherwise.

标签:


 

无题

一个好事:

pidgin出2.02版了,已经修正了QQ不能正常登录的问题,不过几个小问题还是没有解决:
1.安装后,需要自己手动把locale下面的zh_CN文件夹修改为en,才能显示中文界面.
2.当主题设置为default时,QQ上的所有汉字都还是自定义表情.
不过,还是已经很满足了,毕竟又可以正常使用了!自己编译的2.0.1用处也不大了.

一个不太好的事情:

不知道哪根筋出问题,突发奇想装上了Office2007,完成后发现ctrl+space居然出来的是微软拼音输入法, 我原来的输入法设置只有en和google拼音.按下shife+space,google拼音依然不出来.自己预感有有些问题,打开输入法设置一看,果然:输入法的设置只剩下en和微软拼音2007了.因为没有装别的输入法,所以不敢乱猜想是不是恶意竞争导致的结果,还是google拼音自己的bug,可是结果总是让人很不舒服.
想起有一次修复安装windows,结果所有的Borland公司和Sun公司的产品都不能正常运行,原来,它把path中和这两个公司相关的项都删掉了,而其它软件的path项以及自己设定的都还在,让人着实不爽了一把.

帮着MM改了一下论文的格式,发现那个页码怎么也不能做成像模板那样的:前3页和目录没有页码,中文摘要和英文摘要的页码是I,II,剩下的是阿拉伯数字.
搞了很久都不能搞定,不知道是word太难用了?模板设计的太变态了?还是我们用word的水平还不够?
也许都有一点吧.

2007-06-15

 

Python Cook 1.11 判断字符串中是文本还是字节流

需求:

Python可以使用string来保存字符串或任意字节.我们需要判断字符串中的是文本,还是字节流,目前还没有什么明确的算法来区分两种情况.

讨论:

我们需要自己设计出算法来区分字节流和字符串,比如判断是否有超过30%的字节都包含着空字符,控制字符或者非ascii字符(字节高位设置为1),我们必须要自己处理,也要为不同的场景设计出不同的算法:

from _ _future_ _ import division # ensure / does NOT truncate
import string
text_characters = "".join(map(chr, range(32, 127))) + "\n\r\t\b"
_null_trans = string.maketrans("", "")
def istext(s, text_characters=text_characters, threshold=0.30):
    # if s contains any null, it's not text:
    if "\0" in s:
        return False
    # an "empty" string is "text" (arbitrary but reasonable choice):
    if not s:
        return True
    # Get the substring of s made up of non-text characters
    t = s.translate(_null_trans, text_characters)
    # s is 'text' if less than 30% of its characters are non-text ones:
    return len(t)/len(s) <= threshold

可以通过设置istext的threshold参数来定制算法,也可以通过修改text_characters参数来改变字符集.比如,如果需要判断ISO-8859-1编码的意大利重音字符,就需要添加重音符号给text_characters,如" àèéìÃ2Ã1"

上面的代码中需要注意的是,/做整数除法,不会截断小数点,如果要取整除法,请使用//. 默认情况下,如果除数和被除数都是整数,那么结果也是整数.

例如:

>>>5 / 3
1
>>>5 // 3
1
>>>5.0 / 3
1.6666666666666667
>>>5.0 // 3
1.0

所以在例子的第一行我们写了:

from _ _future_ _ import division

这保证在我们的代码中,被除数和除数都是整数时,它们的结果依然可以是小数.

相关说明:

from __future__ import division 请放在代码的 第一句,我自己安装SOAPpy库时,就因为这个问题不能正常安装,修改代码后安装成功了.

标签:


 

Python Cook 1.10 从字符串中过滤非指定集合的字符

需求:

给定一个字符集合,需要构造一个过滤函数,使对于任意字符串s,改方法都能将其,使只包含集合内的字符.

讨论:

string的Translate方法是处理这一类问题的最快方法.然而,要想用translate解决需求提出的问题,我们需要做一些前期工作.translate的第一个参数是一个字符转换表.我们在这里并不需要转换字符 ,所以我们要让第一个参数表示不做任何转换.第二个参数是指要删除的字符串,因为我们的需求不是要删除字符,而是要保留字符,所以我们要构造出我们不需要的字符的集合.对于这样的要求,使用内函数是比较方便的,如在上一节,我们也实现了类似的需求.
import string
# 构造一个字符转换表,表示不做任何转换.
allchars = string.maketrans('', '')
def makefilter(keep):
# 构造出我们不需要的字符集合
delchars = allchars.translate(allchars, keep)
# 定义内函数,返回我们需要的过滤器.
def thefilter(s):
return s.translate(allchars, delchars)
return thefilter
if _ _name_ _ == '_ _main_ _':
just_vowels = makefilter('aeiouy')
print just_vowels('four score and seven years ago')
# 输出: ouoeaeeyeaao
print just_vowels('tiger, tiger burning bright')
# 输出: ieieuii
理解这 一节的关键是理解string的maketrans和translate的实现机制.translate返回了一个字符串s的副本,在副本中,处于第一个参数(转换表)中的字符被替换了,而处于第二个参数中的字符被删除了.maketrans方法是用来构造字符转换表的.所谓字符转换表t是一个包含256个字符的字符串: 当你把t做为translate的第一个参数时,s中的每一个字符c都被转换为t[ord(c)].
在这一节中,我们首先分写了问题,将它划分为与处理和执行两个部分,这样不但减少了问题的复杂度,也提高了执行的效率.allchars是可重用的,可以被所有的过滤函数共享.而translate的第二个参数delete,我们使用了方法工厂来构造,也使用translate来达到最高的效率.

如果把allchars做为makefilter的参数,这样的过滤器能返回排序好的字符串,而且删除了重复字符.
可以这样来定义:

def canonicform(s):
    return makefilter(s)(allchars)

其中return那一句,s做为makefilter的参数,而allchars做为thefilter的参数.
也可以使用lambda函数写法来定义内函数:

return lambda s: s.translate(allchars, delchars)

当然,使用def来定义使得代码更清晰,易读.

对于这个需求,我们也会想到用set来处理,然而,使用translate处理字符串是最高效的.当然其局限性也显而易见,它只能处理ascii常规字符,不能处理unicode.

为了使得unicode字符串也能满足我们的需求,还要做一些与处理工作,unicode的translate方法只有一个参数:一个map或者一个序列,它包含了字符串中字符的序列,表中不包含的字符,就直接复制到结果中去,对于要删除的字符,在map中对应的值是None.

一般来说,我们使用dict或list做为unicode字符串translate方法的参数.但对于这一节的需求(保留一些字符,删除其余的),我们要初始化一个巨大的map,然后把它们的值都映射为None.相反,我们构造一个简单的类,让它实现__getitem__方法.__getitem__方法是让外界可以通过[]来索引序列元素的方法,可以理解为重载了[].同时,对于简单类,我们可以使用别名来使用它.为了使类可以直接执行,我们让它实现恶劣__call__方法.

import sets
class Keeper(object):
    def _ _init_ _(self, keep):
        self.keep = sets.Set(map(ord, keep))
    def _ _getitem_ _(self, n):
        if n not in self.keep:
            return None
        return unichr(n)
    def _ _call_ _(self, s):
        return unicode(s).translate(self)
makefilter = Keeper
if _ _name_ _ == '_ _main_ _':
    just_vowels = makefilter('aeiouy')
    print just_vowels(u'four score and seven years ago')
# 输出: ouoeaeeyeaao
    print just_vowels(u'tiger, tiger burning bright')
# 删除: ieieuii

相关说明:

translate(...)
    S.translate(table) -> unicode
   
    Return a copy of the string S, where all characters have been mapped
    through the given translation table, which must be a mapping of
    Unicode ordinals to Unicode ordinals, Unicode strings or None.
    Unmapped characters are left untouched. Characters mapped to None
    are deleted.


标签:


2007-06-14

 

Python Cook 1.9 简化使用string的translate方法

需求:

想使用强大的string的translate方法,却困惑于它复杂的用法和参数,也不知道string.maketrans的工作机制.我们需要的只是简单的使用它们的功能.

讨论:

我们在这里简单实现一个方法工厂,可以处理常用的这一类的问题:
import string
def translator(frm='', to='', delete='', keep=None):
if len(to) == 1:
to = to * len(frm)
trans = string.maketrans(frm, to)
if keep is not None:
allchars = string.maketrans('', '')
delete = allchars.translate(allchars, keep.translate(allchars, delete))
def translate(s):
return s.translate(trans, delete)
return translate

下面是一些translator的用法:

可以只保留需要的字符

>>> digits_only = translator(keep=string.digits)
>>> digits_only('Chris Perkins : 224-7992')
'2247992'
可以过滤指定的字符:
>>> no_digits = translator(delete=string.digits)
>>> no_digits('Chris Perkins : 224-7992')
'Chris Perkins : -'

可以替换指定的字符:

>>> digits_to_hash = translator(from=string.digits, to='#')
>>> digits_to_hash('Chris Perkins : 224-7992')
'Chris Perkins : ###-####'

相关说明:

内部方法,在上面的例子中,我们使用了一个内部方法来实现方法工厂,返回一个变量来表示内部方法,然后接收参数来实现功能.
最简单的方法工厂可以写成下面这个样子:
def make_adder(basenum):
    def adder(args):
        return args+basenum
    return adder

比如我们要做一个以100为基数的加法器,可以这样使用:

base100 = make_addr(100)
print base100(45)
>>>145


讨论中的例子,我们返回了自己定制的translate方法,它以我们自己定制的delete为参数,来实现我们的keep需求:
allchars = string.maketrans('', '')

返回了包含所有字符的列表
keep.translate(allchars, delete)

返回了过滤掉删除字符的字符表.
allchars.translate(allchars, keep.translate(allchars, delete))
又做了一次运算,现在剩下的只是要删除的字符了.

如果keep和delete不同时使用,可以这样写:
allchars = string.maketrans('', '')
delete = allchars.translate(allchars, keep)
maketrans(...)
    maketrans(frm, to) -> string
   
    Return a translation table (a string of 256 bytes long)
    suitable for use in string.translate.  The strings frm and to
    must be of the same length.

translate(...)
    S.translate(table [,deletechars]) -> string
   
    Return a copy of the string S, where all characters occurring
    in the optional argument deletechars are removed, and the
    remaining characters have been mapped through the given
    translation table, which must be a string of length 256.

标签:


 

Python Cook 1.8 判断字符串内是否包含指定集合的字符

需求:

给定一个字符集合,需要判断在特定字符串内,是否包含该集合的字符.

讨论:

最简单,直观,效率最高的算法就是使用循环迭代.而且还具有通用性,不仅适用与字符串,也适用与其它序列.
 def containsAny(seq, aset):
""" Check whether sequence seq contains ANY of the items in aset. """
for c in seq:
if c in aset: return True
return False

当然,也可以使用更高级的办法,用Python提供的迭代工具:

import itertools
def containsAny(seq, aset):
for item in itertools.ifilter(aset._ _contains_ _, seq):
return True
return False

另:对于和集合相关的问题,都可以使用集合来解决.对于我们需求,可以使用下面的方式来实现:

def containsAny(seq, aset):
return bool(set(aset).intersection(seq))

这个方法返回了两个集合的交集,也就是说,两个集合中所有的元素都进行了判断,从效率上讲,没有上面提供的两种方法好.
然而,如果需求是需要判断集合内的字符是否都包含在字符串内,使用集合提供的方法会更方便一些:

def containsAll(seq, aset):
""" Check whether sequence seq contains ALL the items in aset. """
return not set(aset).difference(seq)

如果需求是判读字符串内是否只包含集合内的字符,可以用下面的算法:

def containsOnly(seq, aset):
""" Check whether sequence seq contains ONLY items in aset. """
for c in seq:
if c not in aset: return False
return True
相关说明:

intersection(...)
    Return the intersection of two sets as a new set.
   
    (i.e. all elements that are in both sets.)

difference(...)
    Return the difference of two sets as a new set.
   
    (i.e. all elements that are in this set but not the other.)

标签:


 

Python Cook 1.7 反转字符串中的字符或单词

需求:

反转字符串中的单词或字符.

讨论:

如果是别的语言,通常的做法是写一个循环,然后利用临时变量,构造反转后的字符串.对于字符的反转,在Python中有一个非常简便的方法,利用切片功能.

newstring = astring[::-1]

这样,就能获得astring的反转字符串了.
对于单词的反转,可以利用序列的reverse方法:

words = astring.split()
words.reverse()
newstring = ' '.join(wrods)

这里我们假设单词之间是用空白字符分隔的.空白字符包括空格,回车,TAB等.
用一个语句表示的方法:

newstring = ' '.join(astring.split()[::-1])

这里我们用到了上面说的第一种方法.
看了上面两个方法,需要注意的时,string没有reverse方法.可能因为有了[::-1]已经满足了需求了吧.

如果单词间的分隔符不是空白字符,可以使用正则式来实现需求,如:

import re
newstring = re.split(r'(\s+)', astring)         # 用'()'分隔
newstring.reverse( )
newstring = ''.join(newstring)  

或者更简单的写法:

newstring = ''.join(re.split(r'(\s+)', astring)[::-1])

当然,如果这样写,就不符合Python的简单清晰的原则了.

相关说明:

split(...)
    S.split([sep [,maxsplit]]) -> list of strings
    
    Return a list of the words in the string S, using sep as the
    delimiter string.  If maxsplit is given, at most maxsplit
    splits are done. If sep is not specified or is None, any
    whitespace string is a separator.

join(...)
    S.join(sequence) -> string
    
    Return a string which is the concatenation of the strings in the
    sequence.  The separator between elements is S.

reverse(...)
    L.reverse() -- reverse *IN PLACE*

关于正则式的说明请看后续的章节.

标签:


 

Python Cook 1.6 拼接字符串

需求:

要将一些字符串拼接成一个字符串.

讨论:

最容易想到的方法是使用'+':

newstring = str1 + ' ' + str2 + ' ' + str3 + '!'

然而在Python中,不推荐使用上面的做法,这可能造成代码的效率底下.
string对象是不可改变的字符序列,一个'+'操作,要先构造一个新的string对象,然后再做字符串的拼接,而不是直接改变原有的字符串.当一个操作完成后,临时使用的对象又被释放掉了.也就是说,如果有N字符串要拼在一起,使用'+'操作的话,就要生成N-1次临时string对象,做N-1次拼接运算,然后再释放这N-1个临时对象.这样的效率是可想而知的 .
根据上面的说明,推荐使用Python的内建方法:join.

newstring = ' '.join([str1,str2,str3,'!'])

类似的,可以使用另外一种方法来实现我们的需求.
在Python中,可以使用字符串格式化符号:%.像C的printf一样的使用方法:

newstring =  '%s %s %s!' %(str1,str2,str3)

这个方法不但适用字符串的拼接,也适合与多种类型构成字符串,如:

newstring = '%d + %d = %d %s' %(1,2,3,'done')

两种方法各有各的优点:join适用于要拼接的字符串个数不定,且连接字符确定的情况;而%适用于

相关说明:

join(...)

    S.join(sequence) -> string
    
    Return a string which is the concatenation of the strings in the
    sequence.  The separator between elements is S.

标签:


2007-06-13

 

SciTE配置文件

#~ 中文字符乱码正确显示
#linux下面使用:code.page=65001
if PLAT_WIN
    code.page=936
    output.code.page=936
if PLAT_GTK
    code.page=65001
    output.code.page=65001
#~ Big5:code.page=950
#~ 最大化
position.width=-1
position.height=-1
#~ 滚动条
end.at.last.line=0
#~ 工具条
#~ toolbar.visible=1
#~ 状态栏
statusbar.visible=1
#~ 自动补全xml结束标签
xml.auto.close.tags=1
#~ 记录最近打开的文件
save.recent=1
#~ 显示行号
line.margin.visible=1
#~ 设置状态栏
statusbar.text.1= [ 行: $(LineNumber) , 列: $(ColumnNumber) ,共:$(NbOfLines) ] ($(OverType)) | ($(EOLMode))
#~ 当前行
caret.line.back=#E8F2FE
#~ 文档背景,保护眼睛
style.*.32=$(font.base),back:#CCE8CF
#~ 行号字符宽度
line.margin.width=3+
#~ 一个文档时隐藏tab栏
tabbar.hide.one=1
#~ 多行tab栏模式
tabbar.multiline=1

#~ 当前文件被外部修改时自动重新载入
load.on.activate=1
#~ 当load.on.activate=1时,且本条设1,重新载入时会先询问你
are.you.sure.on.reload=1
#~ 只有一个scite实例
check.if.already.open=1
#~ 自动补全
autocompleteword.automatic=1
#~ 调整编辑区和输出区的大小
magnification=1
output.magnification=1
#~ 输出栏在下面
split.vertical=0
#~ 执行命令前清空输出区的内容
clear.before.execute=1
#~ 缓冲区个数
buffers=18
#~ 字体大小
font.base=font:Verdana,size:9

#~启用折叠
fold=1
#~编辑区折叠列初始化宽度.
fold.margin.width=16
#~折叠符号,0为三角形,1为展开-收缩+, 2为圆形,3方形。一般选2
fold.symbols=2
#~初始化是否折叠。一般设成1,这样再长的文章,因为只显示结构,看上去了一目了然。
#~ fold.on.open=1
#~设成1,伸展时折叠符号区有一条短线指示折叠位置。收缩时显示一条长线。
fold.compact=1
#~xml和html文件折叠
fold.html=1

#~标题栏显示信息,0为文件名,1为全路径,2为显示文件名和目录名。
title.full.path=1
#~一个tab占几个空格,一般设成4。
tabsize=4
#~一个自动缩进占几个空格,一般为4
indent.size=4
#~1为初始化时自动换行
wrap=0

#~设置中文自动提示用
chars.cn=的一是了不在有人上这大我国来们和个他中说到地为以子小就时全可下要十生会也出年得你主用那道学工多去发作自好过动对行里能二天三同成活太事面民日家方后都于之分经种还看产所起把进前着没而样部长又问法从本定见两新现如么力等电开五心只实社水外政很高月业当义些加老着四头因向理点合明无机意使第正度物想体此知关制然其表重化应各但者间百比什儿公做九相气命西话将内与由利今手平量员回情几最八级位结性代教次路党六便原军总走象口七先常题入给己队战果完反白建革立少文打论门东女放期真数展资通农名解叫提或山线条别万系已变形它边阶报官决她及争声北求世耍美再听才运必安取被南接华干区身济共计特改吃书马组界议车并海育思设件光强品直许造务流治领联金记任受极基质指帮目市快千导花科难深保住统管处认志图则研劳每场带亲至根更斗收信究且怎近非料何呢热术夫眼交布石达步拉众省风据奸增程火团字却油米委色式切望器办群观算调母土较请元爱持清广张连压觉识林际举即死专局类空单权毛师商孩装批府找往王校该未席约照易神克号京转须半习青早规验拿服节精树传备钱技讲告德参斯具织集病友谈示积亚复厂越支婚历兵胜选整铁势笑院板球河吗除准况影倒若格断甚速言采哪离县写台古远
#~ 缩进可见
view.indentation.whitespace=1

#~长行指示
edge.column=200
edge.mode=1
edge.colour=#F9F9F9

#~设置错误行的前景色和背景色
error.marker.fore=#0000A0
error.marker.back=#DADAFF
#~ 定义菜单
user.context.menu=\
||\
UTF-8|IDM_ENCODING_UCOOKIE|\
CodePage|IDM_ENCODING_DEFAULT|

#~ 定义api文件
if PLAT_WIN
    api.$(file.patterns.java)=$(SciteDefaultHome)\api\java150.api
    api.$(file.patterns.cs)=$(SciteDefaultHome)\api\cs.api
    api.$(file.patterns.cpp)=$(SciteDefaultHome)\api\cpp.api
    api.$(file.patterns.py)=$(SciteDefaultHome)\api\python.api
    api.$(file.patterns.php)=$(SciteDefaultHome)\api\php.api
if PLAT_GTK
    api.$(file.patterns.java)=$(SciteDefaultHome)/api/java150.api
    api.$(file.patterns.cs)=$(SciteDefaultHome)/api/cs.api
    api.$(file.patterns.cpp)=$(SciteDefaultHome)/api/cpp.api
    api.$( file.patterns.py)=$(SciteDefaultHome)/api/python.api
    api.$(file.patterns.php)=$(SciteDefaultHome)/api/php.api
#~ 定义缩写文件
if PLAT_WIN
    abbreviations.*.cpp=$(SciteDefaultHome)\abbrev\cppAbbrev.properties
    abbreviations.*.cs=$(SciteDefaultHome)\abbrev\csAbbrev.properties
    abbreviations.*.java=$(SciteDefaultHome)\abbrev\javaAbbrev.properties
if PLAT_GTK
    abbreviations.*.cpp=$(SciteDefaultHome)/abbrev/cppAbbrev.properties
    abbreviations.*.cs=$(SciteDefaultHome)/abbrev/csAbbrev.properties
    abbreviations.*.java=$(SciteDefaultHome)/abbrev/javaAbbrev.properties

 

Python Cook 1.5 去掉字符串两边的空格

需求:

需要过滤掉输入字符串的前导,后续空格或其它字符.这在处理用户输入的时候比较有用.

讨论:

和其它语言类似,Python也提供了lstrip,rstrip和strip方法,类似Delphi和C#的trim方法.
用法:

>>> x = '    test   '
>>> print '|', x.lstrip( ), '|', x.rstrip( ), '|', x.strip( ), '|'
| test    |     test | test |

另外,这三个方法还可以接受一个参数,用于过滤指定的字符组成的串,如:

>>> x = 'xyxxyy testyx  yyx'
>>> print '|'+x.strip('xy')+'|'
| testyx  |

需要注意的是:testyx前面有一个空格,因为空格前的所有的x,y都被过滤掉了,而test后面的yx没有被过滤掉,是因为执行到testyx后面那个空格的时候就完成了.如果我们只需要留下'test',输入下面的语句即可:

>>> print x.strip('xy ')
test

相关说明:

lstrip(...)
    S.lstrip([chars]) -> string or unicode
   
    Return a copy of the string S with leading whitespace removed.
    If chars is given and not None, remove characters in chars instead.
    If chars is unicode, S will be converted to unicode before stripping

rstrip(...)
    S.rstrip([chars]) -> string or unicode
   
    Return a copy of the string S with trailing whitespace removed.
    If chars is given and not None, remove characters in chars instead.
    If chars is unicode, S will be converted to unicode before stripping

strip(...)
    S.strip([chars]) -> string or unicode
   
    Return a copy of the string S with leading and trailing
    whitespace removed.
    If chars is given and not None, remove characters in chars instead.
    If chars is unicode, S will be converted to unicode before stripping

标签:


This page is powered by Blogger. Isn't yours?