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. vue当文字很多的时候实现...代替

    vue当文字很多的时候实现...代替只需加三行代码 overflow: hiddenwhite-space: nowraptext-overflow: ellipsis

  2. python的reduce,map,zip,filter和sorted函数

    一.    reduce(function,Iterable),它的形式和map()函数一样.不过参数function必须有两个参数. reduce()函数作用是:把结果继续和序列的下一个元素做累积计 ...

  3. warning: rpmts_HdrFromFdno: Header V4 DSA/SHA1 Signature, key ID XXXXXX: NOKEY

    我在使用Centos时,会出现这种错误: 本人实践有效的办法是: 加上"--nogpgcheck"参数 就是在你要执行的语句后面加上该参数就行了! 我当时是为了安装jenkins时 ...

  4. java静态初始化块(静态域)

    1. 类变量的初始化可通过静态初始化块来进行. 代码放在一对大括号内,大括号前用static修饰:static {……} 一个类可定义1个或多个静态初始化块. 静态初始化块会在加载时调用而且只被调用一 ...

  5. e = e || window.event的区别及用法

    本文链接:https://blog.csdn.net/qq_41348029/article/details/81288481 e = e || window.event 在做事件处理时,用于区分IE ...

  6. Solr搜索引擎【索引管理】

    一.索引存储 当文档提交到索引之后,directory目录组件会将它们写入到持久化存储器.Solr的目录组件具有以下重要特点: 1.隐藏持久存储的读写细节,例如,将文档写入到磁盘或通过JDBC在数据库 ...

  7. 2019-2020-3 20199317《Linux内核原理与分析》第三周作业

    第2章  操作系统是如何工作的 1  计算机的三大法宝      存储程序计算机:冯诺依曼结构 函数调用堆栈机制:记录调用的路径和参数的空间 中断机制:由CPU和内核代码共同实现了保存现场和恢复现场, ...

  8. webpack4.0(二)--热更新

    webpack4.0---热更新 使用webpack可帮助我们的开发以及打包,在开发过程中,有时候我们只修改了部分代码并想不必刷新整个页面即可看到更改后的效果,这时候 webpack-dev-serv ...

  9. Redis自动化安装以及集群实现

    Redis实例安装 安装说明:自动解压缩安装包,按照指定路径编译安装,复制配置文件模板到Redis实例路的数据径下,根据端口号修改配置文件模板 三个必须文件:1,配置文件,2,当前shell脚本,3, ...

  10. ThinkPHP的视图和模板

    简单来说一个控制器对应一个视图,一个方法对应一个模板下面我们直接上图. 二.给模板赋值 给模板赋值在这里用到了assign()这个函数,assign()函数第一个参数为给这个值自定义名称,第二个参数则 ...