# 可爱的 Python: Python 之优雅与瑕疵

David Mertz, Ph. D. (mertz@gnosis.cx), 开发人员, Gnosis Software, Inc.

2007 年 4 月 29 日

 >>> map(type, (u1, s1, s2))[, , ]>>> u1 < s1True>>> s1 < s2True>>> u1 < s2UnicodeDecodeError: 'ascii' codec can't decode byte 0xf0 in position 0: ordinal not in range(128)>>> map(type, (n, j, u1))[, , ]>>> n < u1True>>> j < u1True>>> n < jTypeError: no ordering relation is defined for complex numbers

 >>> 2-3j < 'spam'True>>> 4+0j < decimal.Decimal('3.14')True>>> 4+0j < 5+0jTypeError: no ordering relation is defined for complex numbers

 list1.sort()list2.sort()list2_xtra = []list2_ndx = 0for it1 in list1: it2 = list2[list2_ndx] while it1 < it2: list2_ndx += 1 it2 = list2[list2_ndx] if it1 == it2: item_in_both(it1) elif it1 > it2: item_in_list1(it1) else: list2_xtra.appen(it2) for it2 in list2_xtra: item_in_list2(it2)

 ['x','y','z', 1],['x','y','z', 1j],['x','y','z', 1j, 1], # Adding an element makes it unsortable[0j, 1j, 2j], # An obvious "natural" order[0j, 1, 2],[0, 1, 2], # Notice that 0==0j --> True[chr(120), chr(240)],[chr(120), chr(240), 'x'],[chr(120), chr(240), u'x'], # Notice u'x'=='x' --> True[u'a', 'b', chr(240)],[chr(240), u'a', 'b'] # Same items, different initial order

 % python compare.py(0) ['x', 'y', 'z', 1] --> [1, 'x', 'y', 'z'](1) ['x', 'y', 'z', 1j] --> [1j, 'x', 'y', 'z'](2) ['x', 'y', 'z', 1j, 1] --> exceptions.TypeError(3) [0j, 1j, 2j] --> exceptions.TypeError(4) [0j, 1, 2] --> exceptions.TypeError(5) [0, 1, 2] --> [0, 1, 2](6) ['x', '/xf0'] --> ['x', '/xf0'](7) ['x', '/xf0', 'x'] --> ['x', 'x', '/xf0'](8) ['x', '/xf0', u'x'] --> exceptions.UnicodeDecodeError(9) [u'a', 'b', '/xf0'] --> [u'a', 'b', '/xf0'](10) ['/xf0', u'a', 'b'] --> exceptions.UnicodeDecodeError

 >>> set1 = set([1j, u'2', 3, 4.0])>>> set2 = set([4, 3, 2, 1])>>> set1 | set2set([3, 1, 2, 1j, 4.0, u'2'])>>> set1 & set2set([3, 4])

 >>> set2 & set1set([3, 4.0])>>> set([3, 4.0, 4, 4+0j])set([3, 4.0])

 def stablesort(o): # Use as: mylist.sort(key=stablesort) if type(o) is complex: return (type(o), o.real, o.imag) else: return (type(o), o)

Python 的发展大方向是以惰性方式构造类似于序列的对象；总体来说，这个方向是正确的。惰性的伪序列既可以节省内存空间，还可以提高操作的速度（尤其是在处理非常大的与序列类似的 “东西” 时）。

 >>> r = range(10)>>> i = iter(r)>>> x = xrange(10)>>> g = itertools.takewhile(lambda n: n<10, itertools.count())#...etc...

 >>> r[4]4>>> i[4]TypeError: unindexable object

 >>> thing, temp = itertools.tee(thing)>>> zip(temp, '.'*5)[-1][0]4

itertools.tee() 的预调用保留了原始迭代器。对于切片，可以按照特殊方式使用 itertools.islice() 函数。

 >>> r[4:9:2][4, 6, 8]>>> list(itertools.islice(r,4,9,2)) # works for iterators[4, 6, 8]

>>> class Indexable(object):...     def __init__(self, it):...         self.it = it...     def __getitem__(self, x):...         self.it, temp = itertools.tee(self.it)...         if type(x) is slice:...             return list(itertools.islice(self.it, x.start, x.stop, x.step))...         else:...             return zip(temp, range(x+1))[-1][0]...     def __iter__(self):...         self.it, temp = itertools.tee(self.it)...         return temp...>>> integers = Indexable(itertools.count())>>> integers[4]4>>> integers[4:9:2][4, 6, 8]

