足球盘口软件
当前位置: 足球盘口软件 > 前端 >
属性和迭代器,学习笔记

魔法方法、属性和迭代器

在python中,有的名称会在前面和后面各加上两个下划线,这种写法很特别。它表示名字有特殊含义,所以绝不要在自己的程序中使用这种名字。在python中,由这些名字组成的集合所包含的方法叫做魔法(或称特殊)方法。如果对象实现了这些方法中的某一个,那么这个方法会在特殊的情况下被python调用,而几乎没有直接调用它们的必要。

构造方法

((Python基础教程))学习笔记 | 第09章 | 魔法方法、属性和迭代器

这一章,有点抽象,看着有点蛋疼!图片 1

双下划线__future__或单下划线有特殊含义,在Python中,这些名字的集合称为魔法方法:最重要的是__init__和一些处理访问对象的方法,这些方法允许你创建自己的序列或者是映射.


准备工作:

将__metaclass__=type放在模块的最开始位置,以确保类时最新式的。考虑下面两个类

 

class NewStyle(object):
    more_code_here
class OldStyle:
    more_code_here

如果文件以__metclass__=type开始,那么这个类都是新式类


构造方法:

和普通方法的不同在于:当一个对象被创建后,会立即调用构造方法。因此,之前章节的

>>>f = FooBar()
>>>f.init()

等同于

>>>f = FooBar()
>>> class FooBar:
def __init__(self):
self.var = 42


>>> f = FooBar()
>>> f.var
42

#如果有默认参数呢?

>>> class Foobar:
def __init__(self,value=42):
self.var = value

>>> f = Foobar()     #无参的话,使用默认值
>>> f.var
42
>>> f1 = Foobar(44)  #有参的话,使用新参数
>>> f1.var
44

在Python中__init__是使用最多的一个.

Python中有个魔法方法__del__,析构方法,她在对象要被垃圾回收之前调用.但是发生调用的具体时间是不可知的。

所以建立尽力避免使用__del__函数.


重写一般方法和特殊构造方法:

>>> class A:
...     def hello(self):
...         print 'Hello,World!'
...
>>> class B(A): pass
...
>>> class C(B): pass
...

#看看他们的工作机制

#c是C类的实例,当c调用hello(),首先找自己类有没有,没有话,去超类B中找,如果还没有话,

就去超类的超类去找。还没有的话,就报错。

>>> c = C()
>>> c.hello()
Hello,World!

如果重写B类,hello的方法呢?

>>> class A:
...     def hello(self):
...         print Hello,I am A!
...
>>> class B(A):
...     def hello(self):
...         print Hello,I am B!
...
>>> b = B()
>>> b.hello()
Hello,I am B!

重写是继承机制中的一个重要内容,对于构造方法尤其重要。构造方法用来初始化新创建对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。虽然重写的机制对于所有方法来说都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:如果一个类的构造方法被重写,那么就需要调用超类的构造方法,否则对象不会被正确初始化。看下面的例子

class Bird:
    def __init__(self):
        self.hungry = True
    def eat(self):
        if self.hungry:
            print Ahaha...
            self.hungry = False
        else:
            print No, Thanks!

该类定义鸟的基本功能吃,吃饱了就不再吃

输出结果:

>>> b = Bird()
>>> b.eat()
Ahaha...
>>> b.eat()
No, Thanks!
下面一个子类SingBird,
class SingBird(Bird):
    def __init__(self):
        self.sound = 'squawk'
    def sing(self):
        print self.sound

输出结果:

>>> s = SingBird()
>>> s.sing()
squawk

SingBird是Bird的子类,但如果调用Bird类的eat()方法时,

>>> s.eat()
Traceback (most recent call last):
  File , line 1, in 
    s.eat()
  File D:LearnPythonPerson.py, line 42, in eat
    if self.hungry:
AttributeError: SingBird instance has no attribute 'hungry'

代码错误很清晰,SingBird中初始化代码被重写,但没有任何初始化hungry的代码

 


调用未绑定的超类构造方法:

class SingBird(Bird):
    def __init__(self):
        Bird.__init__(self)    #增加这行代码就搞定
        self.sound = 'squawk'
    def sing(self):
        print self.sound
