看到类似的__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的

  Python中还有许多有特殊用途的函数,可以帮助我们定制类

  __str__

  先定义一个Student类,打印一个实例

>>> class Student(object):
... def __init__(self,name):
... self.name=name
...
>>> print(Student('Zhangsan'))
<__main__.Student object at 0x7f8a4830a748>

  打印出<__main__.Student object at 0x7f8a4830a748>不好看,不直观

  怎么才能打印的好看呢?只需要定义好__str__()方法,返回一个好看的字符串就可以了

>>> class Student(object):
... def __init__(self,name):
... self.name=name
... def __str__(self):
... return 'Student object(name:%s)' % self.name
...
>>>
>>> print(Student('Zhangsan'))
Student object(name:Zhangsan)

  这样打印出来的实例,不但好看 ,而且容易看出实例内部重要的数据

  但是如果直接在终端敲变量而不用print,打印出来还是一样不好看

>>> Student('Zhangsan')
<__main__.Student object at 0x7f8a4830a8d0>

  这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

解决办法是再定义一个__repr__()。但是通常__str__()__repr__()代码都是一样的,所以,有个偷懒的写法:

>>> class Student(object):
... def __init__(self,name):
... self.name=name
... def __str__(self):
... return 'Student object(name:%s)' % self.name
... __repr__=__str__
...
>>>
>>> Student('Zhangsan')
Student object(name:Zhangsan)

  

  __iter__

  如果一个类想被用于for..in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法循环的拿到下一个值,直到遇到StopIteration错误退出循环

class Fib(object):
def __init__(self):
#初始化两个计数器
self.a,self.b=0,1
def __iter__(self):
#实例本身就是迭代对象,返回自己
return self
def __next__(self):
self.a,self.b=self.b,self.a+self.b
#设置循环退出条件
if self.a>10000:
raise StopIteration()
return self.a for n in Fib():
print(n)

  输出

>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025

  

  __grtitem__

  Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素

