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:2016:12:26|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中许多方法直接返回iterator,比如itertools里面的izip等方法,如果Iterator自己不是Iterable的话,就很不方便,需要先返回一个Iterable对象,再让Iterable返回Iterator。生成器表达式也是一个iterator,显然对于生成器表达式直接使用for是非常重要的。
那么为什么不只保留Iterator的接口而还需要设计Iterable呢?许多对象比如list、dict,是可以重复遍历的,甚至可以同时并发地进行遍历,通过iter每次返回一个独立的迭代器,就可以保证不同的迭代过程不会互相影响。而生成器表达式之类的结果往往是一次性的,不可以重复遍历,所以直接返回一个Iterator就好。让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
__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