>>> sb = SingBird()
>>> sb.sing()
squawk
>>> sb.eat()
Ahaha...
>>> sb.eat()
No, Thanks!

使用super函数:

__metaclass__=type
class Bird:
    def __init__(self):
        self.hungry = True
    def eat(self):
        if self.hungry:
            print Ahaha...
            self.hungry = False
        else:
            print No, Thanks!
class SingBird(Bird):
    def __init__(self):
        super(SingBird,self).__init__()
        self.sound = 'squawk'
    def sing(self):
        print self.sound

Note:

  1. __metaclass__=type 必不可少,否则报错如下:

    sb = SingBird() Traceback (most recent call last): File , line 1, in sb = SingBird() File D:LearnPythonPerson.py, line 51, in init super(SingBird,self).init() TypeError: must be type, not classobj

  2. super(SingBird,self).__init__() 多了这么一句

输出结果:

>>> sb = SingBird()
>>> sb.sing()
squawk
>>> sb.eat()
Ahaha...
>>> sb.eat()
No, Thanks!

基本的序列和映射规则:

序列和映射是对象的集合,为了实现他们的基本行为,如果对象是不可变的,那么就需要两个魔法方法,如果对象时可变的,那么就需要四个魔法方法

__len__(self):返回集合中所含项目的数量

__getitem__(self,key):返回与所给的键对应的值

__setitem__(self,key,value):按一定的方法存储和key相关的value

__delitem__(self,key):删除对象相关的键

实践一下,创建一个无穷序列

def checkIndex(key):
    if not isinstance(key,(int,long)):
        raise TypeError
    if key<0:
        raise IndexError
class ArithmeticSequence:
    def __init__(self,start=0,step=1):
        self.start   = start
        self.step    = step
        self.changed = {}
    def __getitem__(self,key):
        checkIndex(key)
        try:
            return self.changed[key]
        except KeyError:
            return self.start + key*self.step

    def __setitem__(self,key,value):
        checkIndex(key)
        self.changed[key] = value

输出结果

>>> s[100]
201
>>> s = ArithmeticSequence(1,2)
>>> s[4]
9
>>> s[10]
21
>>> del s[4]
Traceback (most recent call last):
  File , line 1, in 
    del s[4]
AttributeError: ArithmeticSequence instance has no attribute '__delitem__'
>>> s['four']
Traceback (most recent call last):
  File , line 1, in 
    s['four']
  File D:LearnPythonPerson.py, line 71, in __getitem__
    checkIndex(key)
  File D:LearnPythonPerson.py, line 62, in checkIndex
    raise TypeError
TypeError
>>> s[-4]
Traceback (most recent call last):
  File , line 1, in 
    s[-4]
  File D:LearnPythonPerson.py, line 71, in __getitem__
    checkIndex(key)
  File D:LearnPythonPerson.py, line 64, in checkIndex
    raise IndexError
IndexError

子类化列表,字典和字符串

三个关于序列和映射规则(UserList,UserString,UserDict),如果希望实现一个和内建列表行为相似的序列,可以使用子类list,看看下面的例子,带有访问计数的列表

class CounterList(list):
    def __init__(self,*args):
        super(CounterList,self).__init__(*args)
        self.counter = 0
    def __getitem__(self,index):
        self.counter +=1
        return super(CounterList,self).__getitem__(index)

#下面是她如何使用的一些例子

>>> c = CounterList(range(10))
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> c.reverse()
>>> c
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> del c[3:6]
>>> c
[9, 8, 7, 3, 2, 1, 0]
>>> c.counter
0
>>> c[4]+c[2]
9
>>> c.counter
2

#其他跟list性能一样,但有个counter特性,每次执行加法后会自增。


属性:

class Rectangle:
    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
>>> r= Rectangle()
>>> r.width = 10
>>> r.height= 5
>>> r.getSize()
(10, 5)
>>> r.setSize((150,100))
>>> r.width
150

property函数:

__metaclass__=type
class Rectangle:
    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)

在这个新版的Rectangle中,property函数创建了一个属性,其中访问器函数被用做参数(先是取值,然后是赋值),这个属性命为size,这样一来,就不用担心是如何实现的,可以用同样的方式处理width,height和size.

