博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python面向对象-定制方法
阅读量:7089 次
发布时间:2019-06-28

本文共 4907 字,大约阅读时间需要 16 分钟。

Python中的class可以定义许多定制方法,可以让我们方便的生成特定的类。

我们之前介绍了__slots__、__len__(),python中还有许多这样的特殊函数:

__str__

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

上面我们定义了一个普通的Student类,打印出的是一堆<__main__.Student object at 0x035F3630> 。如何自定义打印的内容呢?比如我们关心的"WC"呢。这时__str__()派上用场了:

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

在看看我们直接调用变量会是什么情况:

>>> s = Student('wc')>>> s<__main__.Student object at 0x035F3AB0>

当我们直接调用变量的时候,怎么自定义打印的内容呢?使用__repr__()函数:

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

一般来说,__str__和__repr__定义的内容是一样的,只是前者是给用户看的,后者给开发者看的。

__iter__

之前我们介绍过可迭代对象:如果我们想让一个类可以被for……in循环,就必须实现方法__iter__()方法。该方法返回一个迭代对象。下面写一个模仿输出斐波拉契数列的Fib类:

>>> 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 > 1000:...                 raise StopIteration()...             return self.a...>>> for i in Fib():...     print(i)...1123581321345589144233377610987

__getitem__

尝试把Fib当做list使用,比如取第三个元素:

>>> Fib()[3]Traceback (most recent call last):  File "
", line 1, in
TypeError: 'Fib' object does not support indexing

不行!

要表现的像list那样按照按照角标去除元素,需要实现__getiten__():

>>> class Fib(object):...     def __getitem__(self,n):...             a,b = 1,1...             for i in range(n):...                     a,b = b,a+b...             return a...>>> f = Fib()>>> f[3]3>>> f[10]89

再来试试Fib如何使用切片方法:

>>> class Fib(object):...     def __getitem__(self,n):...             if isinstance(n,int):...                     a,b = 1,1...                     for i in range(n):...                             a,b = b,a+b...                     return a...             if isinstance(n,slice):...                     start = n.start...                     stop = n.stop...                     if start is None:...                             start = 0...                     a,b = 1,1...                     L = []...                     for i in range(stop):...                             if i >= start:...                                     L.append(a)...                             a,b = b,a+b...                     return L...>>> f = Fib()>>> f[0:3][1, 1, 2]>>> f[:5][1, 1, 2, 3, 5] >>> f[:6:2] [1, 1, 2, 3, 5, 8]

但是,我们还没对负数做处理。要实现一个__getitem__()还有、还可以做很多事情。比如,如果把类看做是一个dict,可以使__getitem__()的参数为key的object。与之对应的是__setitem__()方法,可以把对象视作list或者dict来赋值;此外,还存在__delitem__()方法,用来删除某个元素。

通过上面的方法,我们可以自定义一个类,使其表现的像python自带的list、tuple、dict一样。由此,我们对于‘鸭子类型’的设计有了更深刻的理解。

 __getattr__

当我们定义好一个类后,如果想调用类不存在的属性或者方法,我们可以给实例增加一个属性或者方法;其实,python还有另外一个机制,那就是写一个__getattr__()函数,动态返回一个属性。:

>>> class Student(object):...     def __init__(self):...             self.name = 'wc'...     def __getattr__(self,attr):...             if attr == 'score':...                     return 88...>>> s = Student()>>> s.name'wc'>>> s.score88

当调用不存在的属性时,python的机制会调用__getattr__()方法来尝试的得到属性。同样的,也适用于返回函数:

>>> class Student(object):...     def __getattr__(self,attr):...             if attr == 'age':...                     return lambda:20...>>> s = Student()>>> s.age()20

注意:只有在没有找到属性的情况下,才会调用__getdattr__()!

继续优化一下:当我们任意调用不存在的属性时,返回指定的错误:

>>> class Student(object):...     def __getattr__(self,attr):...             if attr == 'age':...                     return lambda:20...             raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)...>>> s = Student()>>> s.hahaTraceback (most recent call last):  File "
", line 1, in
File "
", line 5, in __getattr__AttributeError: 'Student' object has no attribute 'haha'

这实际上把一个类的所有属性和方法调用全部动态化处理了。相当有用。

__call__

一个对象实例可以有自己的属性和方法。我们能不能直接在实例本身上调用呢?既然这么问,那答案是肯定的。任何类只要定义一个__call__()方法,就可以直接对实例进行调用:

>>> class Student(object):...     def __init__(self,name):...             self.name = name...     def __call__(self):...             print('你好:%s'% self.name)...>>> s = Student('wc')>>> s()你好:wc

对实例对象进行直接调用就好比对一个函数调用一样,既可以把对象看成函数,把函数看成对象。

如果你把对象看成函数,那么函数本身也可以在运行期间动态创建出来,因为类的实例都是在运行期间创建出来的。

那么怎么判断一个变量是对象还是函数呢?其实更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象:

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

更多的内置函数对象参见

 

posted on
2018-03-12 17:27 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/hiwuchong/p/8536911.html

你可能感兴趣的文章
properties 配置文件中值换行的问题
查看>>
Azure 部署 Asp.NET Core Web App
查看>>
Masonry和FDTemplateLayoutCell 结合使用示例Demo
查看>>
linux 切换用户之后变成-bash-x.x$的解决方法
查看>>
用备份控制文件做不完全恢复下的完全恢复(数据文件备份<旧>--新建表空间--控制文件备份<次新>--日志归档文件<新>)...
查看>>
python下RSA加密解密以及跨平台问题
查看>>
详解Java Spring各种依赖注入注解的区别
查看>>
android 区分wifi是5G还是2.4G(转)
查看>>
多个构造器参数使用构建器
查看>>
模板方法模式(Template Method)
查看>>
创建预编译头 Debug 正常 Release Link Error:预编译头已存在,使用第一个 PCH
查看>>
asp.net上传文件夹权限配置以及权限配置的分析
查看>>
IPC's epoch 6 is less than the last promised epoch 7
查看>>
C语言 · 寂寞的数
查看>>
android Menu 笔记
查看>>
Apache2.2和Apache2.4中httpd.conf配置文件 权限的异同
查看>>
error:Flash Download failed-“Cortex-M3”,“Programming Algorithm”【转】
查看>>
小tips:JS之break,continue和return这三个语句的用法
查看>>
【Java】Java_09 类型转换
查看>>
AndroidStudio gradle配置
查看>>