zoukankan      html  css  js  c++  java
  • 流畅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.11python.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.11python.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.11python.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.11python.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.11python.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.11python.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]).依次按照这个方式进行调用,直到最后一个元素
  • 相关阅读:
    List装form
    《Java设计模式》之调停者模式(Mediator)
    android 4.0 禁用系统home键
    最大权二分匹配
    hdu 3667 /2010哈尔滨赛区H题 费用与流量为非线性关系/费用流
    【python】filter()
    【python】linux将python改为默认3.4版本
    【linux】VMware12.0安装
    【python】lxml-The E-factory
    【xml】python的lxml库使用
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/7189842.html
Copyright © 2011-2022 走看看