>>> r = Rectangle()
>>> r.width = 10
>>> r.height= 20
>>> r.size
(10, 20)
>>> r.size = 100,200
>>> r.width
100

property函数可以用0,1,2,3或4个参数来调用。如果没有参数,产生的属性即不可读,也不可写。如果只使用一个参数调用,产生的属性是只读的第3个参数。名字分别叫:fget,fset,fdel,doc__


静态方法和类成员方法

静态方法和类成员方法分别在创建时被装入Staticmethod类型和Classmethod类型的对象中。

静态方法的定义没有self参数,且可能被类本身直接调用。

类方法在定义时需要名为cls的类似于self的参数,类成员方法可以直接用类的具体对象调用。但cls参数是自动被绑定类的。看下例子:

class MyClass:
    def smeth():
        print 'This is a stacie method'
    smeth = staticmethod(smeth)
    def cmeth(cls):
        print 'This is a class method of', cls
    cmeth = classmethod(cmeth)

#用@来替代

__metaclass__ = type
class MyClass:
    @staticmethod
    def smeth():
        print 'This is a stacie method'
    @classmethod
    def cmeth(cls):
        print 'This is a class method of', cls

#定义好了方法后,可以这样调用。

>>> MyClass.smeth()
This is a stacie method
>>> MyClass.cmeth()
This is a class method of 

__getattr__,__setattr__

为了访问特性的时候可以执行代码,必须使用一些魔法方法。下面四种方法提供了需要的功能。

__getattrbute__(self,name): 当特性name被访问时,自动被调用

__getattr__(self,name):当特性name被访问,且对象没有相应的特性时被自动调用

__setattr__(self,name,value):当试图给特性name赋值时会被自动调用

__delattr__(self,name): 当试图删除特性name时被自动调用。

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:
            self.__dict__[name] = value
    def __getattr__(self,name):
        if name == 'size':
            return self.width,self.height
        else:
            raise AttributeError

迭代器:

主要讨论特殊的方法:__iter__这个迭代器规则的基础.__iter__方法返回一个迭代器,所谓迭代器就是具有next方法的对象。如果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

首先是实现了__iter__方法,这个方法实际上返回迭代器本身。很多情况下,__iter__会放到for循环中使用的对象中.首先产生一个Fibs对象

>>> fibs = Fibs()

其次可在for循环中使用该对象-比如去查找在斐波那契数列中比1000大的数中的最小的数:

>>> for f in fibs:
if f>1000:
print f
break

1597

Note: 内建函数iter可以从可迭代的对象中获得迭代器.

>>> it = iter([1,2,3])
>>> it.next()
1
>>> it.next()
2

除此之外,它也可以从函数或者其他可调用对象中获取可迭代对象.


从迭代器中得到序列:

除了再迭代器和可迭代对象上进行迭代外,还能把它们转换为序列。在大部分能使用序列的情况下,能使用迭代器替换。一个很有用的例子是使用list构造方法显式地将迭代器转化为列表

class TestIter:
    value = 0
    def next(self):
        self.value +=1
        if self.value > 10 :
            raise StopIteration
        return self.value
    def __iter__(self):

输出结果:

>>> ti = TestIter()
>>> ti
<__main__.TestIter instance at 0x0000000002A81A08>
>>> list(ti)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

生成器: 是Python新引入的概念,由于历史原因,它也叫简单生成器。它和迭代器可能是近几年来引入的最强大的两个特性。生成器可以帮助程序员写出非常优雅的代码,当然编写任何程序也不可以不使用。生成器是一种用普通的函数语法定义的迭代器。


创建生成器:

nested = [[1,2],[3,4],[5]]如果像这么个例子,怎么样用将数字一一打印出来。

def flatten(nested):
    for sublist in nested:
        for element in sublist:
            yield element

#如何包含yield的语句,称为生成器

>>> nested = [[1],[2,3],[4,5,6]]
>>> flatten(nested)

>>> for num in flatten(nested):
        print num 
1
2
3
4
5
6

递归生成器:

上一个例子创建的生成器只能处理两层嵌套,使用了两个for循环。如果要处理任意层的嵌套怎么办?就应该更灵活,现在就到了递归生成器登场的时候了。

def flatten(nested):
    try:
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested

两种情况: 基本情况和需要递归的情况

 

1. 如果只是元素,函数被告知展开一个元素,这种情况下,for循环会引发一个TypeError异常,生成器会产生一个元素.

2. 如果是一个列表,那么就要进行特殊处理。程序必须遍历所有子列表,并对它们调用flatten,然后使用另一个for循环来产生被展开的子列表中的所有元素,很神奇吧。

>>> nested = [[[[1,2],3],4],5]
>>> list(flatten(nested))
[1, 2, 3, 4, 5]

如果是字符串对象,那么它就是一个序列,不会引发TypeError,如果你不想对这样的对象进行迭代。为了处理这种情况,则必须在生成器的开始处添加一个检查语句。试着将传入的对象和一个字符串拼接,看看会不会出现TypeError,这是检查一个对象是不是类似于字符串的最简单,最快速的方法。下面加入检查语句的生成器。

>>> nested=['a',[[[1,2],3],4],5]
>>> list(flatten(nested))
['a', 1, 2, 3, 4, 5]


通用生成器:

生成器是一个包含yield关键字的函数。当它被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。yield意味着应该生成一个值。return语句意味着生成器要停止执行。换句话说,生成器由两部分组成:生成器的函数和生成器的迭代器。生成器的函数是用def语句定义,包含yield部分,生成器的迭代器是这个函数返回的部分。

>>> def simple_generator():
yield 1

>>> simple_generator

>>> simple_generator()

>>> 

生成器方法:

>>> def repeater(value):
         while True:
             new = (yield value)
             if new is not None: value = new

输出结果:

>>> r = repeater(42)
>>> r.next()
42
>>> r.next()
42
>>> r.send('Hello,World!')
'Hello,World!'

#next()方法,send()方法,throw()方法,close()方法


模拟生成器:

如何使用普通的函数模拟生成器

首先将下面语句放在程序的开始处

result = []

然后将下面这种形式的代码:

yield some_expression

用下面的语句替换:

result.append(some_expression)

最后在函数的末尾,添加下面的语句:

return result

下面是flatten生成器用普通的函数重写的版本

def flatten(nested):
    result = []
    try:
        try: nested + ''
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                result.append(element)
    except TypeError:
        result.append(nested)
    return result

输出结果:

>>> n = [[[[[['HaHa...'],1],2],3],4],5]
>>> flatten(n)
['HaHa...', 1, 2, 3, 4, 5]

 

iter(obj) 从一个可迭代的对象得到迭代器

property(fget,fset,fdel,doc) 返回一个属性,所有参数都是可选的

super(class,obj) 返回一个类的超类的绑定实例

| 第09章 | 魔法方法、属性和迭代器 这一章,有点抽象,看着有点蛋疼! 双下划线__future__或单下划线有特殊含义...

准备工作

为了确保类是新型的,应该把赋值语句__metaclass__=type放在你的模块的最开始,或者(直接或间接)子类化内建类(实际上是类型)object(或其他一些新式类)。

 

构造方法

构造方法和其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法。

在python中创建一个构造方法很容易。只要把init方法的名字从简单的init修改为魔法版本__init__即可。

在python所有的魔法方法中,__init__是使用最多的一个。

在Python中创建一个构造方法很容易。只要把init方法的名字从简单的init修改为魔法版本__init__即可:

重写一般方法和特殊的构造方法

如果一个方法在B类的一个实例中被调用(或一个属性被访问),但在B类中没有找到该方法,那么会去它的超类A里面找。

>>> class A:

def hello(self):

print "Hello,I'm A."

>>> class B(A):

pass

>>> a=A()

>>> b=B()

>>> a.hello()

Hello,I'm A.

>>> b.hello()

Hello,I'm A.

在子类中增加功能的最基本方式是增加方法。也可以重写一些超类的方法来自定义继承的行为。

>>> class A:

def hello(self):

print "Hello,I'm A."

>>> class B(A):

def hello(self):

print "Hello,I'm B."