>>> Fib()[5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Fib' object is not subscriptable

  要表现的象list那样按照下标取出元素,需要实现__getitem__()方法

  special_getitem.py

class Fib(object):
def __getitem__(self,n):
a,b=1,1
for x in range(n):
a,b=b,a+b
return a

  现在,就可以按下标访问数列的任意一项了

>>> f[0]
1
>>> f[1]
1
>>> f[100]
573147844013817084101

  f[0]相当于把n=0参数传递给方法__getitem__(0),因为n=0,for循环不执行,返回a=1,

f[1]相当于把n=1参数传递给方法__getitem__(1),因为n=1,for循环执行一次a=b=1,b=a+b=2 返回值为a=1

f[2]相当于把n=2参数传递给方法__geritem__(2),第一次循环,a=b=1,b=a+b=2 再次循环 a=b=2,b=a+b=1+2=3 两次循环结束返回值a=2

以此类推

  但是list有个切片的方法对应Fib确报错

>>> f[1:5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __getitem__
TypeError: 'slice' object cannot be interpreted as an integer

  原因是__getitem__()传入的参数可能是一个int,也可以是一个切片对象slice,所以要判断   

special_getitem.py

class Fib(object):
def __getitem__(self,n):
#n是整数
if isinstance(n,int):
a,b=1,1
for x in range(n):
a,b=b,a+b
return a
#n是切片类似[0:2]
if isinstance(n,slice):
start=n.start
stop=n.stop
if start is None:
start=0
a,b=1,1
L=[]
for x in range(stop):
if x>=start:
L.append(a)
a,b=b,a+b
return L
f=Fib()
print(f[0])
print(f[1])
print(f[2])
print(f[3])
print(f[0:3])

  如果传递的参数是整数则和上面的方法不变,一次返回一个整数

如果传递的参数是切片slice则从切片的start开始至stop结束返回一个列表

运行结果如下

1
1
2
3
[1, 1, 2]

  如果传递参数是切片,执行过程分析f[0:1]切片取索引为0及第一个元素的列表

if判断[0:1]是slice
start=n.start 所以start=None
stop=n.stop stop=1
if判断如果start为None则把start置为0
定义初始两位数为1,1
定义空列表L=[]
执行循环 for x in range(1):
x=1执行第一次循环
if判断1>=1满足条件
执行append语句后L=[1]
接着执行往下语句a,b=b,a+b
a=b,b=a+b是同时执行执行完毕后
a=1 b=2
退出for循环返回列表L=[1]

  如果传递的切片参数为[0:2]

if判断[0:2]是slice
start=n.start 所以start=None
stop=n.stop stop=2
if判断如果start为None则把start置为0
定义初始两位数为1,1
定义空列表L=[]
执行循环 for x in range(1):
x=0执行第一次循环
if判断0>=0满足条件
执行append语句后L.append(a) 及L.append(1) L=[1]
接着执行往下语句a,b=b,a+b
a=b,b=a+b是同时执行执行完毕后
a=1 b=2
for循环返回列表L=[1]
x=1执行第二次循环
if判断1>=1满足条件
执行append语句L.append(a)及L.append(1) L=[1,1]
执行a,b=b,a+b执行完毕后
a=2 b=3
因为stop=2执行两次以后退出循环 返回列表L=[1,1]

  如果传递是切片参数是[0:3]

if判断[0:3]是slice
start=n.start 所以start=None
stop=n.stop stop=3
if判断如果start为None则把start置为0
定义初始两位数为1,1
定义空列表L=[]
执行循环 for x in range(1):
x=0执行第一次循环
if判断0>=0满足条件
执行append语句后L.append(a) 及L.append(1) L=[1]
接着执行往下语句a,b=b,a+b
a=b,b=a+b是同时执行执行完毕后
a=1 b=2
for循环返回列表L=[1]
x=1执行第二次循环
if判断1>=0满足条件
执行append语句L.append(a)及L.append(1) L=[1,1]
执行a,b=b,a+b执行完毕后
a=2 b=3
x=2执行第三次循环
if判断2>=0满足条件
执行append语句L.append(a)及L.append(2) L=[1,1,2]
因为stop=3执行三次次以后退出循环 返回列表L=[1,1,2]

  以此类推

  如果start不是 0

for循环会继续执行,但是还没有到start处因为不满足x>=start条件所以L.append(a)不会执行,不会往列表内追加元素
但是a,b=b,a+b会继续执行

  

  关于切片及切片函数slice参考:https://www.cnblogs.com/minseo/p/11113638.html

  

  但是以上还是有缺陷没有对step参数做处理

print(f[0:10:2])
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

  虽然加了参数:2步数还是1

  也没有对负数进行处理,所以,要正确实现以下__getitem__()还有很多工作要做

  

  此外,如果把对象看成dict,__getitem__()的参数也可能是一个可以做key的object,例如str。

  与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

  __getattr__

  正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定了Student类

class Student(object):
def __init__(self):
self.name="Zhangsan"

  调用存在的属性name没有问题,但是调用不存在的score属性就有问题了

>>> s.name
'Zhangsan'
>>> s.score
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

  错误信息很清楚地告诉我们,没有找到score这个attribute

  要避免这个错误,除了可以加上一个score属性外,python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性

  special_getattr.py

class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99

  当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

>>> s=Student()
>>> s.name
'Michael'
>>> s.score
99

  返回函数也是完全可以的

class Student(object):
def __init__(self):
self.name = 'Michael'
def __getattr__(self, attr):
if attr=='score':
return 99
def __getattr__(self,attr):
if attr=='age':
return lambda:25 s=Student()
print(s.age())

  调用方式改为s.age

  注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性比如name,不会再__getattr__中查找

  此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

>>> s=Student()
>>> s.name
'Michael'
>>> s.aba
>>> print(s.aba)
None

  改成如下

def __getattr__(self,attr):
if attr=='age':
return lambda:25
raise AttributeError('\'Student\' object has no attribute \'%s\'' %attr)

  

  __call__一个对象实例可以有自己是属性和方法,当我们调用实例方法时,我们用instance.method()来调用,能不能直接在实例本身上调用呢?

  任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用

  special_call.py

class Student(object):
def __init__(self,name):
self.name=name
def __call__(self):
print('My name is %s.' % self.name)

  调用方法如下

>>> s=Student('Zhansan')
>>> s()
My name is Zhansan.

  __call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:

>>> callable(Student('Zhangsan'))
True
>>> callable(max)
True
>>> callable([1,2,3])
False
>>> callable(None)
False
>>> callable('str')
False

  通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

  

Python3之定制类的更多相关文章

  1. 【Python】__slots__ 、@property、多重继承、定制类、枚举类、元类

    __slots__ @property 多重继承 定制类 枚举类 元类 [使用__slots__] 1.动态语言的一个特点就是允许给实例绑定任意的方法和变量,而静态语言(例如Java)必须事先将属性方 ...

  2. 【Python】[面向对象高级编程] 多成继承,定制类,使用枚举

    1.多成继承 class SmallDog(Animal,Dog) pass MixIn就是一种常见的设计. 2.定制类类似__slots__这种形如 __xxx__ 的变量或者函数名,在python ...

  3. python基础——定制类

    python基础——定制类 看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的. __slots__我们已经知道怎么用了,__len__()方 ...

  4. 定制类自己的的new_handler

    C++中的new操作符首先使用operator new函数来分配空间,然后再在此空间上调用类的构造函数构造对象.当operator new无法分配所需的内存空间时,默认的情况下会抛出一个bad_all ...

  5. python 定制类

    看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的. __slots__我们已经知道怎么用了,__len__()方法我们也知道是为了能让cla ...

  6. python学习第十七天 --定制类

    何为定制类? Python的class允许定义许多特殊方法,可以让我们非常方便地生成特定的类.在类中应用或者重写python的特殊方法,得到的类,就是定制类. 大家都知道print的用法.见下面例子 ...

  7. Python面向对象高级编程-__slots__、定制类,枚举

    当在类体内定义好各种属性后,外部是可以随便添加属性的,Python中类如何限制实例的属性? Python自带了很多定制类,诸如__slots__,__str__ __slots__ __slots__ ...

  8. 快速了解Python的定制类

    多重继承 class Student(man,oldman): pass 可以继承多个父类,拥有他们的方法,如果有父类有相同的方法,哪个在前用哪个 定制类 看到类似__slots__这种形如 __xx ...

  9. python面向对象高级:定制类

    Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类. 比如: __str__ 与__repr____iter____getitem____call__ __str__ 与__r ...

随机推荐

  1. 2019牛客暑期多校训练营(第十场)Han Xin and His Troops——扩展中国剩余定理

    题意 求解 $n$ 个模方程 $x \equiv a (mod \ b)$,不保证模数互素($1 \leq n \leq 100$,$0 \leq b < a< 10^5$). 分析 套扩 ...

  2. flask实现文件下载功能

    在Flask想要实现文件下载功能,只需要导入 send_from_directory(directory, filename, **options) 然后在视图函数中返回该函数即可 示例代码如下 fr ...

  3. 48、[源码]-Spring容器创建-初始化事件派发器、监听器等

    48.[源码]-Spring容器创建-初始化事件派发器.监听器等 8.initApplicationEventMulticaster();初始化事件派发器: 获取BeanFactory 从BeanFa ...

  4. 黑魔法师之门 (magician)-并查集

    题目 经过了 16 个工作日的紧张忙碌,未来的人类终于收集到了足够的能源.然而在与 Violet 星球的战争中,由于 Z 副官的愚蠢,地球的领袖 applepi 被邪恶的黑魔法师 Vani 囚禁在了 ...

  5. Oracle 中MERGE语句的用法

    原文章出处(http://blog.csdn.net/lichkui/article/details/4306299) MERGE语句是Oracle9i新增的语法,用来合并UPDATE和INSERT语 ...

  6. NetworkX系列教程(3)-手动创建graph

    小书匠Graph图论 不可否认,日常中我们使用最多的还是,使用自己的数据去手动创建自己的图形,而不是使用生成器,现从给graph添加点和边入手,讲解手动创建graph. 目录: 3.给graph添加节 ...

  7. python常用函数1

    map()函数 map()是python 内置 的高届函数 ,接收一个函数  f  和一个list,并通过把函数  f  依次作用在list的每个元素上,得到一个新的 list 并返回. 比如,对于l ...

  8. [vsftpd] ubuntu14.04 ansible剧本安装vsftpd流程及报错排查

    需求: 在ubuntu14.04机器上搭建ftp服务,ftp账号通过winscp软件登录后,仅可增删改/data/wwwroot目录. 一.安装步骤 1.apt 安装vsftpd apt-get in ...

  9. Thift初探 (一)

    Maven pox.xml: <dependency> <groupId>org.apache.thrift</groupId> <artifactId> ...

  10. 同一个类里@Cacheable缓存不起作用

    问题原因: 注解@Cacheable是使用AOP代理实现的 ,通过创建内部类来代理缓存方法,类内部的方法调用类内部的缓存方法不会走代理,所以就不能正常创建缓存,所以每次都需要去调用数据库. 解决方法: ...