Python3学习(26)--类的量身定制
2017-09-09 13:37
295 查看
定制类
什么是定制类呢?一般来说,我们的常规类,就是封装一些属性和方法的,然后通过类的实例来访问和调用,仅此而已。你有没有想过,像操作字符串一样操作我们的类实例?有没有想过,像操作迭代器对象那样操作我们的类实例?有没有想过,像调用函数那样调用我们的类实例呢? 等等,这种异想天开的想法在Python中就很贴合实际,因为动态语言的特性,活泼灵活,前面我们讲过,我们可以给一个编译好的类,在其运行期间,动态地绑定类属性或者实例属性(这个不难),当然,你完全可以想到,我们也可以在程序运行时去动态地"创建"一个类(这个就没有那么容易了),下面,我们就先来demo演示一下:
构建方法
__builtin__.type(name, bases, dct)
name: 类名
bases: 继承的父类,一个tuple元组,单继承的话,就是单元组,单元组的表示法-->(one,)
dct:一个dict类型,用来作为类的初始化元素。
test.py:
上述代码,我们干了两件事,一个是定义了两个方法,一个是利用type函数为我们创建了一个类,Hello。
(1)类名 -- Hello
(2)继承自Object
(3)类的两个元素,一个是hello,一个是__init__,注意他们实际指向的是最外层的两个函数实体
我们验证一下,Hello是不是class类型:
![](http://img.blog.csdn.net/20170909140928573?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
没问题,确实是,那我们是不是可以像操作类一样的操作Hello呢?我们继续验证一下:
![](http://img.blog.csdn.net/20170909141405665?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
像操作字符串一样,操作类实例
__len__
![](http://img.blog.csdn.net/20170909151700282?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
根据类实例构造器或类实例直接输出类内部重要成员数据
__str__ (或 __repr__)
![](http://img.blog.csdn.net/20170909155635263?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
使类的实例成为一个可迭代的对象
__iter__
__next__
我们验证下,it实例是否是可迭代的
![](http://img.blog.csdn.net/20170909161233554?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
我们知道只要是可迭代的对象,都可以使用for循环进行遍历:
修改上述代码如下:
我们看下输出:
![](http://img.blog.csdn.net/20170909162341809?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
这样的输出结果,你都不敢相信,it是类MyList的一个实例,但是,我们通过__iter__和__next__组合定制MyList类,使得it成为了一个可迭代的对象实例,以至于以假乱真,输出效果相当逼真,但是,我们知道,一个真正的list对象,是可以通过下标(index)进行元素访问的,试问一下,现在的it可以吗?
我们看看下面的尝试:
![](http://img.blog.csdn.net/20170909163047127?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
我们继续定制我们的MyList类,使他的实例可以像真正的list对象一样通过index访问元素
__getitem__
我们看下输入结果:
![](http://img.blog.csdn.net/20170909170443513?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
现在我们的MyList实例越来越像list了,不但可以for遍历每一个元素,还可以像list那样通过下标访问元素,还能不能模仿的再像点呢?比如我们的list有切片slice操作,我们的实例it行不行呢?由于__getitem__函数,参数index的类型无法确定,如果传入的是一个int对象,那我们就可以根据索引[int]取得相应元素,如果是slice对象呢,我们是不是可以根据[start:stop]切片操作实例it,也取出对应的序列集合呢?当然可以,下面,我们将对类MyList再次优化一下,使得实例it具有切片功能:
![](http://img.blog.csdn.net/20170909172612340?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20170909173022403?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
ok,我们看下效果:
![](http://img.blog.csdn.net/20170909173218280?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
我们对比一下,list的切片操作是不是和上述的输出结果一致:
![](http://img.blog.csdn.net/20170909173350541?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
我们知道list索引是有负数一说的,也就是正反下标均可以访问,还有step跳步操作(start:stop:step),针对MyList类,我们暂时还没有增加这些功能(工作量有点大),也就是说,通过类的定制,我们可以让类的实例大变身,感觉跟施了魔法一样,颠覆了我们对于传统静态语言中类实例的映像。
由于,定制类中的__xxx__特性比较多,我们最后再讲一个
__call__
像调用函数那样调用类的实例
我们直接看下结果:
![](http://img.blog.csdn.net/20170909174625529?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
我们于是想到了斐波那契数列,我们是不是可以定制一个类,使他的实例可以调用本身,并可以传进去一个参数,控制数列的增长,然后返回给我们一个list,这个list就是一个斐波那契数列,噢啦,我们就借助这个__call__来实现我们的描述:
神不神奇,我们且看执行结果再做判定:
![](http://img.blog.csdn.net/20170909180115061?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQXBwbGV5aw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
什么也不说了,再一次颠覆了我们对于传统类实例对象的理解!
其他定制类的__xxx__用法 本篇就不讲了,有兴趣的朋友,可以查官方文档说明:点击这里
什么是定制类呢?一般来说,我们的常规类,就是封装一些属性和方法的,然后通过类的实例来访问和调用,仅此而已。你有没有想过,像操作字符串一样操作我们的类实例?有没有想过,像操作迭代器对象那样操作我们的类实例?有没有想过,像调用函数那样调用我们的类实例呢? 等等,这种异想天开的想法在Python中就很贴合实际,因为动态语言的特性,活泼灵活,前面我们讲过,我们可以给一个编译好的类,在其运行期间,动态地绑定类属性或者实例属性(这个不难),当然,你完全可以想到,我们也可以在程序运行时去动态地"创建"一个类(这个就没有那么容易了),下面,我们就先来demo演示一下:
构建方法
__builtin__.type(name, bases, dct)
name: 类名
bases: 继承的父类,一个tuple元组,单继承的话,就是单元组,单元组的表示法-->(one,)
dct:一个dict类型,用来作为类的初始化元素。
test.py:
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- def hello(self): print('hello,',self.name) def __init__(self,name): self.name = name Hello = type('Hello',(object,),{'hello':hello,'__init__':__init__})
上述代码,我们干了两件事,一个是定义了两个方法,一个是利用type函数为我们创建了一个类,Hello。
(1)类名 -- Hello
(2)继承自Object
(3)类的两个元素,一个是hello,一个是__init__,注意他们实际指向的是最外层的两个函数实体
我们验证一下,Hello是不是class类型:
没问题,确实是,那我们是不是可以像操作类一样的操作Hello呢?我们继续验证一下:
像操作字符串一样,操作类实例
__len__
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- class Student(object): def __init__(self,*args): self.names = args def __len__(self): return len(self.names) s = Student("Kobe",'Durant','Appleyk') print(len(s)) #获得实例s中names的长度 print(len('abc')) #获取字符串abc的长度
根据类实例构造器或类实例直接输出类内部重要成员数据
__str__ (或 __repr__)
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- class Student(object): def __init__(self,name,age): self.name = name self.age = age def __str__(self): return 'my name is : %s,i am %s years old.' %(self.name,self.age) s = Student('Appleyk',26) print(s) print(Student('Appleyk',26))
使类的实例成为一个可迭代的对象
__iter__
__next__
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- from collections import Iterable class MyList(object): #一个长度等于10的有序列表类 def __init__(self): self.num = 0 def __iter__(self): return self def __next__(self): if self.num>9: raise StopIteration #一旦超过长度10,停止迭代(注意,sum的初值从0开始) else: self.num +=1 return self.num it = MyList() print(isinstance(it,Iterable))
我们验证下,it实例是否是可迭代的
我们知道只要是可迭代的对象,都可以使用for循环进行遍历:
修改上述代码如下:
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- from __future__ import print_function from collections import Iterable class MyList(object): def __init__(self): self.num = 0 def __iter__(self): return self def __next__(self): if self.num>9: raise StopIteration else: self.num +=1 return self.num it = MyList() print(isinstance(it,Iterable)) for n in it: print(n,' ',end='')
我们看下输出:
这样的输出结果,你都不敢相信,it是类MyList的一个实例,但是,我们通过__iter__和__next__组合定制MyList类,使得it成为了一个可迭代的对象实例,以至于以假乱真,输出效果相当逼真,但是,我们知道,一个真正的list对象,是可以通过下标(index)进行元素访问的,试问一下,现在的it可以吗?
我们看看下面的尝试:
我们继续定制我们的MyList类,使他的实例可以像真正的list对象一样通过index访问元素
__getitem__
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- from __future__ import print_function from collections import Iterable class MyList(object): def __init__(self): self.num = 0 def __iter__(self): return self def __next__(self): if self.num>19: raise StopIteration else: self.num +=2 return self.num def __getitem__(self,index): for n in [x*2 for x in range(1,index+2)]: self.num = n return self.num it = MyList() print(isinstance(it,Iterable)) for n in it: print(n,' ',end='') print() print(it[0]) print(it[9]) print(it[20])
我们看下输入结果:
现在我们的MyList实例越来越像list了,不但可以for遍历每一个元素,还可以像list那样通过下标访问元素,还能不能模仿的再像点呢?比如我们的list有切片slice操作,我们的实例it行不行呢?由于__getitem__函数,参数index的类型无法确定,如果传入的是一个int对象,那我们就可以根据索引[int]取得相应元素,如果是slice对象呢,我们是不是可以根据[start:stop]切片操作实例it,也取出对应的序列集合呢?当然可以,下面,我们将对类MyList再次优化一下,使得实例it具有切片功能:
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- from __future__ import print_function from collections import Iterable class MyList(object): def __init__(self): self.num = 0 def __iter__(self): return self def __next__(self): if self.num>19: raise StopIteration else: self.num +=2 return self.num def __getitem__(self,index): if isinstance(index,int): for n in [x*2 for x in range(1,index+2)]: pass return n elif isinstance(index,slice): start = index.start stop = index.stop if start is None: start = 0 L = [] for n in [x*2 for x in range(1,stop+1)]: if n>start*2: L.append(n) return L it = MyList() print(isinstance(it,Iterable)) for n in it: print(n,' ',end='') print() print(it[5]) print(it[:2]) print(it[5:10])
ok,我们看下效果:
我们对比一下,list的切片操作是不是和上述的输出结果一致:
我们知道list索引是有负数一说的,也就是正反下标均可以访问,还有step跳步操作(start:stop:step),针对MyList类,我们暂时还没有增加这些功能(工作量有点大),也就是说,通过类的定制,我们可以让类的实例大变身,感觉跟施了魔法一样,颠覆了我们对于传统静态语言中类实例的映像。
由于,定制类中的__xxx__特性比较多,我们最后再讲一个
__call__
像调用函数那样调用类的实例
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- class Student(object): def __call__(self,name): print('My name is %s.' % name) s = Student() s('Appleyk')
我们直接看下结果:
我们于是想到了斐波那契数列,我们是不是可以定制一个类,使他的实例可以调用本身,并可以传进去一个参数,控制数列的增长,然后返回给我们一个list,这个list就是一个斐波那契数列,噢啦,我们就借助这个__call__来实现我们的描述:
#!/usr/bin/env Python3 # -*- encoding:UTF-8 -*- class Fib(object): def __init__(self): self.a,self.b,self.L = 0,1,[] #初始化前后元素值,以及list对象L为空 def __call__(self,max): while True: self.a, self.b = self.b, self.a + self.b #后一个数等于前两个数之和 if len(self.L)>=max: #一旦超出给定的长度,循环立刻终止 break else: self.L.append(self.a) return self.L f = Fib() print(f(10)) #调用实例本身,并传入参数10,得到一个长度为10的斐波那契数列
神不神奇,我们且看执行结果再做判定:
什么也不说了,再一次颠覆了我们对于传统类实例对象的理解!
其他定制类的__xxx__用法 本篇就不讲了,有兴趣的朋友,可以查官方文档说明:点击这里
相关文章推荐
- python基础教程_学习笔记26:好玩的编程
- python基础课程_学习笔记26:编程的乐趣
- 零基础入门学习Python(26)--字典:当索引不好用时2
- 零基础学习python_字典(25-26课)
- 笨方法学习Python-习题26: 恭喜你,现在可以考试了!
- 【python学习笔记】26:scipy卷积运算
- 26. Python脚本学习笔记二十六 mod_python
- Python学习26:类属性vs实例属性
- Python学习(26):Python函数式编程
- Python学习(26)--面向对象编程3
- Python 学习入门(26)—— 装饰器
- 笨办法学Python学习笔记 练习26
- python学习日记_第十天(ex25~26)
- python学习笔记26(python中__name__的使用)
- Learn Python The Hard Way学习(26) - 恭喜,可以进行期中考试了!
- 零基础入门学习Python(26):生成器
- python提高计算速度的几种方法---学习笔记26
- python学习笔记26(python中__name__的使用)
- Python 学习入门(26)—— 装饰器
- 【python学习】动手写第一个类