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')
>>> s
wc

一般来说,__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)
...
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987

__getitem__

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

>>> Fib()[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
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.score
88

当调用不存在的属性时,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.haha
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", 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])

更多的内置函数对象参见官网

Python面向对象-定制方法的更多相关文章

  1. Python 面向对象 特殊方法(魔法方法)

    Python 的特殊方法,两边带双下划线的方法. 比如:__init__(self, ...).__del__(self) 1.__init__(self,...) : 构造方法 __init__(s ...

  2. python面向对象魔术方法补充

    一.描述符 在 面向对象 编程中 定义一个(没有定义方法)类:class person , 在这个类里面,有name,age, heigth, weight,等等属性, 这个类就可以看作一个对 per ...

  3. Python面向对象之方法

    普通方法要执行类里面的方法是通过对象触发的 触发的时候把自己赋值给self 类方法 vim day7-7.py #!/usr/bin/python # -*- coding:utf-8 -*- cla ...

  4. python 面向对象 析构方法

    实例化但从来没有调用他,就浪费了,就应该自动删除它 这个实例一直存在内存里 python有个垃圾自动回收机制 , 每段时间会自动刷新整个内存,把内存垃圾东西删除   析构函数: 在实例释放.销毁的时候 ...

  5. python面向对象-1方法、构造函数

    类是指:描述一种事物的定义,是个抽象的概念 实例指:该种事物的一个具体的个体,是具体的东西 打个比方: “人”是一个类.“张三”是人类的一个具体例子 在编程时也是同样的道理,你先自己定义一个“类”,当 ...

  6. [Python]python面向对象 __new__方法及单例设计

    __new__ 方法 使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间 __new__ 是一个 由 object 基类提供的 内置的静态方法,主 ...

  7. Python面向对象 | 鸭子方法

    鸭子类型 如果看起来像.叫声像而且走起路来像鸭子,那么它就是鸭子’.python程序员通常根据这种行为来编写程序.例如,如果想编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观和行为像,但与 ...

  8. 用python面向对象的方法实现欧拉算法和龙格库塔算法

    #!/bin/python3 # -*-coding:utf-8 -*- import math import numpy as np #定义一个欧拉算法的类,从而实现不同步长的引用 class Eu ...

  9. python面向对象--item方法

    class Foo: def __getitem__(self, item): print("getitem") return self.__dict__[item] def __ ...

随机推荐

  1. 面试官:CPU百分百!给你一分钟,怎么排查?有几种方法?

    Part0 遇到了故障怎么办? 在生产上,我们会遇到各种各样的故障,遇到了故障怎么办? 不要慌,只有冷静才是解决故障的利器. 下面以一个例子为例,在生产中碰到了CPU 100%的问题怎么办? 在生产中 ...

  2. MySql分库分表与分区的区别和思考

    一.分分合合 说过很多次,不要拘泥于某一个技术的一点,技术是相通的.重要的是编程思想,思想是最重要的.当数据量大的时候,需要具有分的思想去细化粒度.当数据量太碎片的时候,需要具有合的思想来粗化粒度. ...

  3. es6 map的用法

    let arr =[ {title:'aaaa',read:100,hot:true}, {title:'bbbb',read:50,hot:false}, {title:'ccc',read:100 ...

  4. 天了噜,为什么外链css要放在头部,js要放在尾部?

    我们最开始学前端的时候都会看到教程在处理外部css,js的时候会将css放在header中,js放在body的最后.为什么要这样子处理,今天参考一些资料好好分析下. 为什么外链css为什么要放头部? ...

  5. 使用python删除指定文件夹及子文件,保留多少

    python版本为:2.7 import os,time,shutil,datetime def rmdir(deldir,N): dellist=os.listdir(deldir) deldate ...

  6. 开发者如何学好 MongoDB

    作为一名研发,数据库是或多或少都会接触到的技术. MongoDB 是当前火热的 NoSQL 之一,我们怎样才能学好 MongoDB 呢?本篇文章,我们将从以下几方面讨论这个话题: MongoDB 是什 ...

  7. nginx反向代理、负载均衡

    什么叫反向代理? A用户--> B(在和C同一个机房,并且有公网)--> C(不带公网的机器) 什么场景下会用到反向代理? 1.访问不带公网的内网机器 2.解决两台之间通信有障碍的问题 编 ...

  8. MySQL必知必会(汇总数据, 聚集函数)

    SELECT AVG(prod_price) AS avg_price FROM products; #AVG只能用于单个列求平均值,如想计算多个列,必须用多个AVG() SELECT AVG(pro ...

  9. Chrome DevTools开发者工具调试

    1-1 Chrome DevTools 功能简介 (九大功能面板) (1)Elements元素面板 检查和调整页面,调试DOM,调试CSS (2)Network网络面板 调试请求,了解页面静态资源分布 ...

  10. react-native测试安装

    !!!注意!!!:init命令默认会创建最新的版本,而目前最新的0.45及以上版本需要下载boost等几个第三方库编译.这些库在国内即便翻墙也很难下载成功,导致很多人无法运行iOS项目!!!中文网在论 ...