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:2016\12\26

 

来个智能的

想法:定义字典,根据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}',
    'y\m\d':'{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("\tThe 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

posted @ 2019-06-19 08:47  钟桂耀  阅读(194)  评论(0编辑  收藏  举报