Python 要比 Java 或 Ruby 更加灵活，这个优点既值得称道，同时也为人们所诟病。如果您在 Python 中访问 foo.bar，或设置 foo.bar=value， 您可能使用了一个简单的数据值，或者调用了某些半隐藏的代码。此外，在后者中，至少有六种不同方法可以访问代码块，各种方法之间稍有不同，这些细微差别极 易混淆。过多的方法损害了 Python 的正则性，使非专家人员（甚至专家）难于理解。我知道为什么这些方法都自成体系：因为新的功能是分步添加到 Python 的面向对象基础中的。但是我并不觉得这种混乱有什么值得高兴的。

 >>> class Foo(object):... def __getattr__(self, name):... return "Value of %s" % name>>> foo = Foo()>>> foo.just_this = "Some value">>> foo.just_this'Some value'>>> foo.something_else'Value of something_else'

foo.just_this 的访问跳过了方法代码，而对 foo.something_else 的访问则运行了代码；除了这个 shell 会话较短以外，没什么特别明显的不同。事实上，是否运行了 hasattr()，答案很让人容易误解：

 >>> hasattr(foo,'never_mentioned')True>>> foo2.__dict__.has_key('never_mentioned') # this worksFalse>>> foo2.__dict__.has_key('just_this')True

slot 方法

 >>> class Foo2(object):... __slots__ = ('just_this')... def __getattr__(self, name):... return "Value of %s" % name>>> foo2 = Foo2()>>> foo2.just_this = "I'm slotted">>> foo2.just_this"I'm slotted">>> foo2.something_else = "I'm not slotted"AttributeError: 'Foo' object has no attribute 'something_else'>>> foo2.something_else'Value of something_else'

 >>> class Foo3(object):... __slots__ = ('x')... def __setattr__(self, name, val):... if name in Foo.__slots__:... object.__setattr__(self, name, val)... def __getattr__(self, name):... return "Value of %s" % name...>>> foo3 = Foo3()>>> foo3.x'Value of x'>>> foo3.x = 'x'>>> foo3.x'x'>>> foo3.y'Value of y'>>> foo3.y = 'y' # Doesn't do anything, but doesn't raise exception>>> foo3.y'Value of y'

.__getattribute__() 方法

 >>> class Foo4(object):... def __getattribute__(self, name):... try:... return object.__getattribute__(self, name)... except:... return "Value of %s" % name...>>> foo4 = Foo4()>>> foo4.x = 'x'>>> foo4.x'x'>>> foo4.y'Value of y'

 >>> class ErrWriter(object):... def __get__(self, obj, type=None):... print >> sys.stderr, "get", self, obj, type... return self.data... def __set__(self, obj, value):... print >> sys.stderr, "set", self, obj, value... self.data = value... def __delete__(self, obj):... print >> sys.stderr, "delete", self, obj... del self.data>>> class Foo(object):... this = ErrWriter()... that = ErrWriter()... other = 4>>> foo = Foo()>>> foo.this = 5set <__main__.ErrWriter object at 0x5cec90> <__main__.Foo object at 0x5cebf0> 5>>> print foo.thisget <__main__.ErrWriter object at 0x5cec90> <__main__.Foo object at 0x5cebf0> 5>>> print foo.other4>>> foo.other = 6>>> print foo.other6

Foo 类将 thisthat 定义为 ErrWriter 类的描述符。属性 other 只是一个普通的类属性。在第一次访问 foo.other 时，我们将读取类属性；对其赋值后，将读取实例属性。类属性仍然存在，只是被隐藏了，例如：

 >>> foo.other6>>> foo.__class__.other4

 >>> foo2 = Foo()>>> foo2.thisget <__main__.ErrWriter object at 0x5cec90> <__main__.Foo object at 0x5cebf0> 5

 class ErrWriter(object): def __init__(self): self.inst = {} def __get__(self, obj, type=None): return self.inst[obj] def __set__(self, obj, value): self.inst[obj] = value def __delete__(self, obj): del self.inst[obj]

 class FooP(object): def getX(self): return self.__x def setX(self, value): self.__x = value def delX(self): del self.__x x = property(getX, setX, delX, "I'm the 'x' property.")

getter、setter 和 deleter 的名字没什么特别的约束。通常，您希望使用类似上文的可感知的名字。实际上没什么具体作用，但对属性名使用两个下划线比较合理。这些属性将使用普通的 Python 名字（针对 “半隐藏” 属性做了修改）附加到实例。而且，方法仍然保持可用性：

>>> foop = FooP()>>> foop.x = 'FooP x'>>> foop.getX()'FooP x'>>> foop._FooP__x'FooP x'>>> foop.x'FooP x'