>>> a=A()

>>> b=B()

>>> a.hello()

Hello,I'm A.

>>> b.hello()

Hello,I'm B.

重写是继承机制中的一个重要内容,对于构造方法尤其重要。构造方法用来初始化新创建对象的状态,大部分子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。虽然重写的机制对于所有方法来说都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:

如果一个类的构造方法被重写,那么就需要调用超类的构造方法,否则对象可能不能被正确地初始化。

父类:

>>> class Bird:

def __init__(self):

self.hungry=True

def eat(self):

if self.hungry:

print "Aaaaah"

self.hungry=False

else:

print "No,thanks!"

>>> b=Bird()

>>> b.eat()

Aaaaah

>>> b.eat()

No,thanks!

子类:

>>> class SongBird(Bird):

def __init__(self):

self.sound='Squawk!'

def sing(self):

print self.sound

>>> sb=SongBird()

>>> sb.sing()

Squawk!

但如果调用eat方法,就会产生一个错误:

>>> sb.eat()

Traceback (most recent call last):

File "", line 1, in

sb.eat()

File "", line 5, in eat

if self.hungry:

AttributeError: SongBird instance has no attribute 'hungry'

原因:在SongBird中,构造方法被重写,但新的构造方法没有任何关于初始化hungry特性的代码。为了达到预期效果,SongBird的构造方法必须调用其超类Bird的构造方法来确保基本的初始化。

有两种方法可以达到这个目的:

调用超类方法的未绑定版本;

使用super函数;

>>> class FooBar:

...     def __init__(self):

...         self.somevar=42

...        

>>> f=FooBar()

>>> f.somevar

42

调用未绑定的超类构造方法

上一节提出的问题的解决方法:

>>> class SongBird(Bird):

def __init__(self):

Bird.__init__(self)

self.sound='Squawk!'

def sing(self):

print self.sound

>>> sb=SongBird()

>>> sb.eat()

Aaaaah

>>> sb.eat()

No,thanks!

在调用一个实例的方法时,该方法的self参数会被自动绑定到实例上(这称为绑定方法)。如果直接调用类的方法(比如Bird.__init__)就没有实例会被绑定。这样就可以自由地提供需要的self参数。这样的方法称为未绑定方法。

给构造方法传几个参数

使用super函数

如果不想坚守旧版python阵营的话,那么就应该使用super函数。它只能在新式类中使用,但不管怎样,都应该使用新式类。

当前的类和对象可以作为super函数的参数使用,调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法。那么就可以不用在SongBird的构造方法中使用Bird,而直接使用super(SongBird,self)。除此之外,__init__方法能以一个普通的绑定方法被调用。

对Bird例子的更新:

>>> __metaclass__=type

>>> class Bird:

def __init__(self):

self.hungry=True

def eat(self):

if self.hungry:

print "Aaaaah"

self.hungry=False

else:

print "No,thanks!"

>>> class SongBird(Bird):

def __init__(self):

super(SongBird,self).__init__()

self.sound='Squawk!'

def sing(self):

print self.sound

>>> sb=SongBird()

>>> sb.sing()

Squawk!

>>> sb.eat()

Aaaaah

>>> sb.eat()

No,thanks!

>>> class FooBar:

...     def __init__(self,value=42):

...         self.somevar=value

...        

>>> f=FooBar('this is a constructor argument')

>>> f.somevar

'this is a constructor argument'

成员访问

尽管__init__是目前为止提到的最重要的特殊方法,但还有一些其他的方法提供的作用也很重要。

基本的序列和映射的规则很简单,但如果要实现它们全部功能就需要实现很多魔法函数。

 

基本的序列和映射规则

序列和映射是对象的集合。为了实现它们基本的行为(规则),如果对象是不可变的,那么就需要使用两个魔法方法,如果是可变的,则需要使用4个。

__len__(self):这个方法应该返回集合中所含项目的数量。

对于序列来说,这是元素的个数;

对于映射来说,是键值对的数量。

如果__len__返回0(并且没有实现重写该行为的__nozero__),对象会被当作一个布尔变量中的假值(空的列表,元组,字符串和字典也一样)进行处理。

