您的位置:首页 > 编程语言 > Python开发

《Beginning Python From Novice to Professional》学习笔记十二:__Magic__

2009-05-20 16:59 501 查看
1.属性访问
class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self, size):
        self.width, self.height = size   #我的理解是自动组成Tuple
    def getSize(self):
        return self.width, self.height   #同上

这种技术语法上不错,但它将程序与一种accessor紧耦合了。
函数property能够解决这个问题:
class Rectangle(object):
    def __init__(self):
        self.width = 0
        self.height = 0
    def setSize(self, size):
        self.width, self.height = size
    def getSize(self):
        return self.width, self.height
    size = property(getSize, setSize)   # getter在前,setter在后

原理:其实property是个满足descriptor协议的类(这样的类实现了__get__, __set__和__delete__方法)
下面测试accessor:
r = Rectangle()
r.width = 10
r.height = 5
r.size ---> (10, 5)
r.size = 150, 100
r.width ---> 150

实际上property函数也可以使用0个、1个、3个、4个参数。
0个 —— resulting property 既不可读也不可写
1个 —— resulting property 仅仅可读
3个 —— 第3个参数是用来删除某个属性的方法(此方法无参数)
4个 —— 第4个参数是documentation string
以上4个参数的名称为 fget, fset, fdel以及doc。
property(fget, fset, fdel, doc)

作者最后说,The moral is this: With new-style classes, you should use property rather than accessors.

2. 静态方法和类方法
静态方法是没有self参数的方法,而且可以由类直接调用。
类方法是使用了一个类似于self的参数cls。

以下是一个小例子:
__metaclass__ = type   #申明使用new-style类
class MyClass:
    def smeth():
        print 'This is a static method'
    smeth = staticmethod(smeth)
    def cmeth(cls):
        print 'This is a class method of', cls
    cmeth = classmethod(cmeth)

在Python 2.4中, 引入了一个新的语法:decorators
__metaclass__ = type
class MyClass:
    @staticmethod
    def smeth():
        print 'This is a static method'
    @classmethod
    def cmeth(cls):
        print 'This is a class method of', cls

以下为调用:
MyClass.smeth()
---> This is a static method
MyClass.cmeth()
---> This is a class method of <class '__main__.MyClass'>
静态方法和类方法之所以在Python中不太重要,是因为它未能在早期版本中实现。但在像factory函数中,它还是非常有用的。

3.__getattr__, __setattr__, and Friends
__getattribute__(self, name): 当属性name被访问时,被自动调用(Works correctly on new-style classes only.) #Note:TODO
__getattr__(self, name):当属性name被访问时,被自动调用
__setattr__(self, name, value):当属性name要被赋值value时,被自动调用
__delattr__(self, name): 当属性name要被删除时,被自动调用
这些magic方法在处理多个属性的时候是很有用的,但是If you have a choice, though, stick with property.
一个使用例子:
class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):
        if name == 'size':
            self.width, self.height = value
        else:
#将成员变量映射为Dictionary中的一个Pair,也是magic attribute,避免了无限调用__setattr__
            self.__dict__[name] = value   
    def __getattr__(self, name):
        if name == 'size':
            return self.width, self.height
        else:
            raise AttributeError   #如果想使你的类能够与内置函数hasattr和getattr配合使用正确,则这里很重要

4.迭代器Iterators
实现了__iter__方法的对象就可以和Sequences和Dictionaries一样在for循环中迭代。
__iter__方法返回一个迭代器,这是一个拥有next方法的任意对象,可调用但无参数。如果调用迭代器时已经没有了返回值,但会引发一个StopIteration异常。

!下面是一个可迭代的类示例:
class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    def next(self):
        self.a, self.b = self.b, self.a+self.b
        return self.a
    def __iter__(self):
        return self

下面在for循环中使用这个迭代:
fibs = Fibs()
for f in fibs:
        if f > 1000:
            print f
            break

#内置函数iter可以从一个可迭代对象中获得迭代器iter(obj) Extracts an iterator from an iterable object.

除了使用迭代器遍历对象,还可以将其转换成Sequence:
class TestIterator:
        value = 0
        def next(self):
            self.value += 1
            if self.value > 10: raise StopIteration
            return self.value
        def __iter__(self):
            return self

...
ti = TestIterator()
list(ti) ---> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

5.Generators(本质也是一种迭代)
#书中后面给出了定义:A generator is a function that contains the keyword yield. When called, the generator function returns a generator, which is a special type of iterator.

Making a Generator:
nested = [[1, 2], [3, 4], [5]]   #一个嵌套List
def flatten(nested):
    for sublist in nested:
        for element in sublist:
            yield element   #凡是包含yield的函数都称为generator,意味着你一次可以“收获”多个变量,而非普通函数一次只能返回一个变量

或者
list(flatten(nested))
---> [1, 2, 3, 4, 5]

递归Generator:(可以用来表示某种树结构)
def flatten(nested):
    try:
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:   #已经不可迭代,通常是已经到达树叶
        yield nested

更简洁的调用方式:
list(flatten([[[1],2],3,4,[5,[6,7]],8]))
---> [1, 2, 3, 4, 5, 6, 7, 8]

以上递归方式有一个隐患,当迭代string-like对象时,因为它是一个sequence,所以不会引起TypeError异常。(不过不应该迭代String-like对象),下面是一种更为安全的定义方式:
def flatten(nested):
    try:
        # Don't iterate over string-like objects:
        try: nested + ''   #通过看是否能够与一个string合并,看是否是一个String-like对象
        except TypeError: pass   #是string-like对象,跳过余下程序
        else: raise TypeError   #不是string-like对象,则抛出一个TypeError让外层来捕获并yield
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested

使用示例:
list(flatten(['foo', ['bar', ['baz']]]))
---> ['foo', 'bar', 'baz']
generators包含两总分:generator函数和generator迭代器。generator函数是包含yield的函数; generator迭代器是generator函数的返回值,和其它迭代器一样使用。

def simple_generator():
yield 1
...
simple_generator
---> <function simple_generator at 153b44> #这里被看成一个函数
simple_generator()
---> <generator object at 1510b0> #加括号则看成一个generator
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: