流畅python学习笔记:第十章:序列的修改,散列和切片

前面在介绍了类的很多内置方法,比如__add__,__eq__,这里继续介绍类的两个内置方法,这2个内置方法可以将一个类实例变成一个序列的形式。代码如下
class vector(object):
    def __init__(self,components):
        self._components=components
        print self._components
    def __len__(self):
        return len(self._components)
    def __getitem__(self,position):
        return self._components[position]

if __name__=="__main__":
    v=vector([1,2,3])
    print len(v)
    print v[:3]
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter10.py
[1, 2, 3]
3
[1, 2, 3]
首先初始化的时候传入一个列表并赋值给_components。然后再调用len(v)的时候会自动调用__len__,返回_components的长度。调用v[1]或者v[0:3]的时候会调用__getitem__的方法,返回对应的_components的数据。
这里比较有意思的是,我们在__getitem__中我们返回的是self._components[position],这是代表position这个位置的元素。但是如果传递的是v[:3]的方式,也就是切片的方式,依然能够返回切片的结果。我们把代码修改下:
class vector(object):
    def __init__(self,components):
        self._components=components
        print self._components
    def __len__(self):
        return len(self._components)
    def __getitem__(self,position):
        print type(position)
        return position

if __name__=="__main__":
    v=vector([1,2,3])
    print len(v)
    print v[:3]
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter10.py
[1, 2, 3]
3
<type ‘slice‘>
slice(None, 3, None)
可以看到position的类型是slice,而且返回的是slice(None,3,None).那么slice是什么类型呢
Slice其实是个切片的对象。我们来看下用法
s=slice(0,2,1)
print a[s]
运行结果
[1, 2]
在这里slice(0,2,1)的原型是slice[start,stop,step].也就是说slice[0,2,1]其实是等于a[0:2]。看到这里我们就明白了。由于position是一个slice对象,且这个slice对象为slice(None, 3, None)因此结果也就等于self. _components[:3]
 
__getattr__和__setattr__方法:
首先来看下vector代码:
class vector(object):
    shortcut_names="xyzt"
    def __init__(self,components):
        self._components=components
    def __len__(self):
        return len(self._components)
    def __getitem__(self,position):
        print type(position)
        return position
    def __str__(self):
        return "vector"
    def __getattr__(self, name):
        cls=type(self)
        print cls
        if len(name) == 1:
            pos=cls.shortcut_names.find(name)
            if 0<=pos<=len(self._components):
                return self._components[pos]

if __name__=="__main__":
    v=vector(range(1,10))
    print "before add attribute %s" % v.__dict__   ⑴
    print v.x           ⑵
    v.x=10              ⑶
    print v.x
    print "after add attribute %s" % v.__dict__    :⑷
 
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter10.py
before add attribute {‘_components‘: [1, 2, 3, 4, 5, 6, 7, 8, 9]}
<class ‘__main__.vector‘> 
1 
10
after add attribute {‘_components‘: [1, 2, 3, 4, 5, 6, 7, 8, 9], ‘x‘: 10}
 
首先在第一步的时候,v的属性只有一个列表。在第二步的时候print v.x。此时会检测v实例中是否有x属性,如果没有则会到类v.__class__中去寻找,如果仍然没找到,则会调用__getattr__方法。在__getattr__方法中,首先找到x在shortcut_names中的索引位置,然后根据这个索引找到_components中对应的数字。
在第三步的时候,我们设置了v.x=10.然后再打印v.x的时候,此时由于v.x=10导致v实例添加了x属性,因此不会调用__getattr__方法。查看v.__dict__也可以看到属性中新增了x属性。但这会导致一个问题,我们的目的是想通过x的索引位置得出_components对应位置的值,但是由于中途改变了x的值,导致我们在继续调用v.x的时候达不到我们的目的。因此有必要将这些在shortcut_names出现的值设置为可读。代码修改如下:
class vector(object):
    shortcut_names="xyzt"
    def __init__(self,components):
        self._components=components
        print self._components
    def __len__(self):
        return len(self._components)
    def __str__(self):
        return "vector"
    def __getattr__(self, name):
        cls=type(self)
        if len(name) == 1:
            pos=cls.shortcut_names.find(name)
            if 0<=pos<=len(self._components):
                return self._components[pos]
    def __setattr__(self, name, value):
        cls=type(self)
        if len(name) == 1:
            if name in cls.shortcut_names:
                error="readonly attributes {attr_name!r}"
            elif name.islower():
                error="can‘t set attributes ‘a‘ to ‘z‘ in {cls_name!r}"
            else:
                error=‘‘
            if error:
                msg=error.format(cls_name=cls.__name__,attr_name=name)
                raise AttributeError(msg)
        super(vector, self).__setattr__(name,value)

if __name__=="__main__":
    v=vector(range(1,10))
    print v.x
    v.x=10
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter10.py
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Traceback (most recent call last):
1
  File "E:/py_prj/fluent_python/chapter10.py", line 39, in <module>
    v.x=10
  File "E:/py_prj/fluent_python/chapter10.py", line 31, in __setattr__
    raise AttributeError(msg)
AttributeError: readonly attributes ‘x‘
新增__setattr__方法,当对属性进行赋值的时候,首先会调用__setattr__方法。在方法中对属性进行判断和保护。从执行结果来看,当执行v.x=10的时候。产生错误AttributeError: readonly attributes ‘x‘。这样就可以避免对x进行赋值了 
 
最后来看一个__hash__的用法。还是以之前vector的例子,我们想要要用异或运算符依次计算各个分量的散列值。比如v[0]^v[1]^v[2].来看下代码实现:
class vector(object):
    shortcut_names="xyzt"
    def __init__(self,components):
        self._components=components
    def __len__(self):
        return len(self._components)
    def __str__(self):
        return "vector"
    def __getattr__(self, name):
        cls=type(self)
        if len(name) == 1:
            pos=cls.shortcut_names.find(name)
            if 0<=pos<=len(self._components):
                return self._components[pos]
    def __setattr__(self, name, value):
        cls=type(self)
        if len(name) == 1:
            if name in cls.shortcut_names:
                error="readonly attributes {attr_name!r}"
            elif name.islower():
                error="can‘t set attributes ‘a‘ to ‘z‘ in {cls_name!r}"
            else:
                error=‘‘
            if error:
                msg=error.format(cls_name=cls.__name__,attr_name=name)
                raise AttributeError(msg)
        super(vector, self).__setattr__(name,value)
    def __hash__(self):
        hashes=(hash(x) for x in self._components)
        print hashes
        return reduce(operator.add,hashes)

if __name__=="__main__":
    v=vector(range(1,10))
    print hash(v)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter10.py
<generator object <genexpr> at 0x017B8148>
1
新增了__hash__方法。首先hashes得到各个向量值的hash值。然后通过reduce运算得到最终的异或值
这里简单介绍下reduce函数:
def add(x,y):
    return x+y
a=[1,2,3,4,5]
print reduce(add,a)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter10.py
15
Reduce函数有2个参数值,第一个是执行函数,第二个是作用对象。例如reduce(fn,lst),fn首先会应用到第一对元素上,fn(lst[0],lst[1])生成一个结果r 然后fn会应用到r和下一个元素上,fn(r,lst[2]).依次按照这个方式进行调用,直到最后一个元素
时间: 2024-12-07 07:01:15

流畅python学习笔记:第十章:序列的修改,散列和切片的相关文章

流畅python学习笔记:第十四章:迭代器和生成器

迭代器和生成器是python中的重要特性,本章作者花了很大的篇幅来介绍迭代器和生成器的用法. 首先来看一个单词序列的例子: import re re_word=re.compile(r'\w+') class Sentence(object):     def __init__(self,text):         self.text=text         self.word=re_word.findall(text)     def __getitem__(self, item):   

流畅python学习笔记:第十三章:重载运算符__add__,__iadd__,__radd__,__mul__,__rmul__,__neg__,__eq__,__invert__,__pos__

在前面第十章以及第一章的时候介绍了Vector对象的运算符重载.第十三章专门介绍运算符重载.这里我们看几个之前没讲过的运算符__neg__,__pos__,__invert__ class Vector(object):     def __init__(self,x):         self.x=x     def __neg__(self):         return "Vector(%d)" % (-self.x)     def __str__(self):      

python学习笔记19(序列的方法)

序列包含有宝值 表(tuple)和表(list).此外,字符串(string)是一种特殊的定值表,表的元素可以更改,定值表一旦建立,其元素不可更改. 任何的序列都可以引用其中的元素(item). 下面的内建函数(built-in function)可用于序列(表,定值表,字符串): # s为一个序列 len(s)          返回: 序列中包含元素的个数 min(s)         返回: 序列中最小的元素 max(s)        返回: 序列中最大的元素 all(s)       

Python学习笔记之-sequence序列

1:sequence(序列)是一组有顺序的元素的集合 : (严格的说,是对象的集合,但鉴于我们还没有引入"对象"概念,暂时说元素) 序列可以包含一个或多个元素,也可以没有任何元素. 我们之前所说的基本数据类型,都可以作为序列的元素.元素还可以是另一个序列,以及我们以后要介绍的其他对象. 序列有两种:tuple(定值表: 也有翻译为元组) 和 list (表) >>>s1 = (2, 1.3, 'love', 5.6, 9, 12, False)         # s

流畅python学习笔记:第十九章:动态属性和特性

首先来看一个json文件的读取.书中给出了一个json样例.该json文件有700多K,数据量充足,适合本章的例子.文件的具体内容可以在http://www.oreilly.com/pub/sc/osconfeed上查看.首先先下载数据生成json文件. def load():     url='http://www.oreilly.com/pub/sc/osconfeed'     JSON="osconfeed.json"     if not os.path.exists(JSO

流畅python学习笔记:第十一章:抽象基类

__getitem__实现可迭代对象.要将一个对象变成一个可迭代的对象,通常都要实现__iter__.但是如果没有__iter__的话,实现了__getitem__也可以实现迭代.我们还是用第一章扑克牌的例子来看下 class FrenchDeck:     ranks=[str(n) for n in range(2,11)] + list('JQKA')     suits='spades diamonds clubs hearts'.split()     def __init__(sel

流畅python学习笔记:第十二章:子类化内置类型

子类化内置类型 在python2.2之后,内置类型都可以子类化,但是有一个注意事项:内置类型不会调用用户定义的类覆盖的特殊方法.这个说起来比较绕口,什么意思呢.我们来看下下面的代码: class DopperDict(dict):     def __setitem__(self, key, value):         super(DopperDict,self).__setitem__(key,[value]*2) ⑴ if __name__=="__main__":     d

流畅python学习笔记第十八章:使用asyncio包处理并发(一)

首先是线程与协程的对比.在文中作者通过一个实例分别采用线程实现和asynchio包实现来比较两者的差别.在多线程的样例中,会用到join的方法,下面来介绍下join方法的使用. 知识点一:当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行流的最小单元,当设置多线程时,主线程会创建多个子线程,在python中,默认情况下(其实就是setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束,例子如下:. def run():

python 学习笔记12(序列一些方法总结)

1.  字符串判断 str.isalnum()        返回:True, 都是字母或数字 str.isalpha()        返回:True,  都是字母 str.isdigit()        返回:True,   都是数字 str.istitle()        返回:True,    首字母都是大写 str.isspace()        返回:True,  都是空格 str.islower()        返回:True,   都是小写字母 str.isupper()