zoukankan      html  css  js  c++  java
  • 30.内置方法下

    六 property

     

    调用property()是构建数据描述符的一种简捷方式,可以在访问属性时触发函数调用。它的特征是

    property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

     属性是一种有用的特殊类型的描述符。它们是用来处理所有对实例属性的访问,其工作方式和我们前面说过的描述符相似。

    通过用 property() 就可以写一个和属性有关的函数来处理实例属性的获取(getting),赋值(setting),和删除(deleting)操作

    property()的一般用法是,将它写在一个类定义中,property()接受一些传进来的函数(其实是方法)作为参数。实际上,property()是在它所在的类被创建时被调用的,这些传进来的(作为参数的)方法是非绑定的,所以这些方法其实就是函数

     

    属性的定义有两种方式:

        装饰器 即:在方法上应用装饰器
        类属性 即:在类中定义值为property对象的类属性

    属性的定义和调用要注意一下几点:


    经典类:
    定义时,在普通方法(即类下面定义的函数)的基础上添加 @property 装饰器; 定义时,属性仅有一个self参数

    调用时,无需括号:经典类和新式类都一样
    方法:foo_obj.func()
    属性:foo_obj.prop

     

    1.装饰器方式:在类的普通方法上应用@property装饰器
    我们知道Python中的类有经典类和新式类,新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 )

    经典类,具有一种@property装饰器

     
    #!/usr/bin/env python
    #coding:utf-8
    
     ############### 定义 ###############
    class Goods:
    
        @property
        def price(self):
            return "test"
    # ############### 调用 ###############
    obj = Goods()
    result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
    print result

    执行结果:

    test

    新式类,具有三种@property装饰器

    #!/usr/bin/env python
    #coding:utf-8
    
    # ############### 定义 ###############
    class Goods(object):
    
        @property
        def price(self):
            print '@property'
    
        @price.setter
        def price(self, value):
            print '@price.setter'
    
        @price.deleter
        def price(self):
            print '@price.deleter'
    
    # ############### 调用 ###############
    obj = Goods()
    
    obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
    
    obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数
    
    del obj.price      # 自动执行 @price.deleter 修饰的 price 方法

    执行结果:

    @property
    @price.setter
    @price.deleter

    注:

    经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
    新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法

    由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

    #!/usr/bin/env python
    #coding:utf-8
    
    class Goods(object):
    
        def __init__(self):
            # 原价
            self.original_price = 100
            # 折扣
            self.discount = 0.8
    
        @property
        def price(self):
            # 实际价格 = 原价 * 折扣
            new_price = self.original_price * self.discount
            return new_price
    
        @price.setter
        def price(self, value):
            self.original_price = value
    
        @price.deleter
        def price(self, value):
            del self.original_price
    
    obj = Goods()
    print obj.price         # 获取商品价格
    obj.price = 200   # 修改商品原价
    del obj.original_price     # 删除商品原价

    执行结果:

    80.0

    2.类属性方式,创建值为property对象的类属性

    当使用类属性的方式创建属性时,经典类和新式类无区别

    #!/usr/bin/env python
    #coding:utf-8
    
    class Foo(object):
    
        def get_bar(self):
            return 'test1'
    
        BAR = property(get_bar)
    
    obj = Foo()
    reuslt = obj.BAR        # 自动调用get_bar方法,并获取方法的返回值
    print reuslt

    执行结果:

    test1

    property的构造方法中有个四个参数:

    property(fget=None, fset=None, fdel=None, doc=None) -> property attribute


    1.第一个参数是方法名,调用 对象.属性 时自动触发执行方法
    2.第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
    3. 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
    4. 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息

    #!/usr/bin/env python
    #coding:utf-8
    
    class Foo():
    
        def get_bar(self):
            return 'test'
    
        # *必须两个参数
        def set_bar(self, value):
            return 'set value' + value
    
        def del_bar(self):
            return 'test'
    
        BAR = property(get_bar, set_bar, del_bar, 'description...')  #内置property三个参数与get,set,delete一一对应
    
    obj = Foo()
    
    print obj.BAR             # 自动调用第一个参数中定义的方法:get_bar
    obj.BAR = "abc"     # 自动调用第二个参数中定义的方法:set_bar方法,并将“abc”当作参数传入
    del Foo.BAR          # 自动调用第三个参数中定义的方法:del_bar方法
    print obj.BAR     # 自动获取第四个参数中设置的值:description...

    执行结果:

    test
    abc

    注意:属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

    实例:对于主机列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据(即:limit m,n),这个分页的功能包括:

    1.根据用户请求的当前页和总数据条数计算出 m 和 n

    2.根据m 和 n 去数据库中请求数据

    #!/usr/bin/env python
    #coding:utf-8
    
    # ############### 定义 ###############
    class Pager:
    
        def __init__(self, current_page):
            # 用户当前请求的页码(第一页、第二页...)
            self.current_page = current_page
            # 每页默认显示10条数据
            self.per_items = 10
    
    
        @property
        def start(self):
            val = (self.current_page - 1) * self.per_items
            return val
    
        @property
        def end(self):
            val = self.current_page * self.per_items
            return val
    
    # ############### 调用 ###############
    
    p = Pager(1)
    print p.start #就是起始值,即:m
    print p.end   #就是结束值,即:n

    执行结果:

    0
    10

    案例二

    第一关:

    #!/usr/bin/env python
    #coding:utf-8
    
    
    class People(object):
        def __init__(self, name):
            self.name = name
    
        @property
        def name(self):
            return self.name
    #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,
    # 所以你这样写会触发property内置的set,抛出异常
    p1=People('alex')

    #第二关:修订版

    #!/usr/bin/env python
    #coding:utf-8
    
    
    class People(object):
        def __init__(self, name):
            self.name = name  # 实例化就触发property
    
        @property
        def name(self):
            # return self.name #无限递归
            print('get------>')
            return self.DouNiWan
    
        @name.setter
        def name(self, value):
            print('set------>')
            self.DouNiWan = value
    
        @name.deleter
        def name(self):
            print('delete------>')
            del self.DouNiWan
    
    
    p1 = People('alex')  # self.name实际是存放到self.DouNiWan里
    print(p1.name)
    print(p1.name)
    print(p1.name)
    print(p1.__dict__)
    
    p1.name = 'egon'
    print(p1.__dict__)
    
    del p1.name
    print(p1.__dict__)

    #第三关:加上类型检查

    #!/usr/bin/env python
    #coding:utf-8
    
    
    class People(object):
        def __init__(self, name):
            self.name = name  # 实例化就触发property
    
        @property
        def name(self):
            # return self.name #无限递归
            print('get------>')
            return self.DouNiWan
    
        @name.setter
        def name(self, value):
            print('set------>')
            if not isinstance(value, str):
                raise TypeError('必须是字符串类型')
            self.DouNiWan = value
    
        @name.deleter
        def name(self):
            print('delete------>')
            del self.DouNiWan
    
    
    p1 = People('alex')  # self.name实际是存放到self.DouNiWan里
    p1.name = 1

    七 __setitem__,__getitem__,__delitem__

    魔术方法的作用:

    __getitem__(self,key):返回键对应的值。obj['属性'] 时触发
    
    __setitem__(self,key,value):设置给定键的值;obj['属性']=属性的值 时触发
    
    __delitem__(self,key):删除给定键对应的元素。del obj['属性'] 时触发

    例子1:

     
    #!/usr/bin/env python
    #coding:utf8
    
    
    class Foo(object):
        def __getitem__(self, item):
            print "__getitem__",item
    
        def __setitem__(self, key, value):
            print "__setitem__",key,value
    
        def __delitem__(self, key):
            print "__delitem__",key
    
    obj = Foo()
    result = obj['k1']      # 自动触发执行 __getitem__
    obj['k2'] = 'test'   # 自动触发执行 __setitem__
    del obj['k1']           # 自动触发执行 __delitem__
     

    例子2:

    #!/usr/bin/env python
    #coding:utf-8
    
    class Foo(object):
        def __init__(self,name):
            self.name = name
        def __getitem__(self, item):
            print('__getitem__执行,%s'%item)
    
        def __setitem__(self, key, value):
            print('__setitem__执行')
            self.__dict__[key] = value      #执行添加对象的属性字典
    
        def __delitem__(self, key):
            print('__delitem__执行')
            self.__dict__.pop(key)          #删除对象
    
    
    
    
    f1 = Foo('aa')  #实例化对象
    f1['aaa']='999'
    f1['age']       #通过字典的方式查询,会触发__getitem__的运行
    f1.aaa
    f1.name = 'zhaok'   #添加实例属性,以.的方式去设置属性,并不会触发__setitem__的执行
    f1.age = 18
    f1.sex = ''
    f1['sex'] = ''     #以字典的方式去访问,会触发item的执行,以点的方式访问会执行attr类型的函数
    print(f1.__dict__)

     在Python中,如果我们想实现创建类似于序列和映射的类,可以通过重写魔法方法__getitem__、__setitem__、__delitem__、__len__方法去模拟。

    mylist.py内容:改写list类,让其下标从1开始

    #!/usr/bin/env python
    #coding:utf-8class MyList(list):  # 自定义类MyList,MyList继承list,所以Mylist也是列表
    
        def __getitem__(self, offset):  # 默认情况下,类list的getitem方法下表都是从0开始的
    
        # 返回父类list的getitem__(self,offset-1) #返回父类list的getitem方法并且把下标减一;offset -1表示下标减一
            return list.__getitem__(self, offset - 1)
    
    if __name__ == "__main__":
        alist = MyList([10, 20])
        print alist[1]  # 其实就是返回列表的offset -1项,即1-1=0返回列表的第0项;所以返回的是10
        alist.append(30)
        print alist
        print alist[0]  # 0-1=-1,就是最后一项

    执行结果:

    10
    [10, 20, 30]
    30

    八 __str__,__repr__,__format__

     _str__:控制返回值,并且返回值必须是str类型,否则报错

    __repr__:控制返回值并且返回值必须是str类型,否则报错

    __repr__是__str__的替代品,如果str存在,直接按str的返回值,返回信息,不会执行repr,如果不存在,会执行repr,并得到repr的返回值

    #!/usr/bin/env python
    #coding:utf-8
    
    class Foo:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        # def __str__(self):      #返回值必须是字符串
        #     return 'str--->名字是%s 年龄是%s'%(self.name,self.age)
    
        def __repr__(self):     #返回值必须是字符串
            return 'repr--->名字是%s 年龄是%s' % (self.name, self.age)
    
    f1 = Foo('zhaok',18)
    print(f1)   #系统:原本打印应该是一个<__main__.Foo object at 0x000000131E2CE6D8> 对象
                #自己定义__str__后,返回的是自己定义的返回值  名字是zhaok 年龄是18
                #print(f1)--> str(f1) ---> f1.__str__()     解析过程
    '''
    f1.name = 'zhaok' #做的就是往属性字典里添加或覆盖值,原理:--->setattr----->f1.__dict__['name'] = 'egon'
    f1['name'] = 'zhaok'                                    --->setitem----->f1.__dict__['name'] = 'egon'
    两个方法的底层都是一样的,只不过通过setattr,setitem中转了一下
    str函数--->obj.__str__()
    repr函数--->obj.__repr__()
    如果__str__没有被定义,那么就会使用__repr__来代替输出
    注意:这俩方法的返回值必须是字符串,否则抛出异常
    '''

    数值定制(Time60)

     定义一个Time60,其中,将整数的小时和分钟作为输入传给构造器:

     
    #!/usr/bin/env python
    #coding:utf8
    
    
    
    class Time60(object): # 顺序对
        #在 Time60 类中,我们将整数的小时和分钟作为输入传给构造器。
        def __init__(self, hr, min): #  构造器
            self.hr = hr # 给小时赋值
            self.min = min #  给分赋值
    #在显示我们的实例的时候,我们需要一个有意义的输出,那么就要覆盖__str__()(如果有必要的话,__repr__()也要覆盖)。
    #我们都习惯看小时和分,用冒号分隔开的格式, # 比如,“4:30”,表示四个小时,加半个小时(4 个小时及 30 分钟): def __str__(self): return '%d:%d' % (self.hr, self.min) #可以实例化一些对象。我们启动一个工时表来跟踪对应构造器的计费小时数: mon = Time60(10, 30) tue = Time60(11, 15) print mon, tue
     

    执行结果:

    10:30 11:15

    __format__

    '字符串%s'.format('字符串拼接')

    print('{0}{0}{0}'.format('aaa'))
    
    # 执行结果:
    # aaaaaaaaa

    很low的办法实现字符串格式化

    不太智能,不能根据用户输入的格式,进行返回信息

    #!/usr/bin/env python
    #coding:utf-8
    
    #这样实现太low
    class Date:
        def __init__(self,year,month,day):
                self.year=year
                self.month=month
                self.day=day
        def __format__(self, format_spec):
            print('__format__执行')
            print('------->',format_spec)   #format_spec默认为空,可不传
            ymd = '{0.year}-{0.month}-{0.day}'.format(d1)
            ymd1 = '{0.year}:{0.month}:{0.day}'.format(d1)
            ymd2 = '{0.year}{0.month}{0.day}'.format(d1)
            return 'ymd:%s|ymd1:%s|ymd2:%s'%(ymd,ymd1,ymd2)
    
    d1 = Date(2016,12,26)   #实例化一个对象d1
    #format(d1)或d1.__format__()  #触发调用函数,d1传给self,format_spec默认为空,可不传
    print(format(d1))
    # __format__执行
    # ('------->', '')
    # ymd:2016-12-26|ymd1:20161226|ymd2:20161226

    来个智能的

    想法:定义字典,根据k取值,拿到v信息赋值操作

    #!/usr/bin/env python
    #coding:utf-8
    
    format_dic={
        'y-m-d':'{0.year}-{0.month}-{0.day}',
        'y:m:d':'{0.year}:{0.month}:{0.day}',
        'ymd':'{0.year}{0.month}{0.day}'
    }
    class Date:
        def __init__(self,year,month,day):
                self.year=year
                self.month=month
                self.day=day
        def __format__(self, format_spec):      #format_spec默认为空值,现在接收用户输入的格式
            print('__format__执行')
            #print('------->',format_spec)
            if not format_spec or format_spec not in format_dic:    #判断用户输入是否为空或格式是否在format_dic字典中存在
                format_spec = 'ymd'                  #如果为空,或不在字典,给个默认值
            else:
                fm = format_dic[format_spec]        #如果在字典中,取到相应的格式,赋值给fm
            return fm.format(self)                  #最后根据情况返回一个拼接好的格式信息
    
    d1 = Date(2016,12,26)    #实例化一个对象d1
    print(format(d1,'y:m:d')) #d1.__format__()  #触发调用函数,d1传给self,用到format_spec传参
    # __format__执行
    # 2016:12:26

    九__next__和__iter__实现迭代器协议

    Python中关于迭代有两个概念,第一个是Iterable(可迭代的),第二个是Iterator(迭代器),协议规定Iterable的iter方法会返回一个Iterator, Iterator的__next__方法(Python 2里是next)会返回下一个迭代对象,如果迭代结束则抛出StopIteration异常。同时,Iterator自己也是一种Iterable,所以也需要实现Iterable的接口,也就是__iter__,这样在for当中两者都可以使用。Iterator的__iter__只需要返回自己就行了


    Python中许多方法直接返回iterator,比如itertools里面的izip等方法,如果Iterator自己不是Iterable的话,就很不方便,需要先返回一个Iterable对象,再让Iterable返回Iterator。生成器表达式也是一个iterator,显然对于生成器表达式直接使用for是非常重要的。

    那么为什么不只保留Iterator的接口而还需要设计Iterable呢?许多对象比如list、dict,是可以重复遍历的,甚至可以同时并发地进行遍历,通过iter每次返回一个独立的迭代器,就可以保证不同的迭代过程不会互相影响。而生成器表达式之类的结果往往是一次性的,不可以重复遍历,所以直接返回一个Iterator就好。让Iterator也实现Iterable的兼容就可以很灵活地选择返回哪一种。
    总结来说Iterator实现的__iter__是为了兼容Iterable的接口,从而让Iterator成为Iterable的一种实现。
    类中的iter不仅仅是返回自身实例,也可以返回其他可迭代对象的实例,这样就实现了委托迭代。
    for为了兼容性其实有两种机制,如果对象有iter会使用迭代器,但是如果对象没有iter,但是实现了getitem,会改用下标迭代的方式,从0开始依次读取相应的下标,直到发生IndexError为止,这是一种旧的迭代协议。
    先判断被循环的是否是Iterable,如果不是,尽管你实现了next,它扔不会去调用,会直接报异常


    之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__

    特殊方法:__iter__,这个方法是迭代器规则的基础
    迭代器规则:

    迭代:重复做一些事很多次
    __iter__方法返回一个迭代器,迭代器具有next方法(这个方法在调用时不需要任何参数)的对象,在调用next方法时,迭代器会返回它的下一个值。
    一个实现了__iter__方法的对象是可迭代的,一个实现了next方法的对象则是迭代器。

    python的迭代器为什么一定要实现__iter__方法?

    分清了下面几个概念,也就搞懂了python的迭代器:

    1、 可迭代类(class collections.abc.Iterable)

    提供 __iter__() 这个方法的类,都是可迭代类,或者 提供__getitem __() 这个方法的类,也是可迭代类




    2、迭代器类(class collections.abc.Iterator)

    同时提供 __iter__() 和 __next__() 这两个方法的类。
    
    (从定义可以看出,迭代器类,一定是 可迭代类,因为它实现了__iter__()方法)
    (从定义来看,迭代器类,要比可迭代类 多实现一个 __next()__方法。)



    3、可迭代对象

    简单来说,就是那些 list, str, 和tuple 用这些定义的对象,都是 可迭代对象,因为他们都实现了__iter__() 或 __getitem__()方法。

     4.迭代器对象:
    代表数据流的对象——迭代器。
    你可以把可迭代对象,当成一个容器(collections)。那么你可以制造一个迭代器类,用他生成的迭代器对象,可以帮你一个一个从容器里取出数据。所以,迭代器必须实现__next__()方法.


    第一步:

     
    class Foo(object):
        pass
    
    
    obj = Foo()
    
    for i in obj:
        print i
        
    # 报错:TypeError: 'Foo' object is not iterable
     

    第二步:

     
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    class Foo(object):
        
        def __iter__(self):
            pass
    
    obj = Foo()
    
    for i in obj:
        print i
    
    # 报错:TypeError: iter() returned non-iterator of type 'NoneType'
     

    第三步:

     
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    class Foo(object):
    
        def __init__(self, sq):
            self.sq = sq
    
        def __iter__(self):
            return iter(self.sq)
    
    obj = Foo([11,22,33,44])
    
    for i in obj:
        print i
     

    以上步骤可以看出,for循环迭代的其实是  iter([11,22,33,44]) ,所以执行流程可以变更为:

     
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
     
    obj = iter([11,22,33,44])
     
    for i in obj:
        print i
     

    简单示范

    #_*_coding:utf-8_*_
     
    class Foo:
        def __init__(self,x):
            self.x=x
     
        def __iter__(self):
            return self
     
        def __next__(self):
            n=self.x
            self.x+=1
            return self.x
     
    f=Foo(3)
    for i in f:
        print(i)
    class Foo:
        def __init__(self,start,stop):
            self.num=start
            self.stop=stop
        def __iter__(self):
            return self
        def __next__(self):
            if self.num >= self.stop:
                raise StopIteration
            n=self.num
            self.num+=1
            return n
     
    f=Foo(1,5)
    from collections import Iterable,Iterator
    print(isinstance(f,Iterator))
     
    for i in Foo(1,5):
        print(i)

    练习:简单模拟range,加上步长

    class Range:
        def __init__(self,n,stop,step):
            self.n=n
            self.stop=stop
            self.step=step
     
        def __next__(self):
            if self.n >= self.stop:
                raise StopIteration
            x=self.n
            self.n+=self.step
            return x
     
        def __iter__(self):
            return self
     
    for i in Range(1,7,3): #
        print(i)

    斐波那契数列

    class Fib:
        def __init__(self):
            self._a=0
            self._b=1
     
        def __iter__(self):
            return self
     
        def __next__(self):
            self._a,self._b=self._b,self._a + self._b
            return self._a
     
    f1=Fib()
     
    print(f1.__next__())
    print(next(f1))
    print(next(f1))
     
    for i in f1:
        if i > 100:
            break
        print('%s ' %i,end='')

    十__doc__

    它是类的描述信息

    class Foo:
        '我是描述信息'
        pass
     
    print(Foo.__doc__)

    该属性无法被继承

    class Foo:
        '我是描述信息'
        pass
     
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类

    十一 __module__和__class__

    __module__ 表示当前操作的对象在那个模块

    __class__ 表示当前操作的对象的类是什么

    lib/aa.py

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
     
    class C:
     
        def __init__(self):
            self.name = ‘SB'

    index.py

    from lib.aa import C
     
    obj = C()
    print obj.__module__  # 输出 lib.aa,即:输出模块
    print obj.__class__      # 输出 lib.aa.C,即:输出类

    例子:

    class C:
        def __init__(self):
            self.name = 'sb'
    obj = C()
    print (obj.__module__)  # 输出 __main__,即:输出模块
    #执行结果:如果是当前文件,返回__main__,如果是其它,返回调用的__name__
    print (obj.__class__)   # 输出 <class '__main__.C'>,即:输出类
    #执行结果:<class '__main__.C'>          模块.类名

    十二_del__

    析构方法,当对象在内存中被释放时,自动触发执行。

    注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__。

    简单示范

    class Foo:
     
        def __del__(self):
            print('执行我啦')
     
    f1=Foo()
    del f1
    print('------->')
     
    #输出结果
    执行我啦
    ------->

    挖坑埋了你

    class Foo:
     
        def __del__(self):
            print('执行我啦')
     
    f1=Foo()
    # del f1
    print('------->')
     
    #输出结果
    ------->
    执行我啦
     
     
    #为何啊???

    典型的应用场景:

    创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

    当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

    这与文件处理是一个道理:

    f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
    del f #只回收用户空间的f,操作系统的文件还处于打开状态
     
    #所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
    f=open('a.txt')
    读写...
    f.close()
    很多情况下大家都容易忽略f.close,这就用到了with上下文管理

    十三 __enter__和__exit__

    我们知道在操作文件对象的时候可以这么写

    with open('a.txt') as f:
      '代码块'

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    上下文管理协议

    class Open:
        def __init__(self,name):
            self.name=name
     
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
            # return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
     
     
    with Open('a.txt') as f:
        print('=====>执行代码块')
        # print(f,f.name)

    __exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

    class Open:
        def __init__(self,name):
            self.name=name
     
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
     
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
     
     
     
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->不会执行

    如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

    class Open:
        def __init__(self,name):
            self.name=name
     
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
     
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
            return True
     
     
     
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->会执行

    练习:模拟Open

    class Open:
        def __init__(self,filepath,mode='r',encoding='utf-8'):
            self.filepath=filepath
            self.mode=mode
            self.encoding=encoding
     
        def __enter__(self):
            # print('enter')
            self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
            return self.f
     
        def __exit__(self, exc_type, exc_val, exc_tb):
            # print('exit')
            self.f.close()
            return True
        def __getattr__(self, item):
            return getattr(self.f,item)
     
    with Open('a.txt','w') as f:
        print(f)
        f.write('aaaaaa')
        f.wasdf #抛出异常,交给__exit__处理

    用途或者说好处:

        使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
    
        在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

    十四 __call__

    对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
     
        def __init__(self):
            pass
     
        def __call__(self, *args, **kwargs):
     
            print('__call__')
     
     
    obj = Foo() # 执行 __init__
    obj()       # 执行 __call__

    结果:

    __call__

    books.py内容:__str__和__call__

    __str__:用来返回对象的字符串表达式,即返回用户看到的。事实上,__str__是被print函数调用的,一般都是return一个字符串。当打印一个类的时候,那么print首先调用的就是类里面的定义的__str__

    __repr__:对象打印出来的字符串(返回开发者看到的)

    __call__:如果类实现了这个方法,相当于把这个类型的对象当作函数来使用,相当于 重载了括号运算符。在Python中,如果在创建class的时候写了call()方法, 那么该class实例化出实例后, 实例名()就是调用call()方法。

     
     
    #!/usr/bin/env python
    #coding:utf8
    
    
    class Book(object):
        def __init__(self,title,author):
            self.title = title 
            self.author = author 
            
            
        def __str__(self):
            return  self.title
        
        def __call__(self):
            print "%s is written by %s" % (self.title,self.author)
        
    if __name__ == "__main__":
        pybook = Book("Core Python","Wesley")
        print pybook  #因为类中定义了__str__方法,此处打印出的内容是__str__的返回值    
        pybook()  #调用pybook就是执行__call__方法中的代码
     
     

    执行结果:

    Core Python
    Core Python is written by Wesley

    十五 切片

    __getslice__、__setslice__、__delslice__

    该三个方法用于分片操作,如列表的切片,仅限python2

     
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
     
    class Foo(object):
     
        def __getslice__(self, i, j):  
            print '__getslice__',i,j
     
        def __setslice__(self, i, j, sequence):
            print '__setslice__',i,j
     
        def __delslice__(self, i, j):
            print '__delslice__',i,j
     
    obj = Foo()
     
    obj[-1:1]                   # 自动触发执行 __getslice__
    obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
    del obj[0:2]                # 自动触发执行 __delslice__
     

    执行结果:

    __getslice__ -1 1
    __setslice__ 0 1
    __delslice__ 0 2
    从 Python3 的官方文档可知,Python3中的切片的魔术方法不再是Python2中的__getslice__(), __setslice__()__delslice__(),而是借助 slice 类整合到了__getitem__(),__setitem__()__delitem__()中。


    具体如下:
    __getslice__(), __setslice__() and __delslice__() were killed. The syntax a[i:j] now translates toa.__getitem__(slice(i, j)) (or __setitem__() or __delitem__(), when used as an assignment or deletion target, respectively).
    
    

    Python3 中的切片

    #!/usr/bin/env python
    #coding:utf-8
    
    
    class Foo(object):
    
        def __getitem__(self, index):
            if isinstance(index, slice):
                print("Get slice---------> start: %s, stop: %s, step: %s." 
                      % (str(index.start), str(index.stop), str(index.step)))
    
        def __setitem__(self, index, value):
            if isinstance(index, slice):
                print("Set slice---------> start: %s, stop: %s, step: %s." 
                      % (str(index.start), str(index.stop), str(index.step)))
                print("	The value is:", value)
    
        def __delitem__(self, index):
            if isinstance(index, slice):
                print("Delete slice------> start: %s, stop: %s, step: %s." 
                      % (str(index.start), str(index.stop), str(index.step)))
    
    
    if __name__ == "__main__":
        obj = Foo()
        obj[-1:10]
        obj[-1:10:1] = [2, 3, 4, 5]
        del obj[-1:10:2]

    执行结果:

    Get slice---------> start: -1, stop: 10, step: None.
    Set slice---------> start: -1, stop: 10, step: 1.
        The value is: [2, 3, 4, 5]
    Delete slice------> start: -1, stop: 10, step: 2.

    十六 __*add____*sub__

    __radd__是自定义的类操作符,执行"右加"。

    当python解释器执行到a+b这样的语句时,首先在查找a中有没有__add__操作符,如果a中没有定义,那么就在b中查找并执行__radd__。

    至于__iadd__(),是运算符类operator的成员函数,就是累加操作符的另一种调用形式。a = operator.__iadd__(a, b)就等价于a += b

    def __add__(self, other)#该类对象+别的对象时调用
        return #加的结果
    
    def __radd__(self, other)#别的对象+该类对象时调用
        return #加的结果

    number.py内容:   

     
     
    #!/usr/bin/env python
    #coding:utf8
    
    
    class MyNumber(object):
        
        def __init__(self,num):
            self.number= num 
            
        def __add__(self,other):
            return self.number + other
        
        def __radd__(self,other):
            return self.number + other
        
        def __sub__(self,other):
            return self.number - other
        
        def __rsub__(self,other):
            return  other - self.number
        
    if __name__ == "__main__":
        digit = MyNumber(10)  
        print digit + 10  #用__add__实现
        print 10 + digit  #用__radd__实现
        print digit - 100 #用__sub__实现
        print 100 - digit #用__rsub__实现
        
     
     

    执行结果:

    20
    20
    -90
    90

    参考:https://www.cnblogs.com/wj-1314/p/8716516.html

    https://www.cnblogs.com/shangpolu/p/6232651.html

    https://www.jianshu.com/p/c48e6e903c38

  • 相关阅读:
    lintcode-144-交错正负数
    lintcode-143-排颜色 II
    lintcode-142-O(1)时间检测2的幂次
    lintcode-141-x的平方根
    lintcode-138-子数组之和
    postman-----使用CSV和Json文件实现批量接口测试
    postman—创建collection,执行collection和批量执行
    postman-变量
    postman-鉴权
    Postman—cookie
  • 原文地址:https://www.cnblogs.com/zhongguiyao/p/11049298.html
Copyright © 2011-2022 走看看