__getitem__(self.key):这个方法返回与所给键对于的值。

对于一个序列,键应该是一个0~n-1的整数,n是序列的长度;

对于映射来说,可以使用任何种类的键。

__setitem__(self,key,value):这个方法应该按一定的方式存储和key相关的value,该值随后可使用__getitem__来获取。当然,只能为可以修改的对象定义这个方法。

__delitem__(self,key):这个方法在对一部分对象使用del语句时被调用,同时必须删除和元素相关的键。这个方法也是为可修改的对象定义的(并不是删除全部的对象,而只删除一些需要移除的元素)。

重写一般方法和特殊的构造方法

属性

访问器是一个简单的方法,它能够使用getHeight、setHeight这样的名字来得到或重绑定一些特性。

python能隐藏访问器方法,让所有特性看起来一样,这些通过访问器定义的特性被称为属性。

 

property函数

实际上,property函数可以用0、1、2、3或者4个参数来调用。如果没有参数,产生的属性既不可读,也不可写。如果只使用一个参数调用(一个取值方法),产生的属性是只读的。第3个参数(可选)是一个用于删除特性的方法。第4个参数是一个文档字符串。property的4个参数分别被叫做fget、fset、fdel和doc。

实际上,property函数不是一个真正的函数——它是其实例拥有很多特殊方法的类。

构造方法用来初始化新创建对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。虽然重写的机制对于所有方法来说都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:如果一个类的构造方法被重写,那么就需要调用超类(你所继承的类)的构造方法,否则对象可能不会被正确地初始化。

静态方法和类成员方法

静态方法和类成员方法分别在创建时被装入Staticmethod类型和Classmethod类型的对象中。静态方法的定义没有self参数,且能够被类本身直接调用。

类方法在定义时需要名为cls的类似于self的参数,类成员方法可以直接用类的具体对象调用。但cls参数是自动被绑定到类的。

装饰器,使用@操作符,在方法(或函数)的上方将装饰器列出,从而指定一个或者更多的装饰器(多个装饰器在应用时的顺序与指定的顺序相反)。

静态方法和类成员方法在python中并不是向来都很重要,主要的原因是大部分情况下可以使用函数或绑定方法代替。

>>> class Bird:

...     def __init__(self):

...         self.hungry=True

...     def eat(self):

...         if self.hungry:

...             print 'aaah...'

...             self.hungry=False

...         else:

...             print 'no thanks'

...  

>>> b=Bird()

>>> b.eat()

aaah...

>>> b.eat()

no thanks

__getattr_、__setattr__和它的朋友们

拦截对象的所有特性访问是可能的,这样可以用旧式类实现属性。为了在访问特性的时候可以执行代码,必须使用一些魔法方法。

__getattribute__(self , name):当特性name被访问时自动被调用(只能在新式类中使用)。

__getattr__(self , name):当特性name被访问且对象没有相应的特性时被自动调用。

__setattr__(self , name , value):当试图给特性name赋值时会被自动调用。

__delattr__(self , name):当试图删除特性name时被自动调用。

__setattr__方法在所涉及到的特性不是size时会被调用。因此,这个方法必须把两方面都考虑进去:如果属性是size,那么就像前面那样执行操作,否则就要使用特殊方法__dict__,该特殊方法包含一个字典,字典里面是所有实例的属性。为了避免__setattr__方法被再次调用,__dict__方法被用来代替普通的特性赋值操作。

__getattr__方法只在普通的特性没有被找到的时候调用,这就是说如果给定的名字不是size,这个特性不存在,这个方法会引发一个AttributeError异常。如果希望类和hasattr或者getattr这样的内建函数一起正确地工作,__getattr__方法就很重要。如果使用的是size属性,那么就会使用在前面的实现中找到的表达式。

鸟吃过了以后,它就不再饥饿。现在考虑子类SongBird,它添加了唱歌的行为:

迭代器

>>> class SongBird(Bird):

...     def __init__(self):

...         self.sound='Squawk'

...     def sing(self):

...         print self.sound

...        

>>> s=SongBird()

>>> s.sing()

Squawk

迭代器规则

迭代的意思是重复做一些事很多次。到目前为止只是在for循环中对序列或字典进行迭代,但实际上也能对其他的对象进行迭代:实现__iter__方法的对象。

__iter__方法返回一个迭代器,所谓的迭代器就是具有next方法的对象。在调用next方法时,迭代器会返回它的下一个值。如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常。

迭代规则的关键是什么?为什么不使用列表?因为列表的杀伤力太大。如果有可以一个接一个地计算值的函数,那么在使用时可能是计算一个值时获得一个值——而不是通过列表一次性获取所有值。如果有很多值,列表就会占用太多的内存。但还有其他理由:使用迭代器更通用、更简单、更优雅。

因为SongBird是Bird的一个子类,它继承了eat方法,但如果调用eat方法,就会产生一个问题:

从迭代器得到序列

除了在迭代器和可迭代对象上进行迭代外,还能把它们转换为序列。在大部分能使用序列的情况下,能使用迭代器替换。

>>> s.eat()

Traceback (most recent call last):

 File "<input>", line 1, in <module>

 File "<input>", line 5, in eat

AttributeError: SongBird instance has no attribute 'hungry'

生成器

生成器可以帮助读者写出非常优雅的代码,当然,编写任何程序时不使用生成器也是可以的。

生成器是一种用普通的函数语法定义的迭代器。

错误:SongBird没有hungry特性。原因是这样的:在SongBird中,构造方法被重写,但新的构造方法没有任何关于初始化hungry特性的代码。为了达到预期的效果,SongBird的构造方法必须调用其超类Bird的构造方法来确保进行基本的初始化。有两种方法能达到这个目的:调用超类构造方法的未绑定版本,或者使用super函数。

创建生成器

任何包含yield语句的函数称为生成器。除了名字不同以外,它的行为和普通的函数也有很大的差别。这就在于它不是像return那样那样返回值,而是每次产生多个值。每次产生一个值(使用yield语句),函数就会被冻结:即函数停在那点等待被激活。函数被激活后就从停止的那点开始执行。

 

递归生成器

当需要处理任意层循环时,可以使用递归方式生成器;

调用未绑定的超类构造方法

通用生成器

生成器是一个包含yield关键字的函数。当它被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,直到遇到一个yield或者return语句。yield语句意味着应该生成一个值。return语句意味着生成器要停止执行(不再生成任何东西,return语句只有在一个生成器中使用时才能进行无参数调用)。

换句话说,生成器是由两部分组成:生成器的函数和生成器的迭代器。生成器的函数是用def语句定义的,包含yield的部分,生成器的迭代器是这个函数返回的部分。

生成器的函数返回的迭代器可以像其他的迭代器那样使用。

 

生成器方法

生成器的新属性是在开始运行后为生成器提供值的能力。

>>> class SongBird(Bird):

...     def __init__(self):

...         Bird.__init__(self)

...         self.sound='Squawk'

...     def sing(self):

...         print self.sound

...        

>>> s=SongBird()

>>> s.eat()

aaah...

>>> s.eat()

no thanks

>>> s.sing()

Squawk

模拟生成器

略;

在调用一个实例的方法时,该方法的self参数会被自动绑定到实例上(这称为绑定方法)。但如果直接调用类的方法(比如Bird.__init__),那么就没有实例会被绑定。这样就可以自由地提供需要的self参数。这样的方法称为未绑定(unbound)方法

八皇后问题

 

生成器和回溯

生成器是逐渐产生结果的复杂递归算法的理想实现工具。没有生成器的话,算法就需要一个额外参数传递的半成品方案,这样递归调用就可以在这个方案上建立起来。如果使用生成器,那么所有的递归调用只要创建自己的yield部分。

在一些应用程序中,答案必须做很多次选择才能得出。并且程序不只是在一个层面上而必须在递归的每个层面上做出选择。

使用super函数

问题

八皇后问题,略;

在python中,有的名称会在前面和后面各加上两个下划线,这种写法很特别。它表示名字有特殊含义,所以绝不要在...

 

当前的类和对象可以作为super函数的参数使用,调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法。

>>> __metaclass__=type

>>> class Bird:

...     def __init__(self):

...         self.hungry=True

...     def eat(self):

...         if self.hungry:

...             print 'aaah...'

...             self.hungry=False

...         else:

...             print 'no thanks'

...            

>>> class SongBird(Bird):

...     def __init__(self):

...         super(SongBird,self).__init__()

...         self.sound='Squawk'

...     def sing(self):

...         print self.sound

...  

这个新式的版本的运行结果和旧式版本的一样      

>>> s=SongBird()

>>> s.sing()

Squawk

>>> s.eat()

aaah...

>>> s.eat()

no thanks

 

基本的序列和映射规则

 

序列和映射是对象的集合。为了实现它们基本的行为(规则),如果对象是不可变的,那么就需要使用两个魔法方法,如果是可变的则需要使用4个。

__len__(self):这个方法应该返回集合中所含项目的数量。对于序列来说,这就是元素的个数。对于映射来说,则是键-值对的数量。

__getitem__(self,key):这个方法返回与所给键对应的值。对于一个序列,键应该是1个0~n-1的整数(或者像后面所说的负数),n是序列的长度;对于映射来说,可以使用任何种类的键。

__setitem__(self,key,value):这个方法应该按一定的方式存储和key相关的value,该值随后可使用__getitem__来获取。当然,只能为可以修改的对象定义这个方法。

__delitem__(self,key):这个方法在对一部分对象使用del语句时被调用,同时必须删除和元素相关的键。这个方法也是为可修改的对象定义的(并不是删除全部的对象,而只删除一些需要移除的元素)。

>>> def checkIndex(key):

...     if not isinstance(key,(int,long)):raise TypeError

...     if key<0:raise IndexError

...  

>>> class ArithmeticSequence:

...     def __init__(self,start=0,step=1):

...         self.start=start

...         self.step=step

...         self.changed={}

...     def __getitem__(self,key):

...         checkIndex(key)

...         try:return self.changed[key]

...         except KeyError:

...             return self.start+key*self.step

...     def __setitem__(self,key,value):

...         checkIndex(key)

...         self.changed[key]=value

...         

>>> s=ArithmeticSequence(1,2)

>>> s[4]

9

>>> s[4]=2

>>> s[4]

2

>>> s[5]

11

没有实现__del__方法的原因是希望删除元素是非法的:

>>> del s[4]

Traceback (most recent call last):

  File "<input>", line 1, in <module>

AttributeError: ArithmeticSequence instance has no attribute '__delitem__'

这个类没有__len__方法,因为它是无限长的。

如果使用了一个非法类型的索引,就会引发TypeError异常,如果索引的类型是正确的但超出了范围(在本例中为负数),则会引发IndexError异常:

>>> s[four]

Traceback (most recent call last):

  File "<input>", line 1, in <module>

NameError: name 'four' is not defined

>>> s[-4]

Traceback (most recent call last):

  File "<input>", line 1, in <module>

  File "<input>", line 7, in __getitem__

  File "<input>", line 3, in checkIndex

IndexError

子类化列表,字典和字符串

例子----带有访问计数的列表:

>>> class CounterList(list):

...     def __init__(self,*args):

...         super(CounterList,self).__init__(*args)

...         self.counter=0

...     def __getitem__(self,index):

...         self.counter +=1

...         return super(CounterList,self).__getitem__(index)

... 

CounterList类严重依赖于它的子类化超类(list)的行为CounterList类没有重写任何的方法(和append  extend, index一样)都能被直接使用。在两个被重写的方法中,super方法被用来调用相应的超类的方法,只在__init__中添加了所需的初始化counter特性的行为,并在__getitem__中更新了counter特性。

>>> c1=CounterList(range(10))

>>> c1

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> c1.reverse()

>>> c1

[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

>>> del c1[3:6]

>>> c1

[9, 8, 7, 3, 2, 1, 0]

>>> c1[4]+c1[2]

9

>>> c1.counter

2

CounterList在很多方面和列表的作用一样,但它有一个counter特性(被初始化为0),每次列表元素被访问时,它都会自增,所以在执行加法c1[4]+c1[2〕后,这个值自增两次,变为2.

图片 2

上一篇:命令详解,linux性能监控之free 下一篇:没有了
返回顶部